我需要做一件非常简单的事情 - 找出是否显示了软件键盘。这在Android中是否可行?
答案 0 :(得分:650)
NEW ANSWER 2012年1月25日添加
自从写下面的答案后,有人告诉我ViewTreeObserver和朋友的存在,这些API自版本1以来一直潜伏在SDK中。
而不是要求自定义布局类型,更简单的解决方案是为您的活动的根视图提供一个已知的ID,比如@+id/activityRoot
,将GlobalLayoutListener挂钩到ViewTreeObserver,然后计算您的活动之间的大小差异查看根和窗口大小:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > dpToPx(this, 200)) { // if more than 200 dp, it's probably a keyboard...
// ... do something here
}
}
});
使用以下实用程序:
public static float dpToPx(Context context, float valueInDp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
容易!
注意:强>
您的应用程序必须在Android Manifest android:windowSoftInputMode="adjustResize"
中设置此标志,否则上述解决方案将无效。
原始回答
是的,这是可能的,但它应该比它应该更难。
如果我需要关心键盘出现和消失的时间(经常是这样),那么我所做的就是将我的顶级布局类自定义为覆盖onMeasure()
的类。基本逻辑是,如果布局发现自己填充的内容明显少于窗口的总面积,则可能会显示软键盘。
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/*
* LinearLayoutThatDetectsSoftKeyboard - a variant of LinearLayout that can detect when
* the soft keyboard is shown and hidden (something Android can't tell you, weirdly).
*/
public class LinearLayoutThatDetectsSoftKeyboard extends LinearLayout {
public LinearLayoutThatDetectsSoftKeyboard(Context context, AttributeSet attrs) {
super(context, attrs);
}
public interface Listener {
public void onSoftKeyboardShown(boolean isShowing);
}
private Listener listener;
public void setListener(Listener listener) {
this.listener = listener;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
Activity activity = (Activity)getContext();
Rect rect = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
int diff = (screenHeight - statusBarHeight) - height;
if (listener != null) {
listener.onSoftKeyboardShown(diff>128); // assume all soft keyboards are at least 128 pixels high
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
然后在你的Activity类......
public class MyActivity extends Activity implements LinearLayoutThatDetectsSoftKeyboard.Listener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
LinearLayoutThatDetectsSoftKeyboard mainLayout = (LinearLayoutThatDetectsSoftKeyboard)findViewById(R.id.main);
mainLayout.setListener(this);
...
}
@Override
public void onSoftKeyboardShown(boolean isShowing) {
// do whatever you need to do here
}
...
}
答案 1 :(得分:293)
所以希望这可以帮助别人。
Reuben Scratton给出的新答案非常棒且效率很高,但它确实只有在将windowSoftInputMode设置为adjustResize时才有效。如果将其设置为adjustPan,则仍无法使用其代码片段检测键盘是否可见。为了解决这个问题,我对上面的代码进行了微小的修改。
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);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
... do something here
}
}
});
答案 2 :(得分:51)
这在计算机方面一直存在,但这个问题仍然令人难以置信![/ p>
所以我已经采取了上述答案,并对它们进行了整合和改进......
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 int DefaultKeyboardDP = 100;
// From @nathanielwolf answer... Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
private final Rect r = new Rect();
@Override
public void onGlobalLayout() {
// Convert the dp to pixels.
int estimatedKeyboardHeight = (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, activityRootView.getResources().getDisplayMetrics());
// Conclude whether the keyboard is shown or not.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
boolean isShown = heightDiff >= estimatedKeyboardHeight;
if (isShown == wasOpened) {
Log.d("Keyboard state", "Ignoring global layout change...");
return;
}
wasOpened = isShown;
listener.onVisibilityChanged(isShown);
}
});
}
适合我:)
注意:强> 如果您发现 DefaultKeyboardDP 不适合您的设备,请使用该值并发布评论以便每个人知道该值应该是什么...最终我们将获得适合所有设备的正确值!
有关详情,请查看Cyborg
上的实施情况答案 3 :(得分:51)
对于迟到的回答感到抱歉,但我创建了一个小帮助类来处理打开/关闭事件,通知听众和其他有用的东西,可能会有人觉得它有用:
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import java.util.LinkedList;
import java.util.List;
public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener {
public interface SoftKeyboardStateListener {
void onSoftKeyboardOpened(int keyboardHeightInPx);
void onSoftKeyboardClosed();
}
private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
private final View activityRootView;
private int lastSoftKeyboardHeightInPx;
private boolean isSoftKeyboardOpened;
public SoftKeyboardStateWatcher(View activityRootView) {
this(activityRootView, false);
}
public SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) {
this.activityRootView = activityRootView;
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout() {
final Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (!isSoftKeyboardOpened && heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
isSoftKeyboardOpened = true;
notifyOnSoftKeyboardOpened(heightDiff);
} else if (isSoftKeyboardOpened && heightDiff < 100) {
isSoftKeyboardOpened = false;
notifyOnSoftKeyboardClosed();
}
}
public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
}
public boolean isSoftKeyboardOpened() {
return isSoftKeyboardOpened;
}
/**
* Default value is zero {@code 0}.
*
* @return last saved keyboard height in px
*/
public int getLastSoftKeyboardHeightInPx() {
return lastSoftKeyboardHeightInPx;
}
public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.add(listener);
}
public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.remove(listener);
}
private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardOpened(keyboardHeightInPx);
}
}
}
private void notifyOnSoftKeyboardClosed() {
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardClosed();
}
}
}
}
用法示例:
final SoftKeyboardStateWatcher softKeyboardStateWatcher
= new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);
// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks
答案 4 :(得分:32)
一些改进,以避免错误地检测高密度设备上软键盘的可见性:
高度差阈值应定义为 128 dp ,而不是 128像素。 请参阅Google design doc about Metrics and Grid, 48 dp 适合触摸对象, 32 dp 是按钮的最小尺寸。通用软键盘应包括4行按键,因此最小键盘高度应为: 32 dp * 4 = 128 dp ,这意味着阈值大小应通过乘以设备密度传输到像素。对于xxxhdpi设备(密度4),软键盘高度阈值应为128 * 4 = 512像素。
根视图与其可见区域之间的高度差:
根视图高度 - 状态栏高度 - 可见框架高度=根视图底部 - 可见框架底部,因为状态栏高度等于根视图可见框架的顶部。
private final String TAG = "TextEditor";
private TextView mTextEditor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_editor);
mTextEditor = (TextView) findViewById(R.id.text_editor);
mTextEditor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
isKeyboardShown(mTextEditor.getRootView());
}
});
}
private boolean isKeyboardShown(View rootView) {
/* 128dp = 32dp * 4, minimum button height 32dp and generic 4 rows soft keyboard */
final int SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD = 128;
Rect r = new Rect();
rootView.getWindowVisibleDisplayFrame(r);
DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
/* heightDiff = rootView height - status bar height (r.top) - visible frame height (r.bottom - r.top) */
int heightDiff = rootView.getBottom() - r.bottom;
/* Threshold size: dp to pixels, multiply with display density */
boolean isKeyboardShown = heightDiff > SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD * dm.density;
Log.d(TAG, "isKeyboardShown ? " + isKeyboardShown + ", heightDiff:" + heightDiff + ", density:" + dm.density
+ "root view height:" + rootView.getHeight() + ", rect:" + r);
return isKeyboardShown;
}
答案 5 :(得分:11)
哇,我们有好消息Android Geeks。现在该告别旧方法了。 首先,我将添加official release note来阅读和了解有关这些方法/类的更多信息,然后我们将看到these amazing methods /类
突破性提示: 在发布这些类/方法之前,请勿将它们添加到发布应用中
如何检查键盘的可见性
val insets = ViewCompat.getRootWindowInsets(view)
val isKeyboardVisible = insets.isVisible(Type.ime())
其他实用程序
如何获取键盘高度
val insets = ViewCompat.getRootWindowInsets(view)
val keyboardHeight = insets.getInsets(Type.ime()).bottom
如何显示/隐藏键盘
val controller = view.windowInsetsController
// Show the keyboard
controller.show(Type.ime())
// Hide the keyboard
controller.hide(Type.ime())
注意:WindowInsetsController是在API-30中添加的,因此请等到向后兼容类不可用。
如何收听键盘隐藏/显示事件
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val isKeyboardVisible = insets.isVisible(Type.ime())
if (isKeyboardVisible) {
// Do it when keyboard is being shown
} else {
// Do it when keyboard is hidden
}
// Return the insets to keep going down this event to the view hierarchy
insets
}
答案 6 :(得分:8)
我用了一点时间来解决这个问题......我运行了一些CastExceptions,但想到你可以在layout.xml中用类名替换LinearLayout。
像这样:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/llMaster">
<com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard android:background="@drawable/metal_background"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:id="@+id/rlMaster" >
<LinearLayout android:layout_width="fill_parent"
android:layout_height="1dip" android:background="@drawable/line"></LinearLayout>
....
</com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard>
</LinearLayout>
这样你就不会遇到任何演员问题。
...如果您不想在每个页面上执行此操作,我建议您使用“Android中的MasterPage”。请看这里的链接: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx
答案 7 :(得分:7)
检查元素的高度是不可靠的,因为某些键盘如WifiKeyboard的高度为零。
相反,您可以使用showSoftInput()和hideSoftInput()的回调结果来检查键盘的状态。
的完整详情和示例代码https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android
答案 8 :(得分:5)
这个想法是,如果您需要隐藏键盘并同时检查软输入状态,请使用以下解决方案:
public boolean hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}
如果在隐藏之前显示键盘,则此方法返回true。
答案 9 :(得分:4)
我没有假设差异编码,而是做了类似的事情,因为我的应用程序中没有菜单选项。
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
}
}
});
答案 10 :(得分:3)
您可以使用活动的装饰视图来观察软键盘的隐藏。
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);
}
}
答案 11 :(得分:3)
还有系统插入的解决方案,但它仅适用于API >= 21
(Android L
)。假设你有BottomNavigationView
,它是LinearLayout
的孩子,你需要在显示键盘时隐藏它:
> LinearLayout
> ContentView
> BottomNavigationView
您需要做的就是以这种方式扩展LinearLayout
:
public class KeyboardAwareLinearLayout extends LinearLayout {
public KeyboardAwareLinearLayout(Context context) {
super(context);
}
public KeyboardAwareLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public KeyboardAwareLinearLayout(Context context,
@Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public KeyboardAwareLinearLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
int childCount = getChildCount();
for (int index = 0; index < childCount; index++) {
View view = getChildAt(index);
if (view instanceof BottomNavigationView) {
int bottom = insets.getSystemWindowInsetBottom();
if (bottom >= ViewUtils.dpToPx(200)) {
// keyboard is shown
view.setVisibility(GONE);
} else {
// keyboard is hidden
view.setVisibility(VISIBLE);
}
}
}
return insets;
}
}
这个想法是,当显示键盘时,系统插入更改的值很大.bottom
。
答案 12 :(得分:3)
隐藏的方法可以为此提供帮助,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()
}
}
答案 13 :(得分:3)
我发现@ 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
}
}
});
答案 14 :(得分:2)
根据文档。 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())
}
答案 15 :(得分:2)
我使用了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
}
}
});
答案 16 :(得分:2)
这些解决方案都不适用于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
}
}
});
答案 17 :(得分:2)
它在计算机方面一直存在,但这个问题仍然令人难以置信! 所以我已经采取了上述答案,并对它们进行了整合和改进......
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);
}
});
}
它对我有用。
答案 18 :(得分:1)
我在上面使用大多数建议添加固定数字的解决方案时遇到了一个错误。
S4具有高dpi,导致导航栏的高度为100px,因此我的应用程序认为键盘一直处于打开状态。
因此,随着所有新的高分辨率手机的发布,我相信使用硬编码值并不是一个长期的好主意。
我在各种屏幕和设备上进行一些测试后发现的更好的方法是使用百分比。 获取decorView和ur app内容之间的区别,然后检查该差异的百分比。 根据我得到的统计数据,大多数导航条(无论大小,分辨率等)都占据屏幕的3%到5%。在键盘打开的情况下,它占据了屏幕的47%到55%之间。
作为结论,我的解决方案是检查差异是否超过10%然后我假设键盘打开。
答案 19 :(得分:1)
试试这个:
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 ... \\
}
}
});
答案 20 :(得分:1)
在更改viewpager中片段的方向时,我很难保持键盘状态。我不确定为什么,但它似乎很不稳定,并且与标准活动的行为不同。
要在这种情况下保持键盘状态,首先应将android:windowSoftInputMode = "stateUnchanged"
添加到AndroidManifest.xml
。但是,您可能会注意到,这实际上并没有解决整个问题 - 如果之前在方向更改之前打开了键盘,则键盘不会打开。在所有其他情况下,行为似乎是正确的。
然后,我们需要实现这里提到的解决方案之一。 我找到的最干净的是George Maisuradze的 - 使用hideSoftInputFromWindow的布尔回调:
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
我将此值存储在Fragment的onSaveInstanceState
方法中并检索onCreate
。然后,如果键盘的值为onCreateView
,我强行显示true
中的键盘(如果键盘在碎片破坏之前实际隐藏之前键盘可见,则返回true)。
答案 21 :(得分:1)
你可以尝试这个,对我很有用:
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isAcceptingText()) {
//Software Keyboard was shown..
} else {
//Software Keyboard was not shown..
}
答案 22 :(得分:1)
我的答案与Kachi的答案基本相同,但我把它包装成一个很好的助手类来清理它在整个应用程序中使用的方式。
import android.app.Activity;
import android.app.Fragment;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
/**
* Detects Keyboard Status changes and fires events only once for each change
*/
public class KeyboardStatusDetector {
KeyboardVisibilityListener visibilityListener;
boolean keyboardVisible = false;
public void registerFragment(Fragment f) {
registerView(f.getView());
}
public void registerActivity(Activity a) {
registerView(a.getWindow().getDecorView().findViewById(android.R.id.content));
}
public KeyboardStatusDetector registerView(final View v) {
v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
v.getWindowVisibleDisplayFrame(r);
int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
/** Check this variable to debounce layout events */
if(!keyboardVisible) {
keyboardVisible = true;
if(visibilityListener != null) visibilityListener.onVisibilityChanged(true);
}
} else {
if(keyboardVisible) {
keyboardVisible = false;
if(visibilityListener != null) visibilityListener.onVisibilityChanged(false);
}
}
}
});
return this;
}
public KeyboardStatusDetector setVisibilityListener(KeyboardVisibilityListener listener) {
visibilityListener = listener;
return this;
}
public static interface KeyboardVisibilityListener {
public void onVisibilityChanged(boolean keyboardVisible);
}
}
您可以使用它来检测整个应用程序中的键盘更改,如下所示:
new KeyboardStatusDetector()
.registerFragment(fragment) //register to a fragment
.registerActivity(activity) //or register to an activity
.registerView(view) //or register to a view
.setVisibilityListener(new KeyboardVisibilityListener() {
@Override
public void onVisibilityChanged(boolean keyboardVisible) {
if(keyboardVisible) {
//Do stuff for keyboard visible
}else {
//Do stuff for keyboard hidden
}
}
});
注意:只使用其中一个“注册”调用。它们都是相同的,只是为了方便
答案 23 :(得分:0)
在理解了不同分辨率的一些问题后,我决定使用相对大小。我注意到可见和隐藏状态之间的差异大约是30%。所以我决定用0.3替换128 PX。
我添加了这个类监听器来通知任何变化。
这是我的版本
import android.app.*;
import android.graphics.*;
import android.view.*;
public class SoftKeyboardState {
public static final int HIDDEN = 0, VISIBLE = 1;
private OnKeyboardStateChangedListener listener;
private View decorView;
public SoftKeyboardState(Activity activity) {
this.decorView = activity.findViewById(android.R.id.content);
initKeyboardListener();
}
private void initKeyboardListener() {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener(){
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) && (lastVisibleDecorViewHeight / visibleDecorViewHeight >= 0.3f)) {
// visible
if (listener != null)listener.onKeyboardStateChanged(VISIBLE);
} else if ((lastVisibleDecorViewHeight < visibleDecorViewHeight) && (visibleDecorViewHeight / lastVisibleDecorViewHeight >= 0.3f)) {
// hidden
if (listener != null)listener.onKeyboardStateChanged(HIDDEN);
}
}
lastVisibleDecorViewHeight = visibleDecorViewHeight;
}
});
}
public void setOnKeyboardStateChangedListener(OnKeyboardStateChangedListener listener) {
this.listener = listener;
}
public interface OnKeyboardStateChangedListener {
public void onKeyboardStateChanged(int state);
}
}
答案 24 :(得分:0)
View#setOnApplyWindowInsetsListener 可用于获取窗口插入回调
public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) {
getListenerInfo().mOnApplyWindowInsetsListener = listener;
}
//OnApplyWindowInsetsListener
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets);
而 boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime())
可以给出可见性状态。
答案 25 :(得分:0)
这可能不适合生产,因为它会打开键盘。请注意,类似函数返回的布尔值未在 API 中指定,因此不可靠。请参阅此处的文档...
public boolean showSoftInput (View view,
int flags,
ResultReceiver resultReceiver)
请注意,此方法采用 ResultReceiver。它可以得到结果:RESULT_UNCHANGED_SHOWN、RESULT_UNCHANGED_HIDDEN、RESULT_SHOWN 或 RESULT_HIDDEN。如果您得到 RESULT_UNCHANGED_SHOWN,则键盘可见。如果您需要它保持关闭(如果它已关闭),则需要将其关闭。
答案 26 :(得分:0)
除了正确答案之外,当在片段中使用 webview 时,我必须在 onCreateView 的末尾添加它。
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
可能是因为我在片段内运行 Webview 或者可能是 API 30 上的新行为,我的问题是即使显示键盘,片段的高度也从未改变。
所以对于 Fragment,整个代码应该是
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
//mWebView.postUrl("https://www.google.com/");
final View activityRootView = view;
layoutListener = 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);
// This variable was created only for Debug purposes and
// to see the height change when clicking on a field inside mWebView
int screenHeight = activityRootView.getRootView().getHeight();
Log.d("onGlobalLayout", "rect: " + r.toString());
Log.d("onGlobalLayout", "screenHeight: " + screenHeight);
//The difference on the heights from bottom to top and on the root height
int heightDiff = screenHeight - (r.bottom - r.top);
Log.d("onGlobalLayout", "heightDiff: " + heightDiff);
//I suggest to put 250 on resources to have better order
float dpx = dpToPx(getActivity(), 250);
if (previousHeightDiff != heightDiff) {
if (heightDiff > dpx) {
isSoftKeyboardPresent = true;
} else {
isSoftKeyboardPresent = false;
}
previousHeightDiff = heightDiff;
}
}
};
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
return view;
}
private static float dpToPx(Context context, float valueInDp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
答案 27 :(得分:0)
这是我的解决方案,它有效。不要查找像素大小,只需检查内容视图的高度是否已更改:
// Scroll to the latest comment whenever the keyboard is shown
commentsContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
private int oldHeight;
@Override
public void onGlobalLayout() {
int newHeight = commentsContent.getMeasuredHeight();
if (newHeight < oldHeight) {
// Check for the keyboard showing in case the height difference
// is a result of orientation change
if (isSoftKeyboardShowing(CommentsActivity.this)) {
// Keyboard is showing so scroll to the latest comment
scrollToLatestComment();
}
}
oldHeight = newHeight;
}
});
public static boolean isSoftKeyboardShowing(Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
return inputMethodManager.isActive();
}
答案 28 :(得分:0)
不要制作任何硬代码。最好的方法是在使用KeyBord Show获取焦点在EditText上时调整视图大小。 您可以使用下面的代码将活动的resize属性添加到Manifest文件中。
android:windowSoftInputMode="adjustResize"
答案 29 :(得分:0)
参考@ TacB0sS的this答案我在科特林开发了一个课程。希望这会有所帮助。让我知道是否需要改进。
class KeyboardVisibilityObserver(val layRootContainer: View?, val keyboardVisibilityListener: KeyboardVisibilityListener?) {
var isKeyboardOpen = false
private set
private var keyBoardObserver = object : ViewTreeObserver.OnGlobalLayoutListener {
private val DefaultKeyboardDP = 100
// Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
private val EstimatedKeyboardDP = DefaultKeyboardDP + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 48 else 0
private val r = Rect()
override fun onGlobalLayout() {
if (layRootContainer != null) {
// Convert the dp to pixels.
val estimatedKeyboardHeight = TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP.toFloat(), layRootContainer.resources.displayMetrics).toInt()
// Conclude whether the keyboard is shown or not.
layRootContainer.getWindowVisibleDisplayFrame(r)
val heightDiff = layRootContainer.rootView.height - (r.bottom - r.top)
val isShown = heightDiff >= estimatedKeyboardHeight
if (isShown == isKeyboardOpen) {
// Log.d("Keyboard state", "Ignoring global layout change...");
return
}
isKeyboardOpen = isShown
keyboardVisibilityListener?.onKeyboardVisibilityChanged(isKeyboardOpen)
}
}
}
init {
layRootContainer?.viewTreeObserver?.addOnGlobalLayoutListener(keyBoardObserver)
}
// call this in onDestroy
fun removeObserver(){
layRootContainer?.viewTreeObserver?.removeOnGlobalLayoutListener(keyBoardObserver)
}
interface KeyboardVisibilityListener {
fun onKeyboardVisibilityChanged(isKeyboardOpen: Boolean)
}
}
答案 30 :(得分:0)
因为:
因此,猜测它是IME总是错误的-不要怀疑!!!
@ kevin-du是目前最好的解决方案,因为它查询IMM以获取IME高度-但是它说该方法是隐藏的API,因此使用它可能会以错误的“假阴性结果”的方式危险-由错误的dev用法。
答案 31 :(得分:0)
也许这会帮助您:
self.window?.rootViewController = tabBarController
答案 32 :(得分:0)
此代码非常好用
将此类用于根视图:
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);
}
public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}
public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}
@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) {
}
});
答案 33 :(得分:0)
有一种直接的方法可以找到它。并且,它不需要任何布局更改
因此,它也适用于沉浸式全屏模式。
诀窍是你试图隐藏或显示软键盘并捕获该尝试的结果
没有恐慌,这并没有真正显示或隐藏键盘。我们只是要求国家。
为了保持最新,您可以简单地重复操作,例如,每200毫秒,使用一个处理程序。
您可以在此处找到实施:https://stackoverflow.com/a/27567074/2525452
答案 34 :(得分:0)
不需要LayoutListener
的方法在我的情况下,我想在更换片段之前保存键盘的状态。我从onSaveInstanceState
调用方法hideSoftInputFromWindow,关闭键盘并返回键盘是否可见。
这种方法很简单,但可能会改变键盘的状态。
答案 35 :(得分:0)
我认为这种方法可以帮助您找出键盘是否可见。
public Boolean isSoftKeyBoardVisible(){
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isAcceptingText()) {
Log.d(TAG,"Software Keyboard was shown");
return true;
} else {
Log.d(TAG,"Software Keyboard was not shown");
return false;
}
}
答案 36 :(得分:0)
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
)将不起作用。
如果您使用半透明状态栏,activityRootView.getHeight()
将永远不会改变天气,软键盘可见。它将始终返回活动和状态栏的高度。
例如,Nexus 4,Android 5.0.1,将android:windowTranslucentStatus
设置为true,它将永远返回1184,即使ime已经打开。如果你将android:windowTranslucentStatus
设置为false,它将正确返回Height,如果ime不可见,则返回1134(不包括状态栏)。关闭ime,它将返回5xx(取决于ime的高度)
我不知道天气这是一个错误,我尝试4.4.4和5.0.1,结果是一样的。
所以,到目前为止,第二个最常见的答案是,Kachi的解决方案将是计算ime高度最安全的方法。这是一份副本:
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);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
... do something here
}
}
});
答案 37 :(得分:0)
这是一种解决方法,可以了解软键盘是否可见。
一些流行的键盘在其classNames中有某些关键字:
Google AOSP = IME
Swype = IME
Swiftkey = KeyboardService
Fleksy = keyboard
Adaptxt = IME (KPTAdaptxtIME)
Smart = Keyboard (SmartKeyboard)
从ActivityManager.RunningServiceInfo中,检查ClassNames中的上述模式。此外,ActivityManager.RunningServiceInfo的 clientPackage = android,表示键盘已绑定到系统。
上述信息可以合并,以确定软键盘是否可见。
答案 38 :(得分:0)
Reuben Scratton和Kachi提供的解决方案似乎依赖于设备的像素密度,如果你有一个高密度设备,即使键盘关闭,高度差也可能大于100。稍微解决一下,看看初始高度差(键盘向下),然后与当前差异进行比较。
ng-bind-html-unsafe
答案 39 :(得分:0)
思考有一个简单的方法,像这样:
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.isActive();
您还可以查看他是否在特定视图中处于活动状态:
imm.isActive(View v);
答案 40 :(得分:0)
我知道您是否可以确定键盘是否隐藏。
H = [X;Y;ones(1,5)]; %// points as 3D homogeneous coordinates
Tc = [1 0 -Cx; 0 1 -Cy; 0 0 1]; %// translation as a matrix
Tr = [cosd(30) -sind(30) 0; sind(30) cosd(30) 0; 0 0 1]; %// rotation
Hr = inv(Tc) * Tr * Tc * H; %// all transformations as matrix products
plot( Hr(1,:), Hr(2,:) ); %// the rotated rect
这适用于平板电脑。导航栏水平显示时。
答案 41 :(得分:0)
if (keyopen())
{
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);
}
以上功能是我用来检查键盘是否可见的功能。如果是,那我就关闭它。
下面显示了所需的两种方法。
首先,在onCreate中定义可行的窗口高度。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// add to onCreate method
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
sheight= rectgle.bottom;
//
}
然后,添加一个布尔方法,该方法获取该实例的Window高度。如果它与原始版本不匹配(假设您没有沿途改变它......)那么键盘是打开的。
public boolean keyopen()
{
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int curheight= rectgle.bottom;
if (curheight!=sheight)
{
return true;
}
else
{
return false;
}
}
了frotz!
答案 42 :(得分:0)
我知道这是一个老帖子,但我认为这是我所知道的最简单的方法,我的测试设备是Nexus 5.我还没有在其他设备上尝试过。如果他们发现我的代码不好,希望其他人分享他们的方法:)
public static boolean isKeyboardShown(Context context, View view) {
if (context == null || view == null) {
return false;
}
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
imm.hideSoftInputFromWindow返回布尔值。
谢谢,
答案 43 :(得分:-1)
此解决方案可能会重新打开键盘,但它可以正常工作。
InputMethodManager inputManager = ( (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE) );
private boolean isKeyboardShowing() {
boolean isKeyboardShowing = inputManager.hideSoftInputFromWindow(irrelevantView.getWindowToken(), 0);
if (isKeyboardShowing) {
inputManager.showSoftInput(this.getCurrentFocus(), 0);
}
return isKeyboardShowing;
}
答案 44 :(得分:-2)
InputMethodManager包含有关软键盘的信息。你可以通过以下活动获得它:
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
你可以玩弄它,看它能告诉你什么。您可以使用它来显示或隐藏软输入...