安卓中关于软键盘的一些 研究和经验

来源:互联网 发布:凯聪智云软件pc版 编辑:程序博客网 时间:2024/05/24 01:39

最近做了一个项目 里面有几处对软键盘的 处理 ,自己从网上和其他地方查的资料和自己亲自研究认证,做了一点总结 希望对有些人有些作用


Android是一个针对触摸屏专门设计的操作系统,当点击编辑框,系统自动为用户弹出软键盘,以便用户进行输入。那么,弹出软键盘后必然会造成原有布局高度的减少,那么系统应该如何来处理布局的减少?我们能否在应用程序中进行自定义的控制?这些是本文要讨论的重点。

一、软键盘显示的原理     
软件盘的本质是什么?软键盘其实是一个Dialog!InputMethodService为我们的输入法创建了一个Dialog,并且将该Dialog的Window的某些参数(如Gravity)进行了设置,使之能够在底部或者全屏显示。当我们点击输入框时,系统对活动主窗口进行调整,从而为输入法腾出相应的空间,然后将该Dialog显示在底部,或者全屏显示。  

二、活动主窗口调整     
android定义了一个属性,名字为windowSoftInputMode, 用它可以让程序可以控制活动主窗口调整的方式。我们可以在AndroidManifet.xml中对Activity进行设置。如:android:windowSoftInputMode="stateUnchanged|adjustPan" 。该属性可选的值有两部分,一部分为软键盘的状态控制,另一部分是活动主窗口的调整。前一部分本文不做讨论,请读者自行查阅android文档。     
模式一,压缩模式:
在AndroidManifest.xml的Activity设置属性:android:windowSoftInputMode = "adjustResize"
自定一个布局继承LinearLayout,作为Activity的根布局,覆盖整个屏幕。通过log可以发现,当软键盘启用时,根布局调用了onMeasure,onSizeChanged和onLayout方法。实际上,当设置为adjustResize后,软键盘弹出时,要对主窗口布局重新进行measure和layout,而在layout时,发现窗口的大小发生的变化,因此调用了onSizeChanged。此种模式会压缩根布局的大小,腾出空间用于显示软键盘。
模式二,平移模式:
在AndroidManifest.xml的Activity属性进行更改:android: windowSoftInputMode = "adjustPan"
windowSoftInputMode的值如果设置为adjustPan,那么该Activity主窗口并不调整屏幕的大小以便留出软键盘的空间。相反,当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分。这个通常是不期望比调整大小,因为用户可能关闭软键盘以便获得与被覆盖内容的交互操作。通过log可以发现,当软键盘启用时,根布局调用了onMeasure和onLayout方法,并没有调用onSizeChanged方法,这说明输入法弹出前后并没有改变原有布局的大小。事实上,当输入框不会被遮挡时,该模式没有对布局进行调整,然而当输入框将要被遮挡时,窗口就会进行平移。也就是说,该模式始终是保持输入框为可见。
模式三,自动模式:
当属性windowSoftInputMode被设置为adjustUspecified时,它不被指定为该Activity主窗口调整大小以便留出软键盘的空间,也不指定窗口上的焦点为可见的。系统将自动选择这些模式中一种,选择主要依赖于窗口的内容是否有任何布局视图能够滚动它们的内容。如果有这样的一个视图,这个窗口将调整大小,压缩这个能够滚动的视图在一个较小的区域中可见的。这个是主窗口默认的行为设置。也就是说,系统自动决定是采用平移模式还是压缩模式,决定因素在于内容是否可以滚动。

三、侦听软键盘的显示隐藏
android目前尚未提供监听软键盘的显示与隐藏的API,只提供了获取当前键盘状态的API。但是我们发现,无论哪种模式,当键盘显示和隐藏时候,onMeasure和onLayout都会执行,于是我们可以重写自己的根布局元素,以达到监听键盘状态的效果。
public class KeyboardListenRelativeLayout extends RelativeLayout {
    private static final String TAG = KeyboardListenRelativeLayout.class.getSimpleName();
    public static final byte KEYBOARD_STATE_SHOW = -3;
    public static final byte KEYBOARD_STATE_HIDE = -2;
    public static final byte KEYBOARD_STATE_INIT = -1;
    private boolean mHasInit = false;
    private boolean mHasKeyboard = false;
    private int mHeight;
    private IOnKeyboardStateChangedListener onKeyboardStateChangedListener;
    public KeyboardListenRelativeLayout(Context context) {
        super(context);
    }
    public KeyboardListenRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public KeyboardListenRelativeLayout(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
    }
    public void setOnKeyboardStateChangedListener(
            IOnKeyboardStateChangedListener onKeyboardStateChangedListener) {
        this.onKeyboardStateChangedListener = onKeyboardStateChangedListener;
    }
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (!mHasInit) {
            mHasInit = true;
            mHeight = b;
            if (onKeyboardStateChangedListener != null) {
                onKeyboardStateChangedListener
                        .onKeyboardStateChanged(KEYBOARD_STATE_INIT);
            }
        } else {
            mHeight = mHeight < b ? b : mHeight;
        }
        if (mHasInit && mHeight > b) {
            mHasKeyboard = true;
            if (onKeyboardStateChangedListener != null) {
                onKeyboardStateChangedListener
                        .onKeyboardStateChanged(KEYBOARD_STATE_SHOW);
            }
        }
        if (mHasInit && mHasKeyboard && mHeight == b) {
            mHasKeyboard = false;
            if (onKeyboardStateChangedListener != null) {
                onKeyboardStateChangedListener
                        .onKeyboardStateChanged(KEYBOARD_STATE_HIDE);
            }
        }
    }
    public interface IOnKeyboardStateChangedListener {
        public void onKeyboardStateChanged(int state);
    }
}
选择onLayout方法(因为onMeasure方法在布局调整中会调用多次),根据判断高度来辨别键盘是否显示,以达到监听的效果,但此种方法只适用于压缩模式。
 
设想:我们可以在onLayout方法中通过以下方法获取键盘的显示状态,但目前尚未找到获取键盘显示状态的方法。

四、显示或隐藏软键盘
private void hideInputMethodManagerKeyStore() {
    InputMethodManager imm = (InputMethodManager) this.getSystemService(INPUT_METHOD_SERVICE);
    if (imm != null) {
        View tmpView = this.getCurrentFocus();
        if (tmpView != null) {
            IBinder ib = tmpView.getWindowToken();//获取View的标记
            if (ib != null)
                try {
                    imm.hideSoftInputFromWindow(ib, InputMethodManager.HIDE_NOT_ALWAYS);//需要一个标记
                } catch (RuntimeException e) {}
        }
    }
}
private void showInputMethodManagerKeyStore(View view) {
    InputMethodManager imm = (InputMethodManager) this.getSystemService(INPUT_METHOD_SERVICE);
    if (imm != null && view != null) {
        try {
            imm.showSoftInput(view, InputMethodManager.RESULT_UNCHANGED_SHOWN);//需要一个view对象以便向该view传递键盘输入的类容
        } catch (RuntimeException e) {}
    }
}
启动键盘的view一般为获取焦点的view,当我们无法获取到该view时,可以直接获取该窗口的最顶层view,利用该view来隐藏键盘:
final View v = getWindow().peekDecorView(); 
if (v != null && v.getWindowToken() != null) {   
    InputMethodManager imm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);   
    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);   
}

五、让EditText显示时不弹出键盘但能获得光标
1、在清单文件中设置Activity的属性:
android:configChanges="keyboardHidden|orientation"
android:windowSoftInputMode="stateHidden|stateAlwaysHidden" 
2、在清单文件中设置Activity的属性:
在该Activity的界面初始化时,再次调用InputMethodManager进行软键盘的

0 0
原创粉丝点击