前言
Android暂时还没有提供一个合适的API来获取/监听键盘的状态和高度 , 而我们又经常会有这个需求.
最近我的一个项目中,在ugc页面需要在键盘顶部,紧贴着键盘显示一个文字提示,当键盘消失时就隐藏.
因此,我需要监听软键盘的打开/关闭 , 以及获取它的高度.
ViewTreeObserver
A view tree observer is used to register listeners that can be notified of global changes in the view tree. Such global events include, but are not limited to, layout of the whole tree, beginning of the drawing pass, touch mode change…
Android框架提供了一个ViewTreeObserver类,它是一个View视图树的观察者类。ViewTreeObserver类中定义了一系列的公共接口(public interface)。当一个View attach到一个窗口上时就会创建一个ViewTreeObserver对象,这样当一个View的视图树发生改变时,就会调用该对象的某个方法,将事件通知给每个注册的监听者。
OnGlobalLayoutListener是ViewTreeObserver中定义的众多接口中的一个,它用来监听一个视图树中全局布局的改变或者视图树中的某个视图的可视状态的改变。当软键盘由隐藏变为显示,或由显示变为隐藏时,都会调用当前布局中所有存在的View中的ViewTreeObserver对象的dispatchOnGlobalLayout()方法,此方法中会遍历所有已注册的OnGlobalLayoutListener,执行相应的回调方法,将全局布局改变的消息通知给每个注册的监听者。
view.getViewTreeObserver().addOnGlobalLayoutListener(listener);
getWindowVisibleDisplayFrame
Retrieve the overall visible display size in which the window this view is attached to has been positioned in.
getWindowVisibleDisplayFrame()会返回窗口的可见区域高度,通过和屏幕高度相减,就可以得到软键盘的高度了。
完整示例代码
package com.cari.cari.promo.diskon.util; import android.content.Context; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.TypedValue; 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<>(); private final View activityRootView; private int lastSoftKeyboardHeightInPx; private boolean isSoftKeyboardOpened; private Context mContext; //使用时用这个构造方法 public SoftKeyboardStateWatcher(View activityRootView, Context context) { this(activityRootView, false); this.mContext = context; } private SoftKeyboardStateWatcher(View activityRootView) { this(activityRootView, false); } private SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) { this.activityRootView = activityRootView; this.isSoftKeyboardOpened = isSoftKeyboardOpened; activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this); } @Override public void onGlobalLayout() { final Rect r = new Rect(); activityRootView.getWindowVisibleDisplayFrame(r); final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top); if (!isSoftKeyboardOpened && heightDiff > dpToPx(mContext, 200)) { isSoftKeyboardOpened = true; notifyOnSoftKeyboardOpened(heightDiff); } else if (isSoftKeyboardOpened && heightDiff < dpToPx(mContext, 200)) { 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); } /** * @param keyboardHeightInPx 可能是包含状态栏的高度和底部虚拟按键的高度 */ 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(); } } } private static float dpToPx(Context context, float valueInDp) { DisplayMetrics metrics = context.getResources().getDisplayMetrics(); return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics); } }