您的位置:首页 > 移动开发 > Android开发

Android 检测软键盘的弹起与隐藏

2013-06-27 14:49 323 查看
转自:http://www.eoeandroid.com/thread-157446-1-1.html

   http://blog.csdn.net/caesardadi/article/details/8252829

Android是一个针对触摸屏专门设计的操作系统,当点击编辑框,系统自动为用户弹出软键盘,以便用户进行输入。

    那么,弹出软键盘后必然会造成原有布局高度的减少,那么系统应该如何来处理布局的减少?我们能否在应用程序中进行自定义的控制?这些是本文要讨论的重点。

一、软键盘显示的原理

    软件盘的本质是什么?软键盘其实是一个Dialog!

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

二、活动主窗口调整

    android定义了一个属性,名字为windowSoftInputMode, 用它可以让程序可以控制活动主窗口调整的方式。我们可以在AndroidManifet.xml中对Activity进行设置。如:android:windowSoftInputMode="stateUnchanged|adjustPan"

    该属性可选的值有两部分,一部分为软键盘的状态控制,另一部分是活动主窗口的调整。前一部分本文不做讨论,请读者自行查阅android文档。

    

模式一,压缩模式

    windowSoftInputMode的值如果设置为adjustResize,那么该Activity主窗口总是被调整大小以便留出软键盘的空间。

我们通过一段代码来测试一下,当我们设置了该属性后,弹出输入法时,系统做了什么。

工程目录如下图



重写Layout布局

package com.example.android_imetest;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;

public class KeyboardLayout extends LinearLayout {

private static int count = 0;

public KeyboardLayout(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

public KeyboardLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {

Log.e("onLayout " + count++, "=>OnLayout called! l=" + l + ", t=" + t + ",r=" + r + ",b="+b);

super.onLayout(changed, l, t, r, b);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

Log.e("onMeasure " + count++, "=>onMeasure called! widthMeasureSpec=" + widthMeasureSpec + ", heightMeasureSpec=" + heightMeasureSpec);

super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {

Log.e("onSizeChanged " + count++, "=>onResize called! w="+w + ",h="+h+",oldw="+oldw+",oldh="+oldh);

super.onSizeChanged(w, h, oldw, oldh);
}

}


布局文件

<com.example.android_imetest.KeyboardLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<EditText
android:id="@+id/et_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:gravity="center"
android:layout_marginTop="5dp"/>
</LinearLayout>

</com.example.android_imetest.KeyboardLayout>


在AndroidManifest.xml的MainActivity 设置属性:android:windowSoftInputMode = "adjustResize"

运行程序,点击文本框,查看调试信息:

06-27 14:32:35.855: E/onMeasure 19(14186): =>onMeasure called! widthMeasureSpec=1073742464, heightMeasureSpec=1073742642

06-27 14:32:35.871: E/onMeasure 20(14186): =>onMeasure called! widthMeasureSpec=1073742464, heightMeasureSpec=1073742110

06-27 14:32:35.875: E/onSizeChanged 21(14186): =>onResize called! w=640,h=286,oldw=640,oldh=818

06-27 14:32:35.875: E/onLayout 22(14186): =>OnLayout called! l=0, t=0,r=640,b=286

    

    从调试结果我们可以看出,当我们点击文本框后,根布局调用了onMeasure,onSizeChanged和onLayout。

    实际上,当设置为adjustResize后,软键盘弹出时,要对主窗口布局重新进行measure和layout,而在layout时,发现窗口的大小发生的变化,因此调用了onSizeChanged。

    从下图的运行结果我们也可以看出,原本在下方的TextView被顶到了输入法的上方。

模式二,平移模式

    windowSoftInputMode的值如果设置为adjustPan,那么该Activity主窗口并不调整屏幕的大小以便留出软键盘的空间。相反,当前窗口的内容将自动移动以便当前焦点从不被键盘覆盖和用户能总是看到输入内容的部分。这个通常是不期望比调整大小,因为用户可能关闭软键盘以便获得与被覆盖内容的交互操作。

    上面的例子中,我们将AndroidManifest.xml的MainActivity  属性进行更改:android: windowSoftInputMode = "adjustPan"

重新运行,并点击文本框,查看调试信息:

06-27 14:35:48.644: E/onMeasure 9(14850): =>onMeasure called! widthMeasureSpec=1073742464, heightMeasureSpec=1073742642

06-27 14:35:48.648: E/onLayout 10(14850): =>OnLayout called! l=0, t=0,r=640,b=818

    

我们看到:系统也重新进行了measrue和layout,但是我们发现,layout过程中onSizeChanged并没有调用,这说明输入法弹出前后并没有改变原有布局的大小。

    从下图的运行结果我们可以看到,下方的TextView并没有被顶到输入法上方。

    事实上,当输入框不会被遮挡时,该模式没有对布局进行调整,然而当输入框将要被遮挡时,窗口就会进行平移。也就是说,该模式始终是保持输入框为可见。如下图,整个窗口,包括标题栏均被上移,以保证文本框可见。

模式三 自动模式

    当属性windowSoftInputMode被设置为adjustUspecified时,它不被指定是否该Activity主窗口调整大小以便留出软键盘的空间,或是否窗口上的内容得到屏幕上当前的焦点是可见的。系统将自动选择这些模式中一种主要依赖于是否窗口的内容有任何布局视图能够滚动他们的内容。如果有这样的一个视图,这个窗口将调整大小,这样的假设可以使滚动窗口的内容在一个较小的区域中可见的。这个是主窗口默认的行为设置。

    也就是说,系统自动决定是采用平移模式还是压缩模式,决定因素在于内容是否可以滚动。

三、侦听软键盘的显示隐藏

有时候,借助系统本身的机制来实现主窗口的调整并非我们想要的结果,我们可能希望在软键盘显示隐藏的时候,手动的对布局进行修改,以便使软键盘弹出时更加美观。这时就需要对软键盘的显示隐藏进行侦听。

我们可以重写根布局,因为根布局的高度一般情况下是不发生变化的。

假设跟布局为相对布局,我们写一个例子,当输入法弹出时隐藏某个view,输入法隐藏时显示某个view。

自定义根布局

package com.example.softkeyboard.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.RelativeLayout;

public class KeyboardLayout extends RelativeLayout {
private static final String TAG = KeyboardLayout.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;
private boolean mHasKeybord;
private int mHeight;
private onKybdsChangeListener mListener;

public KeyboardLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

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

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

/**
* set keyboard state listener
*/
public void setOnkbdStateListener(onKybdsChangeListener listener) {
mListener = listener;
}

@Override
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 (mListener != null) {
mListener.onKeyBoardStateChange(KEYBOARD_STATE_INIT);
}
} else {
mHeight = mHeight < b ? b : mHeight;
}
if (mHasInit && mHeight > b) {
mHasKeybord = true;
if (mListener != null) {
mListener.onKeyBoardStateChange(KEYBOARD_STATE_SHOW);
}
Log.w(TAG, "show keyboard.......");
}
if (mHasInit && mHasKeybord && mHeight == b) {
mHasKeybord = false;
if (mListener != null) {
mListener.onKeyBoardStateChange(KEYBOARD_STATE_HIDE);
}
Log.w(TAG, "hide keyboard.......");
}
}

public interface onKybdsChangeListener {
public void onKeyBoardStateChange(int state);
}
}


布局文件

<?xml version="1.0" encoding="utf-8"?>
<com.example.softkeyboard.view.KeyboardLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/keyboardLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:fillViewport="true" >

<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
 
<TextView
android:id="@+id/mTextView"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1.0"
android:background="#000000"
android:gravity="center"
android:text="@string/tv_softkeyboard"
android:textColor="#00ff00"
android:textStyle="bold" />
 
<EditText
android:id="@+id/mEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</EditText>
        
</LinearLayout>
   
</ScrollView>

</com.example.softkeyboard.view.KeyboardLayout>


MainActivity 中的代码逻辑

package com.example.softkeyboard;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.example.softkeyboard.view.KeyboardLayout;
import com.example.softkeyboard.view.KeyboardLayout.onKybdsChangeListener;

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
KeyboardLayout mainView = (KeyboardLayout) findViewById(R.id.keyboardLayout);
final TextView tv = (TextView) findViewById(R.id.mTextView);

mainView.setOnkbdStateListener(new onKybdsChangeListener() {
public void onKeyBoardStateChange(int state) {
switch (state) {
case KeyboardLayout.KEYBOARD_STATE_HIDE:
tv.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), "软键盘隐藏",
Toast.LENGTH_SHORT).show();
break;
case KeyboardLayout.KEYBOARD_STATE_SHOW:
tv.setVisibility(View.INVISIBLE);
Toast.makeText(getApplicationContext(), "软键盘弹起",
Toast.LENGTH_SHORT).show();
break;
}
}
});
}

@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;
}

}


这样我们就可以监听软键盘的弹起与隐藏状态了,如果想要隐藏掉 软键盘的话,可以调用下面的一段代码

public void hideSoftKeyBoard(){
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(windowToken, flags);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息