假设有如下界面(输入法的上面的输入区域是用Dialog实现的)

Android 监听onViewAttachedToWindow_android

要求当输入法关闭的时候,Dialog也一起关闭,这样用户就不需要返回两次了。

网上找了很多资料都没有很好的解决这个问题,输入法是第三方程序,确实不好检测它的关闭与显示。

后来在EditText源码中看到

public boolean onKeyPreIme(int keyCode, KeyEvent event){
}

对该方法官方文档:

Handle a key event before it is processed by any input method associated with the view hierarchy. This can be used to intercept key events in special situations before the IME consumes them; a typical example would be handling the BACK key to update the application’s UI instead of allowing the IME to see it and close itself.

特别是最后一句话,该方法可以用来处理返回键来更新程序的UI。

一、使用onKeyPreIme()方法来实现

所以我们监听通过 onKeyPreIme 的返回键,来关闭Dialog。

public class MyEditText extends EditText {

    public MyEditText(Context context) {
        super(context);
    }

    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    public interface BackListener {
        void back(TextView textView);
    }



    private BackListener listener;

    public void setBackListener(BackListener listener) {
        this.listener = listener;
    }



    @Override

    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
            if (listener != null) {
                listener.back(this);
            }
        }
        return false;
    }
}

在布局中使用我们自定义的EditText来代替系统的EditText:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:orientation="vertical">



    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:orientation="horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:text="长"
                android:textColor="#333"
                android:textSize="14sp"/>



            <MyEditText
                android:id="@+id/et_length"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:background="@null"
                android:gravity="center"
                android:hint="0cm"
                android:inputType="number"
                android:maxLines="5"
                android:textColorHint="#ccc"/>
        </LinearLayout>



        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/divider_line"/>



        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:text="宽
                android:textColor="#333"
                android:textSize="14sp"/>
                
            <MyEditText
                android:id="@+id/et_width"
                android:layout_width="match_parent"
                android:layout_height="wrap_content
                android:layout_marginLeft="10dp"
                android:background="@null"
                android:gravity="center"
                android:hint="0cm"
                android:inputType="number"
                android:maxLines="5"
                android:textColorHint="#ccc"/>
        </LinearLayout>
    </LinearLayout>



    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/divider_line"/>



    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="44dp"
        android:gravity="center_vertical"
        android:orientation="horizontal">



        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:orientation="horizontal">



            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="16dp"
                android:text="高"
                android:textColor="#333"
                android:textSize="14sp"/>



            <MyEditText
                android:id="@+id/et_height"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:background="@null"
                android:gravity="center"
                android:hint="0cm"
                android:inputType="number"
                android:maxLines="5"
                android:textColorHint="#ccc"/>
        </LinearLayout>



        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:background="@color/divider_line"/>



        <View
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="horizontal"/>

    </LinearLayout>
</LinearLayout>

然后在Activity实现该接口,并且调用back方法

@Override

 public void back(TextView textView) { 
       if (sizeDialog != null && sizeDialog.isShowing()) {
                sizeDialog.dismiss();
        }
 }
private void initViews(){
    MyEditText etLength = (MyEditText) sizeDialog.findViewById(R.id.et_length);
    etLength.setBackListener(this);
    MyEditText etWidth = (MyEditText) sizeDialog.findViewById(R.id.et_width);
    etWidth.setBackListener(this);
    MyEditText etHeight = (MyEditText) sizeDialog.findViewById(R.id.et_height);
    etHeight.setBackListener(this);
}

二、使用setOnKeyListener()方法来实现

如果不想新建一个EditText的子类,可以使用EditText的setOnKeyListener()方法,如:

editText.setOnKeyListener(new View.OnKeyListener() {
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if ((event.getAction() == KeyEvent.ACTION_UP) && (keyCode == KeyEvent.KEYCODE_BACK)) {
            Logger.d("editTextvalue:" + editText.getText());
            return true;
        }
        return false;
    }
});

该方式和onKeyPreIme的方式有点区别,EditText输入完数值后,按返回键,setOnKeyListener回调只会调用一次(action=ACTION_UP),而onKeyPreIme的回调会调用两次(action=ACTION_DOWN和action=ACTION_UP)。但是当用户继续按返回键关闭界面时,onKeyPreIme和setOnKeyListener的方式的回调方法都会调用两次(action=ACTION_DOWN和action=ACTION_UP)。

最后运行效果,按返回键,键盘和Dialog都消失了:

Android 监听onViewAttachedToWindow_android_02

三、监听View的OnGlobalLayoutListener方法来实现

以上两个方法都有一个问题:如果关闭键盘通过键盘上的按钮关闭的,不是通过手机的返回键关闭的,则无法监听到输入法的关闭。

可以通过实现ViewTreeObserver.OnGlobalLayoutListener接口来监听,当布局发生变动,来获取正在显示的窗口大小(getWindowVisibleDisplayFrame),如果超过了屏幕的百分之多少 ,来确定输入法状态(关闭、弹出

public class KeyboardLayout extends FrameLayout {

    private KeyboardLayoutListener mListener;
    private boolean mIsKeyboardActive = false; // 输入法是否激活
    private int mKeyboardHeight = 0; // 输入法高度

    public KeyboardLayout(Context context) {
        this(context, null, 0);
    }

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

    public KeyboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 通过视图树监听布局变化
        getViewTreeObserver().addOnGlobalLayoutListener(new KeyboardOnGlobalChangeListener());
    }

    private class KeyboardOnGlobalChangeListener implements ViewTreeObserver.OnGlobalLayoutListener {

        int mScreenHeight = 0;
        Rect mRect = new Rect();

        private int getScreenHeight() {
            if (mScreenHeight > 0) {
                return mScreenHeight;
            }
            mScreenHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay().getHeight();
            return mScreenHeight;
        }

        @Override
        public void onGlobalLayout() {
            // 获取当前页面窗口的显示范围
            getWindowVisibleDisplayFrame(mRect);

            int screenHeight = getScreenHeight(); //屏幕高度
            int keyboardHeight = screenHeight - mRect.bottom; // 输入法的高度
            boolean isActive = false;
            if (Math.abs(keyboardHeight) > screenHeight / 5) {
                isActive = true; // 超过屏幕五分之一则表示弹出了输入法
                mKeyboardHeight = keyboardHeight;
            }
            mIsKeyboardActive = isActive;
            if (mListener != null) {
                mListener.onKeyboardStateChanged(isActive, keyboardHeight);
            }
        }
    }

    public void setKeyboardListener(KeyboardLayoutListener listener) {
        mListener = listener;
    }

    public KeyboardLayoutListener getKeyboardListener() {
        return mListener;
    }

    public boolean isKeyboardActive() {
        return mIsKeyboardActive;
    }

    /**
     * 获取输入法高度
     *
     * @return
     */
    public int getKeyboardHeight() {
        return mKeyboardHeight;
    }

    public interface KeyboardLayoutListener {
        /**
         * @param isActive       输入法是否激活
         * @param keyboardHeight 输入法面板高度
         */
        void onKeyboardStateChanged(boolean isActive, int keyboardHeight);
    }

}

如果你觉得本文帮助到你,给我个关注和赞呗!

另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图

如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图:

Android 监听onViewAttachedToWindow_Text_03