Bootstrap

Android监听系统输入法键盘弹出显示与隐藏事件

Android监听系统输入法键盘弹出显示与隐藏事件

有时候需要监听Android系统输入法的弹出显示事件,比如:微信聊天时,不管你当前在聊天中的什么位置(上滑查看消息历史),每当你点击输入框时,都会自动帮你聚集到最新的聊天记录。
而android系统没有提供对应的API来监听输入法的显示与隐藏,那就需要我们自己来实现了。
我目前发现有2种方式可以实现。

实现原理:通过监听布局的高度变化来间接监听输入法的弹出事件


  • 方式一

通过父布局的onLayout回调高度的改变来判断输入法的显示与隐藏
上代码!

public class MyMeasureLinearLayout extends LinearLayout {
    public static final String TAG = "MeasureLinearLayout";
    public static final boolean DEBUG = true;

    private FragmentSizeObserver mObserver;

    private int initBottom = -1;
    private int initLeft  = -1;

    public MyMeasureLinearLayout(Context context){
        this(context, null);
    }

    public MyMeasureLinearLayout(Context context, AttributeSet attrs){
        this(context, attrs, 0);
    }

    public MyMeasureLinearLayout(Context context, AttributeSet attrs,
            int defStyleAttr){
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // TODO Auto-generated method stub
        super.onLayout(changed, l, t, r, b);
        Log.d(TAG, " changed  "+changed +" b "+b);
        if(initBottom == -1 || initLeft == -1){
            initBottom = b;
            initLeft   = l;
            return;
        }
        if(changed){
            Log.d(TAG, " height "+b +" width "+r
                    +" initBottom "+initBottom+" initLeft "+initLeft);
            int height = b - initBottom; //高度变化值(弹出输入法,布局变小,则为负值)
            int width  = r - initLeft;  // 当前屏幕宽度(对应输入法而言无影响)
            onInputSizeChanged(width, height);
        }   
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }
    private void onInputSizeChanged(int widthDiff,int heightDiff) {
        if(mObserver != null){
            mObserver.onFragmentSizeChanged(widthDiff, heightDiff);
        }
    }
    public void registFragmentSizeObserver(FragmentSizeObserver observer) {
        mObserver = observer;       
    }
    public interface FragmentSizeObserver{
        public void onFragmentSizeChanged(int wdiff,int hdiff);
    }
}

原理分析
自定义MyMeasureLinearLayout ,继承自LinearLayout,以该布局作为包含EditText View在内的父布局容器,当EditText获取焦点时,系统输入法弹出,则该父布局整体被上移了,下面留出空间来显示输入法,所以可以根据布局被移动前后的bottom值变化来计算出高度的差值

FragmentSizeObserver接口中的onFragmentSizeChanged方法表示高度发生变化时的回调,可以实现该方法来做输入法弹出与隐藏的逻辑。
代码参考:

实现
public void onFragmentSizeChanged(int wdiff, int hdiff) {
        if (DEBUG)
            Log.d(TAG, "onFragmentSizeChanged wdiff " + wdiff + " hdiff "
                    + hdiff);
//      doFragmentSizeChanged(wdiff, hdiff);
if(hdiff < -200){ // show soft input
// 处理输入法弹出事件
}
else if(hdiff == 0){ // hide soft input
// 处理输入法隐藏事件
            if(null != mMessageRoot){
                Log.d(TAG, "--keybord hide soft input--add messageRootListener ");
                //mMessageRoot.getViewTreeObserver().addOnGlobalLayoutListener(messageRootListener);
            }
        }
    }


  • 方式二

可以不用实现自定义的布局,只需要用系统原生的布局如LinearLayout或RelativeLayout,获取根布局的实例,设置监听OnGlobalLayoutListener,通过根布局高度变化大小来判断输入法的弹出。
原理和方式一类似。注意一定要是在根布局上监听。
代码参考

...
mMessageRoot = (RelativeLayout) root.findViewById(R.id.message_root);

if(null != mMessageRoot){
    mMessageRoot.getViewTreeObserver().addOnGlobalLayoutListener(messageRootListener);
        }

....
OnGlobalLayoutListener messageRootListener = new OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            int heightDiff = mMessageRoot.getRootView().getHeight() - mMessageRoot.getHeight();
            if(heightDiff > 250){  // 700c (113, 473); 880 (146, 728); 850 (219, 1044)
                // keybord has show
                if(null != mListView){
                    mListView.setSelectionFromTop(mDataList.size() - 1, -30);
                    mMessageRoot.getViewTreeObserver().removeOnGlobalLayoutListener(messageRootListener);// 执行完输入法弹出逻辑后,应及时取消监听
                    Log.d(TAG, "--keybord has show we set the select index :"+(mDataList.size() -1));
                }
            }
            Log.d(TAG, "--messageRootListener--heightDiff:"+heightDiff);
        }
    };
....

public void onFragmentSizeChanged(int wdiff, int hdiff) {
        if(hdiff == 0){ // hide soft input
            if(null != mMessageRoot){
                Log.d(TAG, "--keybord hide soft input--add messageRootListener ");
//输入法隐藏后,继续添加监听             
mMessageRoot.getViewTreeObserver().addOnGlobalLayoutListener(messageRootListener);
            }
        }
    }

总结:通过布局的高度变化来间接实现监听输入法的弹出事件,布局高度的变化监听有不同的方法,我列出的方法仅供参考,借此抛砖引玉,欢迎大家评论留言。

;