软键盘在 Android 活动中打开和关闭侦听器

我有一个有5个 EditTextActivity。当用户单击第一个 EditText时,软键盘打开输入一些值。我想设置一些其他的 View的可见性到 Gone时,软键盘打开,也是当用户点击第一个 EditText时,也是当软键盘关闭从相同的 EditText的背面按钮。然后我想设置一些其他 View的可见性可见。

是否有任何监听器或回调或任何黑客时,软键盘打开点击第一个 EditText在 Android?

223474 次浏览

正如维克拉姆在评论中指出的那样,只有通过一些丑陋的黑客手段才能检测软键盘是否显示或消失。

也许这对 在编辑文本上设置焦点侦听器来说已经足够了:

yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
//got focus
} else {
//lost focus
}
}
});

这只有在清单中将活动的 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);
}
}
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);
}
}
});
}
}

用这门课,

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();
}

附言,完全取自 给你

不幸的是,我没有足够高的声誉来评论范亨斯塔姆的回答。但是我读了一些人的评论,问题是 contentViewTop总是 0,而 onShowKeyboard(...)总是被调用。

我也遇到了同样的问题,并且找到了自己的问题所在。我使用的是 AppCompatActivity而不是“正常”的 Activity。在这种情况下,Window.ID_ANDROID_CONTENT指的是 ContentFrameLayout,而不是具有正确顶值的 FrameLayout。在我的案例中,使用“正常”的 Activity是可以的,如果你不得不使用另一种活动类型(我刚刚测试了 AppCompatActivity,也许这也是其他活动类型如 FragmentActivity的一个问题) ,你必须访问 FrameLayout,它是 ContentFrameLayout的祖先。

活动:

    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...


}
}
});

小菜一碟 事件库

KeyboardVisibilityEvent.setEventListener(
getActivity(),
new KeyboardVisibilityEventListener() {
@Override
public void onVisibilityChanged(boolean isOpen) {
// Ah... at last. do your thing :)
}
});

片名: 清水康弘

当键盘显示

rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight()

是真的,否则就藏起来

对于 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;
}
}
};

你可以试试:

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;
}
});
}

如果可以,请尝试扩展 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);
}

你如何延伸它:

  1. 实现 onFocus 收听并声明‘ onKeyboard Showed’
  2. 声明‘ onKeyboard Hidden’

我认为,重新计算屏幕高度不是100% 成功,因为前面提到。 需要说明的是,“ onKeyPreIme”的重写并不是在“ hide soft key赶程序”方法上调用的,但是如果你在任何地方这样做,你应该在那里执行“ onKeyboardHidden”逻辑,并且不要创建一个全面的解决方案。

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;
}
}
};

我稍微修改了亚普接受的答案。但在我的例子中,很少有像 android:windowSoftInputMode=adjustResize这样的假设,而且当应用程序启动时,键盘不会在开始时显示出来。而且,我假设这个屏幕符合父母的身高。

contentHeight > 0这个检查提供我知道是否关于屏幕隐藏或显示应用键盘事件监听这个特定的屏幕。另外,我在主要活动的 onCreate()方法中传递了 attachKeyboardListeners(<your layout view here>)中关于屏幕的布局视图。每次当有关屏幕的高度改变时,我将它保存到 prevContentHeight变量,以便稍后检查键盘是显示还是隐藏。

对于我来说,到目前为止,它已经运作得很好了。我希望它对其他人也有效。

另一种方法是检查用户何时停止输入..。

当 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);
}
}
});

“ 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();}

这个代码非常好用

将这个类用于 root 视图:

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) {


}
});

这将工作没有任何需要改变您的活动的 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);
}

第二步: 在你的活动中创建这两个:

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);

你可以使用我的 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
}

这不是我想要的效果。

... 已经看到许多使用大小计算来检查..。

我想确定它是否打开和 我找到了 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");


}

下面的代码正在为我工作,

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
}
}
}
});

您可以通过重写 Activity: onKeyUp()onKeyDown()中的两个方法来处理键盘可见性

检查以下代码:

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);
}
}

我迟到了,但我刚刚发现一个非常方便的依赖在那里。使用它,您可以检查键盘的可见性,以及使键盘“隐藏”和显示随时您想与单行代码。

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);

有一个键盘解散听众。
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"

找到了一个准确的方法,告诉是否键盘时使用“调整大小”软输入模式(科特林代码)

定义几个活动范围变量

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")
}
}

你现在有了一个布尔值,它可以精确地跟踪键盘是否打开,做你想做的事情

首先创建一个 kotlin 文件并添加以下方法:

fun Activity.getRootView(): View {
return findViewById<View>(android.R.id.content)
}
fun Context.convertDpToPx(dp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
this.resources.displayMetrics
)
}
fun Activity.isKeyboardOpen(): Boolean {
val visibleBounds = Rect()
this.getRootView().getWindowVisibleDisplayFrame(visibleBounds)
val heightDiff = getRootView().height - visibleBounds.height()
val marginOfError = Math.round(this.convertDpToPx(50F))
return heightDiff > marginOfError
}


fun Activity.isKeyboardClosed(): Boolean {
return !this.isKeyboardOpen()
}

然后创建一个侦听器类来检查键盘是否打开:

class KeyboardEventListener(
private val activity: AppCompatActivity,
private val callback: (isOpen: Boolean) -> Unit
) : LifecycleObserver {
private val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
private var lastState: Boolean = activity.isKeyboardOpen()
override fun onGlobalLayout() {
val isOpen = activity.isKeyboardOpen()
if (isOpen == lastState) {
return
} else {
dispatchKeyboardEvent(isOpen)
lastState = isOpen
}
}
}
init {
// Dispatch the current state of the keyboard
dispatchKeyboardEvent(activity.isKeyboardOpen())
// Make the component lifecycle aware
activity.lifecycle.addObserver(this)
registerKeyboardListener()
}
private fun registerKeyboardListener() {
activity.getRootView().viewTreeObserver.addOnGlobalLayoutListener(listener)
}
private fun dispatchKeyboardEvent(isOpen: Boolean) {
when {
isOpen  -> callback(true)
!isOpen -> callback(false)
}
}
@OnLifecycleEvent(value = Lifecycle.Event.ON_PAUSE)
@CallSuper
fun onLifecyclePause() {
unregisterKeyboardListener()
}
private fun unregisterKeyboardListener() {
activity.getRootView().viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}

像这样使用它:

override fun onResume() {
super.onResume()
KeyboardEventListener(this) { isOpen -> // handle event }
}

希望对你有用。

解决方案的额外属性在活动片段,但没有任何假设的硬编码高度(如100等)。 只需将 OnGlobalLayoutListener 添加到根视图,并在显示键盘之前保存其初始高度:

var firstLoad         = true
var contentFullWeight = 0


override fun onViewCreated(layoutView: View, savedInstanceState: Bundle?) {
super.onViewCreated(layoutView, savedInstanceState)


view?.viewTreeObserver?.addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener {


if(firstLoad){
contentFullWeight = view?.height!!
firstLoad = false
}


if (view?.height!! < contentFullWeight) {
Log.d("TEZT_KEYBOARD", ">> KBD OPENED")
} else {
Log.d("TEZT_KEYBOARD", ">> KBD closed")
}
})


}

对于在 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()
}
})
}

来源和信用

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/addresses_confirm_root_view"
android:orientation="vertical">

< ——-在 xml 根目录中使用 id ——->

Final LinearLayout activityRootView = view.findViewById (R.id.address _ sure _ root _ view) ; GetViewTreeObserver () . addOnGlobalLayoutListener (新的 ViewTreeObserver.OnGlobalLayoutListener ()){ @ 重写 在 GlobalLayout ()上 public void () Rect r = new Rect () ; //r 将使用视图中该区域仍然可见的坐标填充。 GetWindowVisibleDisplayFrame (r) ;

            int heightDiff = activityRootView.getRootView().getHeight() - r.height();
if (heightDiff > 0.25 * activityRootView.getRootView().getHeight()) {
// if more than 25% of the screen, its probably a keyboard...
onkeyboard();


} else {
//Keyboard not visible
offkeyboard();
}
}
});

我使用 这个答案和我的任务 循环任务构建了这个监听器,顺便说一句,谢谢。

使用 毒蛇帮 android 库中的 软键盘监听器非常容易实现。

只要安装这个库,然后将其添加到代码中:

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()删除所有以前的监听器。 免责声明2: 在活动 onCreate中直接创建一个 SoftKeyboardListener 将在每次活动恢复时添加一个新的 Listener,因此请确保使用布尔值限制:
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()删除所有以前的软键盘监听器。

检查我的 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

在 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
}

插入是唯一的 正式的和正确答案。简单的插入式侦听器非常有效。密码如下:

ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
insets
}

注意! 此解决方案只在设置边缘到边缘模式时有效:

WindowCompat.setDecorFitsSystemWindows(window, false)

请查阅有关 检查键盘软件可见性的正式文件和 边对边模式的正确实施: