android 软键盘的显示与隐藏问题的研究

别裁伪体亲风雅,转益多师是汝师。这篇文章主要讲述android 软键盘的显示与隐藏问题的研究相关的知识,希望能为你提供帮助。
在android中,常常会和输入法的软件键盘交互。在Manifest文件中,系统给activity的一个属性-windowSoftInputMode来控制输入法的显示方式。
该属性提供了Activity的window与软键盘的window交互的方式。这里的属性设置有双方面的影响:
1.软键盘的显示与隐藏。-当Activity界面成为用户的焦点时,或隐藏或显示。
2。
对Activty的主window窗体进行调整。或者将Activity的window窗体调小以便为软键盘腾出空间,或者当Activity的部分window被软件盖住时,移动Activity的内容以便用户可以看到当前的焦点。

你至少要从下列属性值 选取一项对其设置,要么是”state...“,要么是”adjust...“。
你能够设置多个值 。设置不同的属性值 。要用|进行分开。
比方:
< activityandroid:windowSoftInputMode=" stateVisible|adjustResize" . . . >
以下是对属性的值 的描写叙述。

描写叙述
”stateUnSpecified“ 不指定软件的状态(显示或隐藏)。系统会依据主题中的设置来选择对应的状态。 该属性软键盘的默认设置。
”stateUnchnaged“ 总是保持上次软键盘的状态。当Activity进入到最前端时,不论是它上次它是显示或隐藏。保持不变。
”stateHidden“ 当用户进入目标Activity时。软键盘保持隐藏状态。这里的Activity是用户是向前进入Activity,而不是因为退出其他Activity退回到目标Activity。
”stateVisible“ 仅仅有条件合适(当用户前进进入到Activity的主window),就会显示键盘
”stateAlawaysVisible“ 当用户选择进入目标Activity时。软键盘被设置为可见的。这里的Activity是用户向前进入的Activity,而不是因为退出其他Activity而回到目标Activity
" adjustUnspecified" 不指定是否去调整Activity的界面。或者调整Activity窗体的大小以便为软键盘腾出空间或者移动窗体的内容来屏幕上当前的焦点可见。系统会自己主动选择当中一种模式,这依赖于窗体是包括能够滑动其内容的view.如有这种视图,窗体的大小就会被调整。在这种假定的情况下,非常小的滑动就能够使用窗体的内容可见。
该属性是主windowr默认设置。
”adjustResize“ Activity的窗体总是被调整其大小以便为软键盘腾出空间。
”adjustPan“ Activity的主窗体不会被调整其大小以便为软键盘腾出空间。相反,窗体的内容会被自己主动移动以便当前的焦点不会被软键盘遮住,用户能够总是看到他输入的内容。
这个值 一般用于用户非常少想调整窗体的大小的情况下。由于用户可能须要关闭软键盘来与窗体的其他部分进行交互。

从上面系统的描写叙述了解了系统在处理两个交互时都显示Activity和软键盘都是看作window窗体来处理的。
非常多的时候,我们都希望可以监听到软件键盘的显示与关闭状态。比方下图的情况,
你有一个登录界面是这样设计的
android 软键盘的显示与隐藏问题的研究

文章图片


你希望登录时它是这种,
android 软键盘的显示与隐藏问题的研究

文章图片


然后你去尝试给activity的windowSoftInputMode属性加上值 ,当你加上  adjustResize时。它是这种
android 软键盘的显示与隐藏问题的研究

文章图片


你不甘心。你将adjustResize改动成adjustPan,结果是这种
android 软键盘的显示与隐藏问题的研究

文章图片



没有办法。仅仅有靠我们自己来监听键盘的显示或隐藏来动态改变布局。如今的问题是怎么监听到键盘的动态改变呢。系统并没有提供对应的方法。我们能够通过两种方法来实现。

1.在adjustResize属性下。activity的窗体大小会发生改变,而窗体中的layout的大小也必定后会发生改变。
当view大小发生改变时,必定会引起onSizeChanged(int, int, int, int)的回调。所以可能自己定义一个Layout。来监測onSizeChanged()函数的回调。然后在当中的作对应的处理。
这种方法也是网上传的最多的方法。

2.借助ViewTreeOberserver类。ViewTreeOberserver能够用来注冊一个监听器。它能监听view树的全局变化。
这些变化包含但不限于,整个View的布局,绘制传递的源头,触摸模式的变化。

第一种方法的实现:
自己定义一个Layout。以RelativeLayout为例,
能够在onSizeChanged(int w, int h,int oldw,int oldh)  方法里监听界面大小的变化,然后在该方法里定义回调函数。当监听到键盘被调出时,将键盘向上推一段距离,这样将登录键显示出来。

package com.example.keyboardlistener; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.widget.RelativeLayout; public class KeyboardLayout extends RelativeLayout {private onSizeChangedListener mChangedListener; private static final String TAG =" KeyboardLayoutTAG" ; private boolean mShowKeyboard = false; public KeyboardLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub }public KeyboardLayout(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub }public KeyboardLayout(Context context) { super(context); // TODO Auto-generated constructor stub }@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); Log.d(TAG, " onMeasure-----------" ); }@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, " onLayout-------------------" ); }@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { // TODO Auto-generated method stub super.onSizeChanged(w, h, oldw, oldh); Log.d(TAG, " --------------------------------------------------------------" ); Log.d(TAG, " w----" + w + " \n" + " h-----" + h + " \n" + " oldW-----" + oldw + " \noldh----" + oldh); if (null != mChangedListener & & 0 != oldw & & 0 != oldh) { if (h < oldh) { mShowKeyboard = true; } else { mShowKeyboard = false; } mChangedListener.onChanged(mShowKeyboard); Log.d(TAG, " mShowKeyboard-----" + mShowKeyboard); } }public void setOnSizeChangedListener(onSizeChangedListener listener) { mChangedListener = listener; }interface onSizeChangedListener{void onChanged(boolean showKeyboard); }}


在主Activity里创建一个Handler。当监听到布局发生变化时,通过Handler更新UI。

package com.example.keyboardlistener; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.ViewTreeObserver.OnPreDrawListener; import android.widget.Button; import com.example.keyboardlistener.KeyboardLayout.onSizeChangedListener; public class MainActivity extends Activity {private static final String TAG = " KeyboardLayoutTAG" ; private KeyboardLayout mRoot; private Button mLogin; private int mLoginBottom; private static final int KEYBOARD_SHOW = 0X10; private static final int KEYBOARD_HIDE = 0X20; private boolean mGetBottom = true; private Handler mHandler = new Handler() {@Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); switch (msg.what) { case KEYBOARD_HIDE: mRoot.setPadding(0, 0, 0, 0); break; case KEYBOARD_SHOW: int mRootBottom = mRoot.getBottom(); Log.d(TAG, " the mLoginBottom is" + mLoginBottom); mRoot.setPadding(0, mRootBottom - mLoginBottom, 0, 0); break; default: break; } }}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getActionBar().hide(); mRoot = (KeyboardLayout) findViewById(R.id.root_view); mLogin = (Button) findViewById(R.id.login); mRoot.setOnSizeChangedListener(new onSizeChangedListener() {@Override public void onChanged(boolean showKeyboard) { // TODO Auto-generated method stub if (showKeyboard) { mHandler.sendMessage(mHandler.obtainMessage(KEYBOARD_SHOW)); Log.d(TAG, " show keyboard" ); } else { mHandler.sendMessage(mHandler.obtainMessage(KEYBOARD_HIDE)); } } }); mRoot.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {@Override public boolean onPreDraw() { // TODO Auto-generated method stub if (mGetBottom) { mLoginBottom = mLogin.getBottom(); //获取登录button的位置信息。
} mGetBottom = false; return true; } }); }//@Override //public boolean onCreateOptionsMenu(Menu menu) { //// Inflate the menu; this adds items to the action bar if it is present. //getMenuInflater().inflate(R.menu.main, menu); //return true; //}}


另外一种方法的实现:
借助ViewTreeObserver类对你想监听的view加入OnGlobalLayoutListener监听器。然后在onGlobalLayout()方法里窗体的变化情况来推断键盘是否被调出来了。以下是ViewTreeObserver监听的部分的代码:

mRoot.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {@Override public void onGlobalLayout() { int offset = mRoot.getRootView().getHeight() - mRoot.getHeight(); //依据视图的偏移值来推断键盘是否显示 if (offset > 300) { mHandler.sendMessage(mHandler.obtainMessage(KEYBOARD_SHOW)); } else { mHandler.sendMessage(mHandler.obtainMessage(KEYBOARD_HIDE)); }} });


当中Handler的详细实现同上述结构中的Handler一样。



參考文章:InputMethod


样例源代码下载地址

【android 软键盘的显示与隐藏问题的研究】
































    推荐阅读