我需要做一件非常简单的事情——找出软键盘是否显示。这在安卓系统中可行吗?
我需要做一件非常简单的事情——找出软键盘是否显示。这在安卓系统中可行吗?
有一个隐藏的方法可以帮助解决这个问题,InputMethodManager.getInputMethodWindowVisibleHeight
。但我不知道为什么它被隐藏了。
import android.content.Context
import android.os.Handler
import android.view.inputmethod.InputMethodManager
class SoftKeyboardStateWatcher(private val ctx: Context) {
companion object {
private const val DELAY = 10L
}
private val handler = Handler()
private var isSoftKeyboardOpened: Boolean = false
private val height: Int
get() {
val imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val method = imm.javaClass.getMethod("getInputMethodWindowVisibleHeight")
method.isAccessible = true
return method.invoke(imm) as Int
}
private val task: Runnable by lazy {
Runnable {
start()
if (!isSoftKeyboardOpened && height > 0) {
isSoftKeyboardOpened = true
notifyOnSoftKeyboardOpened(height)
} else if (isSoftKeyboardOpened && height == 0) {
isSoftKeyboardOpened = false
notifyOnSoftKeyboardClosed()
}
}
}
var listener: SoftKeyboardStateListener? = null
interface SoftKeyboardStateListener {
fun onSoftKeyboardOpened(keyboardHeightInPx: Int)
fun onSoftKeyboardClosed()
}
fun start() {
handler.postDelayed(task, DELAY)
}
fun stop() {
handler.postDelayed({
if (!isSoftKeyboardOpened) handler.removeCallbacks(task)
}, DELAY * 10)
}
private fun notifyOnSoftKeyboardOpened(keyboardHeightInPx: Int) {
listener?.onSoftKeyboardOpened(keyboardHeightInPx)
}
private fun notifyOnSoftKeyboardClosed() {
listener?.onSoftKeyboardClosed()
}
}
我没有在我的应用程序中使用菜单选项,而是像这样做,不假设编码差异。
final View root= findViewById(R.id.myrootview);
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = root.getRootView().getHeight() - root.getHeight();
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int contentViewTop=
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
if(heightDiff <= contentViewTop){
//Soft KeyBoard Hidden
}else{
//Soft KeyBoard Shown
}
}
});
public final class SoftKeyboardUtil {
public static final String TAG = "SoftKeyboardUtil";
public static void observeSoftKeyBoard(Activity activity , final OnSoftKeyBoardHideListener listener){
final View decorView = activity.getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
int displayHight = rect.bottom - rect.top;
int hight = decorView.getHeight();
boolean hide = (double)displayHight / hight > 0.8 ;
if(Log.isLoggable(TAG, Log.DEBUG)){
Log.d(TAG ,"DecorView display hight = "+displayHight);
Log.d(TAG ,"DecorView hight = "+ hight);
Log.d(TAG, "softkeyboard visible = " + !hide);
}
listener.onSoftKeyBoardVisible(!hide);
}
});
}
public interface OnSoftKeyBoardHideListener{
void onSoftKeyBoardVisible(boolean visible);
}
}
我发现将@Reuben_Scratton的方法和@Yogesh的方法结合起来似乎效果最好。将它们两个的方法结合起来,可以得到如下结果:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
// ... do something here
}
}
});
尝试这个:
final View activityRootView = getWindow().getDecorView().getRootView();
activityRootView.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.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff < activityRootView.getRootView().getHeight() / 4 ) { // if more than 100 pixels, its probably a keyboard...
// ... do something here ... \\
}
}
});
这些解决方案对于Lollipop来说都行不通。在Lollipop中,activityRootView.getRootView().getHeight()
包括按钮栏的高度,而测量视图则不包括它。我已经将上面最好/最简单的解决方案调整为适用于Lollipop。
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
Resources res = getResources();
// The status bar is 25dp, use 50dp for assurance
float maxDiff =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, res.getDisplayMetrics());
//Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
float buttonBarHeight =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, res.getDisplayMetrics());
maxDiff += buttonBarHeight;
}
if (heightDiff > maxDiff) { // if more than 100 pixels, its probably a keyboard...
...do something here
}
}
});
我使用了Reuban答案的变种,在某些情况下,特别是在高分辨率设备上,这种方法更加有效。
final View activityRootView = findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightView = activityRootView.getHeight();
int widthView = activityRootView.getWidth();
if (1.0 * widthView / heightView > 3) {
//Make changes for Keyboard not visible
} else {
//Make changes for keyboard visible
}
}
});
R.id.activityRoot
,你可以直接使用 android.R.id.content
,它正是你所需要的。 - Marcin Orlowski在使用上述大部分建议添加固定数字的解决方案时,我刚刚遇到了一个错误。
S4具有较高dpi,导致导航栏高度为100px,因此我的应用程序认为键盘一直处于打开状态。
因此,随着发布越来越多的新高分辨率手机,我认为长期使用硬编码值不是一个好主意。
在多个屏幕和设备上进行了一些测试后,我发现更好的方法是使用百分比。获取decorView和应用程序内容之间的差异,然后检查该差异的百分比。从我得到的统计数据来看,大多数导航栏(无论大小、分辨率等)将占据屏幕的3%到5%。而如果键盘打开,则会占用47%到55%的屏幕空间。
作为结论,我的解决方案是检查差异是否超过10%,然后我假设它是一个打开的键盘。
在计算机领域,这个问题已经存在了很久,但它仍然是不可思议的相关!因此,我已经综合并完善了以上答案...
public interface OnKeyboardVisibilityListener {
void onVisibilityChanged(boolean visible);
}
public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
private boolean wasOpened;
private final Rect r = new Rect();
@Override
public void onGlobalLayout() {
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
boolean isOpen = heightDiff > 100;
if (isOpen == wasOpened) {
logDebug("Ignoring global layout change...");
return;
}
wasOpened = isOpen;
listener.onVisibilityChanged(isOpen);
}
});
}
这对我有效。
根据文档.. https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat
查看发布说明.. https://developer.android.com/jetpack/androidx/releases/core#1.5.0-alpha02
要获取当前键盘的可见性,可以使用getRootWindowInsets
,然后调用 isVisible()
函数,并传入IME类型。
val windowinsetscompat = ViewCompat.getRootWindowInsets(view)
val imeVisible = windowinsetscompat.isVisible(Type.ime())
还有一种用于监听变化的方法是 OnApplyWindowInsetsListener
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val imeVisible = insets.isVisible(Type.ime())
}