我有一个 Activity
,其中有 5 个 EditText
。当用户点击第一个 EditText
时,软键盘打开以在其中输入一些值。当软键盘打开时以及当用户单击第一个 EditText
以及软键盘从后退按钮上的同一个 EditText
关闭时,我想将其他一些 View
的可见性设置为 Gone
按。然后我想将其他一些 View
的可见性设置为可见。
当通过单击 Android 中的第一个 EditText
打开软键盘时,是否有任何侦听器或回调或任何黑客攻击?
trying to detect the virtual keyboard height in Android.
X
和 Y
位置落在键盘上/上方 => SecurityException
=>递减 Y
并重试 =>直到没有抛出异常=>当前 Y
值是键盘高度。 2.如果键盘没有打开=>没有SecurityException
。
SecurityException
=>键盘打开。否则,键盘已关闭。
EditText
而不是其他 EditText
。我怎么能区分这个?
很棒的KeyboardVisibilityEvent library小菜一碟
KeyboardVisibilityEvent.setEventListener(
getActivity(),
new KeyboardVisibilityEventListener() {
@Override
public void onVisibilityChanged(boolean isOpen) {
// Ah... at last. do your thing :)
}
});
Yasuhiro SHIMIZU 学分
这仅在清单中将您的 Activity 的 android:windowSoftInputMode
设置为 adjustResize
时有效。您可以使用布局侦听器来查看您的活动的根布局是否通过键盘调整大小。
我为我的活动使用类似以下基类的东西:
public class BaseActivity extends Activity {
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this);
if(heightDiff <= contentViewTop){
onHideKeyboard();
Intent intent = new Intent("KeyboardWillHide");
broadcastManager.sendBroadcast(intent);
} else {
int keyboardHeight = heightDiff - contentViewTop;
onShowKeyboard(keyboardHeight);
Intent intent = new Intent("KeyboardWillShow");
intent.putExtra("KeyboardHeight", keyboardHeight);
broadcastManager.sendBroadcast(intent);
}
}
};
private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;
protected void onShowKeyboard(int keyboardHeight) {}
protected void onHideKeyboard() {}
protected void attachKeyboardListeners() {
if (keyboardListenersAttached) {
return;
}
rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
keyboardListenersAttached = true;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (keyboardListenersAttached) {
rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
}
}
}
以下示例活动使用它在显示键盘时隐藏视图,并在隐藏键盘时再次显示它。
xml布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
>
<!-- omitted for brevity -->
</ScrollView>
<LinearLayout android:id="@+id/bottomContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<!-- omitted for brevity -->
</LinearLayout>
</LinearLayout>
和活动:
public class TestActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_activity);
attachKeyboardListeners();
}
@Override
protected void onShowKeyboard(int keyboardHeight) {
// do things when keyboard is shown
bottomContainer.setVisibility(View.GONE);
}
@Override
protected void onHideKeyboard() {
// do things when keyboard is hidden
bottomContainer.setVisibility(View.VISIBLE);
}
}
rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
?
正如 Vikram 在评论中指出的那样,只有通过一些丑陋的 hack 才能检测软键盘是显示还是消失。
也许在edittext上设置一个焦点监听器就足够了:
yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
//got focus
} else {
//lost focus
}
}
});
setOnFocusChangeListener
监听器将被调用然后我按下然后它关闭键盘&但没有点击其他视图,现在我再次点击已经有焦点的相同编辑文本,然后会发生什么?
onFocusChange()
。
对于活动:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) {
//enter your code here
}else{
//enter code for hid
}
}
});
对于片段:
view = inflater.inflate(R.layout.live_chat_fragment, null);
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
view.getWindowVisibleDisplayFrame(r);
int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...
}
}
});
android:windowSoftInputMode="adjustResize"
吗?
Jaap's 答案不适用于 AppCompatActivity。而是获取状态栏和导航栏等的高度,并与您的应用程序的窗口大小进行比较。
像这样:
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// navigation bar height
int navigationBarHeight = 0;
int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
}
// status bar height
int statusBarHeight = 0;
resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
// display window size for the app layout
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
// screen height - (user app height + status + nav) ..... if non-zero, then there is a soft keyboard
int keyboardHeight = rootLayout.getRootView().getHeight() - (statusBarHeight + navigationBarHeight + rect.height());
if (keyboardHeight <= 0) {
onHideKeyboard();
} else {
onShowKeyboard(keyboardHeight);
}
}
};
你可以试试:
private void initKeyBoardListener() {
// Минимальное значение клавиатуры.
// Threshold for minimal keyboard height.
final int MIN_KEYBOARD_HEIGHT_PX = 150;
// Окно верхнего уровня view.
// Top-level window decor view.
final View decorView = getWindow().getDecorView();
// Регистрируем глобальный слушатель. Register global layout listener.
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
// Видимый прямоугольник внутри окна.
// Retrieve visible rectangle inside window.
private final Rect windowVisibleDisplayFrame = new Rect();
private int lastVisibleDecorViewHeight;
@Override
public void onGlobalLayout() {
decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();
if (lastVisibleDecorViewHeight != 0) {
if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
Log.d("Pasha", "SHOW");
} else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
Log.d("Pasha", "HIDE");
}
}
// Сохраняем текущую высоту view до следующего вызова.
// Save current decor view height for the next call.
lastVisibleDecorViewHeight = visibleDecorViewHeight;
}
});
}
我迟到了,但我刚刚发现了一个非常方便的依赖项。使用它,您可以检查键盘的可见性以及使键盘“隐藏”并在任何时候使用一行代码显示。
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'
然后您只需使用此代码段来检查键盘的可见性。
KeyboardVisibilityEvent.setEventListener(this, new KeyboardVisibilityEventListener() {
@Override
public void onVisibilityChanged(boolean isOpen) {
if (isOpen)
Toast.makeText(MainActivity.this, "keyboard opened",Toast.LENGTH_SHORT).show();
else
Toast.makeText(MainActivity.this, "keyboard hidden", Toast.LENGTH_SHORT).show();
}
});
然后,如果您想在任何时候隐藏/显示键盘,那么您只需编写这些单行之一即可实现它。
UIUtil.showKeyboard(this,edittext_to_be_focused);
UIUtil.hideKeyboard(this);
下面的代码对我有用,
mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (mainLayout != null) {
int heightDiff = mainLayout.getRootView().getHeight() - mainLayout.getHeight();
if (heightDiff > dpToPx(getActivity(), 200)) {
//keyboard is open
} else {
//keyboard is hide
}
}
}
});
您可以使用我的 Rx 扩展功能 (Kotlin)。
/**
* @return [Observable] to subscribe of keyboard visibility changes.
*/
fun AppCompatActivity.keyboardVisibilityChanges(): Observable<Boolean> {
// flag indicates whether keyboard is open
var isKeyboardOpen = false
val notifier: BehaviorSubject<Boolean> = BehaviorSubject.create()
// approximate keyboard height
val approximateKeyboardHeight = dip(100)
// device screen height
val screenHeight: Int = getScreenHeight()
val visibleDisplayFrame = Rect()
val viewTreeObserver = window.decorView.viewTreeObserver
val onDrawListener = ViewTreeObserver.OnDrawListener {
window.decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame)
val keyboardHeight = screenHeight - (visibleDisplayFrame.bottom - visibleDisplayFrame.top)
val keyboardOpen = keyboardHeight >= approximateKeyboardHeight
val hasChanged = isKeyboardOpen xor keyboardOpen
if (hasChanged) {
isKeyboardOpen = keyboardOpen
notifier.onNext(keyboardOpen)
}
}
val lifeCycleObserver = object : GenericLifecycleObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event?) {
if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
viewTreeObserver.removeOnDrawListener(onDrawListener)
source.lifecycle.removeObserver(this)
notifier.onComplete()
}
}
}
viewTreeObserver.addOnDrawListener(onDrawListener)
lifecycle.addObserver(lifeCycleObserver)
return notifier
.doOnDispose {
viewTreeObserver.removeOnDrawListener(onDrawListener)
lifecycle.removeObserver(lifeCycleObserver)
}
.onTerminateDetach()
.hide()
}
例子:
(context as AppCompatActivity)
.keyboardVisibilityChanges()
.subscribeBy { isKeyboardOpen ->
// your logic
}
dip()
和 getScreenHeight()
对于在 Kotlin 内部片段中使用,这是一个常见的用例,使用 KeyboardVisibilityEvent
库非常容易。
在 build.gradle 中:
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'
在片段中:
activity?.let {
KeyboardVisibilityEvent.setEventListener(it,object: KeyboardVisibilityEventListener {
override fun onVisibilityChanged(isOpen: Boolean) {
if (isOpen) Toast.makeText(context,"Keyboard is opened",Toast.LENGTH_SHORT).show()
else Toast.makeText(context,"Keyboard is closed",Toast.LENGTH_SHORT).show()
}
})
}
if (!isOpen) {}
在 kotlin 中,您可以在活动中使用此代码
window.decorView.viewTreeObserver.addOnGlobalLayoutListener{
val r = Rect()
window.decorView.getWindowVisibleDisplayFrame(r)
val height =window.decorView.height
if(height - r.bottom>height*0.1399){
//keyboard is open
}else{
//keyboard is close
}
如果可以,请尝试扩展 EditText 并覆盖 'onKeyPreIme' 方法。
@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
mEditorListener = listener; //keep it for later usage
super.setOnEditorActionListener(listener);
}
@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
if (mEditorListener != null) {
//you can define and use custom listener,
//OR define custom R.id.<imeId>
//OR check event.keyCode in listener impl
//* I used editor action because of ButterKnife @
mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
}
}
return super.onKeyPreIme(keyCode, event);
}
你如何扩展它:
实现 onFocus 监听并声明 'onKeyboardShown' 声明 'onKeyboardHidden'
我认为,屏幕高度的重新计算并非如前所述 100% 成功。需要明确的是,“以编程方式隐藏软键盘”方法不会调用“onKeyPreIme”的覆盖,但是如果您在任何地方执行此操作,则应该在那里执行“onKeyboardHidden”逻辑并且不要创建全面的解决方案。
这无需更改您的活动的 android:windowSoftInputMode
第 1 步:扩展 EditText 类并覆盖这两个:
@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
mEditorListener = listener;
super.setOnEditorActionListener(listener);
}
@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
if (mEditorListener != null) {
mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
}
}
return super.onKeyPreIme(keyCode, event);
}
第 2 步:在您的活动中创建这两个:
private void initKeyboard() {
final AppEditText editText = findViewById(R.id.some_id);
editText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
setKeyboard(hasFocus);
}
});
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (event == null || event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
editText.clearFocus();
}
return false;
}
});
}
public void setKeyboard(boolean isShowing) {
// do something
}
*** 请记住,为了使 clearFocus
起作用,您必须使父层次结构中的父级或第一个子级具有焦点。
setFocusableInTouchMode(true);
setFocusable(true);
检查我的 Kotlin 扩展 View.keyboardVisibilityChanges()
:
fun View.keyboardVisibilityChanges(): Flow<Boolean>{
return onPreDrawFlow()
.map { isKeyboardVisible() }
.distinctUntilChanged()
}
fun View.onPreDrawFlow(): Flow<Unit> {
return callbackFlow {
val onPreDrawListener = ViewTreeObserver.OnPreDrawListener {
trySendBlocking(Unit)
true
}
viewTreeObserver.addOnPreDrawListener(onPreDrawListener)
awaitClose {
viewTreeObserver.removeOnPreDrawListener(onPreDrawListener)
}
}
}
fun View.isKeyboardVisible(): Boolean = ViewCompat.getRootWindowInsets(this)
?.isVisible(Type.ime())
?: false
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainactivity);
attachKeyboardListeners();
....
yourEditText1.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
yourEditText2.setVisibility(View.GONE);
yourEditText3.setVisibility(View.GONE);
yourEditText4.setVisibility(View.GONE);
yourEditText5.setVisibility(View.GONE);
} else {
yourEditText2.setVisibility(View.VISIBLE);
yourEditText3.setVisibility(View.VISIBLE);
yourEditText4.setVisibility(View.VISIBLE);
yourEditText5.setVisibility(VISIBLE);
}
}
});
}
}
setOnFocusChangeListener
监听器将被调用然后我按下然后它关闭键盘&但没有点击其他视图,现在我再次点击已经有焦点的相同编辑文本,然后会发生什么?
onfocus
监听器从不调用时关闭键盘,这就是我正在寻找的不是您所建议的
使用这个类,
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
public class SoftKeyboard implements View.OnFocusChangeListener
{
private static final int CLEAR_FOCUS = 0;
private ViewGroup layout;
private int layoutBottom;
private InputMethodManager im;
private int[] coords;
private boolean isKeyboardShow;
private SoftKeyboardChangesThread softKeyboardThread;
private List<EditText> editTextList;
private View tempView; // reference to a focused EditText
public SoftKeyboard(ViewGroup layout, InputMethodManager im)
{
this.layout = layout;
keyboardHideByDefault();
initEditTexts(layout);
this.im = im;
this.coords = new int[2];
this.isKeyboardShow = false;
this.softKeyboardThread = new SoftKeyboardChangesThread();
this.softKeyboardThread.start();
}
public void openSoftKeyboard()
{
if(!isKeyboardShow)
{
layoutBottom = getLayoutCoordinates();
im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT);
softKeyboardThread.keyboardOpened();
isKeyboardShow = true;
}
}
public void closeSoftKeyboard()
{
if(isKeyboardShow)
{
im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
isKeyboardShow = false;
}
}
public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback)
{
softKeyboardThread.setCallback(mCallback);
}
public void unRegisterSoftKeyboardCallback()
{
softKeyboardThread.stopThread();
}
public interface SoftKeyboardChanged
{
public void onSoftKeyboardHide();
public void onSoftKeyboardShow();
}
private int getLayoutCoordinates()
{
layout.getLocationOnScreen(coords);
return coords[1] + layout.getHeight();
}
private void keyboardHideByDefault()
{
layout.setFocusable(true);
layout.setFocusableInTouchMode(true);
}
/*
* InitEditTexts now handles EditTexts in nested views
* Thanks to Francesco Verheye (verheye.francesco@gmail.com)
*/
private void initEditTexts(ViewGroup viewgroup)
{
if(editTextList == null)
editTextList = new ArrayList<EditText>();
int childCount = viewgroup.getChildCount();
for(int i=0; i<= childCount-1;i++)
{
View v = viewgroup.getChildAt(i);
if(v instanceof ViewGroup)
{
initEditTexts((ViewGroup) v);
}
if(v instanceof EditText)
{
EditText editText = (EditText) v;
editText.setOnFocusChangeListener(this);
editText.setCursorVisible(true);
editTextList.add(editText);
}
}
}
/*
* OnFocusChange does update tempView correctly now when keyboard is still shown
* Thanks to Israel Dominguez (dominguez.israel@gmail.com)
*/
@Override
public void onFocusChange(View v, boolean hasFocus)
{
if(hasFocus)
{
tempView = v;
if(!isKeyboardShow)
{
layoutBottom = getLayoutCoordinates();
softKeyboardThread.keyboardOpened();
isKeyboardShow = true;
}
}
}
// This handler will clear focus of selected EditText
private final Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message m)
{
switch(m.what)
{
case CLEAR_FOCUS:
if(tempView != null)
{
tempView.clearFocus();
tempView = null;
}
break;
}
}
};
private class SoftKeyboardChangesThread extends Thread
{
private AtomicBoolean started;
private SoftKeyboardChanged mCallback;
public SoftKeyboardChangesThread()
{
started = new AtomicBoolean(true);
}
public void setCallback(SoftKeyboardChanged mCallback)
{
this.mCallback = mCallback;
}
@Override
public void run()
{
while(started.get())
{
// Wait until keyboard is requested to open
synchronized(this)
{
try
{
wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
int currentBottomLocation = getLayoutCoordinates();
// There is some lag between open soft-keyboard function and when it really appears.
while(currentBottomLocation == layoutBottom && started.get())
{
currentBottomLocation = getLayoutCoordinates();
}
if(started.get())
mCallback.onSoftKeyboardShow();
// When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
// and at some moment equals layoutBottom.
// That broke the previous logic, so I added this new loop to handle this.
while(currentBottomLocation >= layoutBottom && started.get())
{
currentBottomLocation = getLayoutCoordinates();
}
// Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
while(currentBottomLocation != layoutBottom && started.get())
{
synchronized(this)
{
try
{
wait(500);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
currentBottomLocation = getLayoutCoordinates();
}
if(started.get())
mCallback.onSoftKeyboardHide();
// if keyboard has been opened clicking and EditText.
if(isKeyboardShow && started.get())
isKeyboardShow = false;
// if an EditText is focused, remove its focus (on UI thread)
if(started.get())
mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
}
}
public void keyboardOpened()
{
synchronized(this)
{
notify();
}
}
public void stopThread()
{
synchronized(this)
{
started.set(false);
notify();
}
}
}
}
在 Android Manifest
中,android:windowSoftInputMode="adjustResize"
是必需的。
/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use the layout root
InputMethodManager im = (InputMethodManager)getSystemService(Service.INPUT_METHOD_SERVICE);
/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged() {
@Override
public void onSoftKeyboardHide() {
// Code here
}
@Override
public void onSoftKeyboardShow() {
// Code here
}
});
/*
Open or close the soft keyboard easily
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();
/* Prevent memory leaks:*/
@Override
public void onDestroy() {
super.onDestroy();
softKeyboard.unRegisterSoftKeyboardCallback();
}
PS - 完全取自 here。
对于 adjustResize
和 FragmentActivity 的情况,@Jaap 接受的解决方案对我不起作用。
这是我的解决方案:
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
private int contentDiff;
private int rootHeight;
@Override
public void onGlobalLayout() {
View contentView = getWindow().findViewById(Window.ID_ANDROID_CONTENT);
if (rootHeight != mDrawerLayout.getRootView().getHeight()) {
rootHeight = mDrawerLayout.getRootView().getHeight();
contentDiff = rootHeight - contentView.getHeight();
return;
}
int newContentDiff = rootHeight - contentView.getHeight();
if (contentDiff != newContentDiff) {
if (contentDiff < newContentDiff) {
onShowKeyboard(newContentDiff - contentDiff);
} else {
onHideKeyboard();
}
contentDiff = newContentDiff;
}
}
};
另一种方法是检查用户何时停止输入......
当 TextEdit 处于焦点时(用户正在/正在键入),您可以隐藏视图(焦点侦听器)
并使用 Handler + Runnable 和文本更改侦听器关闭键盘(无论其可见性如何)并在延迟后显示视图。
要注意的主要是您使用的延迟,这取决于这些 TextEdits 的内容。
Handler timeoutHandler = new Handler();
Runnable typingRunnable = new Runnable() {
public void run() {
// current TextEdit
View view = getCurrentFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// reset focus
view.clearFocus();
// close keyboard (whether its open or not)
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
// SET VIEWS VISIBLE
}
};
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// SET VIEWS GONE
// reset handler
timeoutHandler.removeCallbacks(typingRunnable);
timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
}
}
});
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Reset Handler...
timeoutHandler.removeCallbacks(typingRunnable);
}
@Override
public void afterTextChanged(Editable s) {
// Reset Handler Cont.
if (editText.getText().toString().trim().length() > 0) {
timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
}
}
});
这段代码很好用
将此类用于根视图:
public class KeyboardConstraintLayout extends ConstraintLayout {
private KeyboardListener keyboardListener;
private EditText targetEditText;
private int minKeyboardHeight;
private boolean isShow;
public KeyboardConstraintLayout(Context context) {
super(context);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); //128dp
}
public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}
public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!isInEditMode()) {
Activity activity = (Activity) getContext();
@SuppressLint("DrawAllocation")
Rect rect = new Rect();
getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;
if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
if (keyboardHeight > minKeyboardHeight) {
if (!isShow) {
isShow = true;
keyboardListener.onKeyboardVisibility(true);
}
}else {
if (isShow) {
isShow = false;
keyboardListener.onKeyboardVisibility(false);
}
}
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public boolean isShowKeyboard() {
return isShow;
}
public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
this.targetEditText = targetEditText;
this.keyboardListener = keyboardListener;
}
public interface KeyboardListener {
void onKeyboardVisibility (boolean isVisible);
}
}
并在活动或片段中设置键盘监听器:
rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
@Override
public void onKeyboardVisibility(boolean isVisible) {
}
});
这没有按预期工作......
...已经看到许多使用尺寸计算来检查...
我想确定它是否打开并且I found isAcceptingText()
所以这真的不能回答这个问题,因为它没有解决打开或关闭问题,而更像是打开或关闭,所以它是相关代码,可以在各种情况下帮助其他人......
在一个活动中
if (((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
Log.d(TAG,"Software Keyboard was shown");
} else {
Log.d(TAG,"Software Keyboard was not shown");
}
在一个片段中
if (((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
Log.d(TAG,"Software Keyboard was shown");
} else {
Log.d(TAG,"Software Keyboard was not shown");
}
您可以通过覆盖 Activity 中的两种方法来处理键盘可见性:onKeyUp()
和 onKeyDown()
此链接中的更多信息:https://developer.android.com/training/keyboard-input/commands
在使用“adjustResize”软输入模式(Kotlin 代码)时找到了一种准确判断键盘是否存在的方法
定义几个活动范围变量
private var activityHeight = 0
private var keyboardOpen = false
在 onCreate 中编写如下代码
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
/* Grab initial screen value */
this@ActivityMain.window.decorView.doOnNextLayout {
val displayFrame : Rect = Rect()
this@ActivityMain.window.decorView.getWindowVisibleDisplayFrame(displayFrame)
activityHeight = displayFrame.height()
}
/* Check for keyboard open/close */
this@ActivityMain.window.decorView.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
val drawFrame : Rect = Rect()
this@ActivityMain.window.decorView.getWindowVisibleDisplayFrame(drawFrame)
val currentSize = drawFrame.height()
keyboardOpen = currentSize < activityHeight
Log.v("keyboard1","$keyboardOpen $currentSize - $activityHeight")
}
}
你现在有一个布尔值,它可以准确跟踪键盘是否打开,做你想做的事
不幸的是,我没有足够高的声誉来评论 Jaap van Hengstum 的回答。但我读了一些人的评论,有一个问题,即 contentViewTop
总是 0
并且总是调用 onShowKeyboard(...)
。
我有同样的问题,并找出了我遇到的问题。我使用了 AppCompatActivity
而不是“正常”Activity
。在这种情况下,Window.ID_ANDROID_CONTENT
指的是一个 ContentFrameLayout
,而不是指具有右顶部值的 FrameLayout
。在我的情况下,如果您必须使用另一种活动类型(我刚刚测试了 AppCompatActivity
,也许它也是其他活动类型(如 FragmentActivity
)的问题)使用“正常”Activity
,您必须访问 FrameLayout
,它是 ContentFrameLayout
的祖先。
当键盘显示
rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight()
是真的,否则隐藏
private boolean isKeyboardShown = false;
private int prevContentHeight = 0;
private ViewGroup contentLayout;
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener =
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int contentHeight = contentLayout.getHeight();
int rootViewHeight = contentLayout.getRootView().getHeight();
if (contentHeight > 0) {
if (!isKeyboardShown) {
if (contentHeight < prevContentHeight) {
isKeyboardShown = true;
onShowKeyboard(rootViewHeight - contentHeight);
}
} else {
if (contentHeight > prevContentHeight) {
isKeyboardShown = false;
onHideKeyboard();
}
}
prevContentHeight = contentHeight;
}
}
};
我稍微修改了 Jaap 接受的答案。但在我的情况下,几乎没有 android:windowSoftInputMode=adjustResize
之类的假设,并且键盘在应用程序启动时没有出现在开头。而且,我假设屏幕与父母的身高相匹配。
contentHeight > 0
此检查让我知道相关屏幕是否隐藏或显示以应用键盘事件侦听此特定屏幕。我还在我的主要活动的 onCreate()
方法中传递了 attachKeyboardListeners(<your layout view here>)
中有关屏幕的布局视图。每次相关屏幕的高度发生变化时,我都会将其保存到 prevContentHeight
变量中,以便稍后检查键盘是显示还是隐藏。
对我来说,到目前为止,它工作得很好。我希望它也适用于其他人。
“Jaap van Hengstum”的答案对我有用,但没有必要像他刚才所说的那样设置“android:windowSoftInputMode”!
我把它变小了(它现在只检测我想要的,实际上是显示和隐藏键盘的事件):
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
if(heightDiff <= contentViewTop){
onHideKeyboard();
} else {
onShowKeyboard();
}
}
};
private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;
protected void onShowKeyboard() {}
protected void onHideKeyboard() {}
protected void attachKeyboardListeners() {
if (keyboardListenersAttached) {
return;
}
rootLayout = (ViewGroup) findViewById(R.id.CommentsActivity);
rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);
keyboardListenersAttached = true;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (keyboardListenersAttached) {
rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
}
}
只是不要忘记添加这个
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
attachKeyboardListeners();}
检查以下代码:
XML 代码:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinatorParent"
style="@style/parentLayoutPaddingStyle"
android:layout_width="match_parent"
android:layout_height="match_parent">
.................
</android.support.constraint.ConstraintLayout>
JAVA代码:
//Global Variable
android.support.constraint.ConstraintLayout activityRootView;
boolean isKeyboardShowing = false;
private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
android.support.constraint.ConstraintLayout.LayoutParams layoutParams;
//onCreate or onViewAttached
activityRootView = view.findViewById(R.id.coordinatorParent);
onGlobalLayoutListener = onGlobalLayoutListener();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
//outside oncreate
ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener() {
return new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
int screenHeight = activityRootView.getRootView().getHeight();
int keypadHeight = screenHeight - r.bottom;
if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
if (!isKeyboardShowing) { // keyboard is opened
isKeyboardShowing = true;
onKeyboardVisibilityChanged(true);
}
}
else {
if (isKeyboardShowing) { // keyboard is closed
isKeyboardShowing = false;
onKeyboardVisibilityChanged(false);
}
}
}//ends here
};
}
void onKeyboardVisibilityChanged(boolean value) {
layoutParams = (android.support.constraint.ConstraintLayout.LayoutParams)topImg.getLayoutParams();
if(value){
int length = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, getResources().getDisplayMetrics());
layoutParams.height= length;
layoutParams.width = length;
topImg.setLayoutParams(layoutParams);
Log.i("keyboard " ,""+ value);
}else{
int length1 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 175, getResources().getDisplayMetrics());
layoutParams.height= length1;
layoutParams.width = length1;
topImg.setLayoutParams(layoutParams);
Log.i("keyboard " ,""+ value);
}
}
@Override
public void onDetach() {
super.onDetach();
if(onGlobalLayoutListener != null) {
activityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
有一个键盘已关闭的侦听器。
SearchEditText
类派生自 android.widget.EditText
类。这个类中有一个接口SearchEditText.OnKeyboardDismissListener
。您可以查看文档:
https://developer.android.com/reference/androidx/leanback/widget/SearchEditText
笔记。在使用 SearchEditText 之前,您需要在 build.gradle (:app) 中设置 Gradle 依赖项:
implementation 'androidx.leanback:leanback:1.1.0-alpha05'
也许有人会派上用场。
详细回复:
import androidx.appcompat.app.AppCompatActivity;
import androidx.leanback.widget.SearchEditText;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity
implements SearchEditText.OnKeyboardDismissListener {
SearchEditText searchEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
searchEditText = findViewById(R.id.search_edit_text);
searchEditText.setOnKeyboardDismissListener(this);
}
/**
* Method invoked when the keyboard is dismissed.
*/
@Override
public void onKeyboardDismiss() {
Toast.makeText(this, "The listener worked", Toast.LENGTH_LONG).show();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.leanback.widget.SearchEditText
android:id="@+id/search_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:textSize="20sp"
android:focusableInTouchMode="true"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
注意:监听器适用于:
android:windowSoftInputMode="adjustPan"
android:windowSoftInputMode="adjustResize"
我使用 this 答案和我的任务 LoopingTask 构建了监听器,顺便感谢一下。
使用 Viper Pack android 库中的 SoftKeyboardListener 很容易实现。
只需安装库,然后将其添加到您的代码中:
Lava.app.addSoftKeyboardListener(context, new Lava.SoftKeyboardListener() {
@Override
public void onSoftKeyboardShow(EditText focusedview) {
// when shows
}
@Override
public void onSoftKeyboardHide(EditText focusedview) {
// when hides
}
});
Lava.app.removeSoftKeyboardListeners()
创建
boolean isListenerAdded;
...
@Override
public void onCreate(Bundle sis) {
super.onCreate(sis);
...
if (!isListenerAdded) {
isListenerAdded = true;
// create your listener here
}
...
}
...
或者只是删除所有以前的:
...
@Override
public void onCreate(Bundle sis) {
super.onCreate(sis);
...
Lava.app.removeSoftKeyboardListeners();
// create your listener here
...
}
...
Lava.app.removeSoftKeyboardListeners()
删除所有以前的 SoftKeyboardListener。
在我的情况下,'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'库由于某些原因无法使用滚动视图。这可能是因为滚动视图中的视图高度计算错误。插图就是答案。简单的插入监听器就像一个魅力。这是代码:
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
insets
}