攻克Android軟鍵盤的疑難雜癥

vdog5u0y 8年前發布 | 6K 次閱讀 安卓開發 Android開發 移動開發

在Activity中含有EditText時,我們常常在AndroidManifest.xml中為該Activity設置 Android :windowSoftInputMode屬性,其中最常用的值就是adjustResize和adjustPan。在此請思考幾個問題:

  1. adjustResize和adjustPan有什么區別?
  2. adjustResize和adjustPan的應用場景有何差異?
  3. 當設置android:windowSoftInputMode后如何監聽軟鍵盤的彈起與隱藏?

在看到第三個問題的時候,有人會想:

干嘛要去監聽軟鍵盤的彈起呢?有什么用呢?

嗯哼,看到了吧:當鍵盤彈起來的時候在緊靠鍵盤上方的地方出現了一個自定義布局,點擊笑臉就可以發送專屬emoji表情,點擊禮盒就可以發送福利。

當然,在鍵盤收起的時候這個布局也就不可見了。

除此以外,在其他不少場景也會有類似的UI設計。在這些情況下,我們都要監聽鍵盤軟鍵盤的彈起與隱藏。善良的童鞋會想:這個沒難度呀,調用一下官方的API就行。很久以前,我也是這么想的。可是,事與愿違,官方文檔中根本就沒有提供檢測軟鍵盤狀態的接口。

既然官方沒有把這個API洗干凈整整齊齊的擺在眼前,那我們就自己實現它。

adjustResize

在AndroidManifest.xml中為該Activity設置

android:windowSoftInputMode=”adjustResize”

該模式下系統會調整屏幕的大小以保證軟鍵盤的顯示空間。

舉個例子:

屏幕的高為1920px,那么整個Activity的布局高度也為1920px。當設置該屬性后點擊界面中的EditText,此時彈出軟鍵盤其高度為800px。為了完整地顯示此軟鍵盤,系統會調整Activity布局的高度為1920px-800px=1120px。

所以,此時的布局與原本的布局就發生了一些變化,比如:整體布局顯示不完整,控件外觀的變形,控件顯示位置的錯亂等等。這些現象都是因為原布局的高度變小所導致。

以下,再結合代碼詳細分析該情況。

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
/**
 * 原創作者:
 * 谷哥的小弟
 *
 * 博客地址:
 * http://blog.csdn.net/lfdfhl
 *
 */
public class RelativeLayoutSubClass extends RelativeLayout{
    private OnSoftKeyboardListener mSoftKeyboardListener;
    public RelativeLayoutSubClass(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        System.out.println("----> onMeasure");
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mSoftKeyboardListener.onSoftKeyboardChange();
        System.out.println("----> onLayout");
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        System.out.println("----> onSizeChanged");
    }

    public void setSoftKeyboardListener(OnSoftKeyboardListener listener){
        mSoftKeyboardListener=listener;
    }

    public interface OnSoftKeyboardListener{
        public void onSoftKeyboardChange();
    }

}

我們將該自定義RelativeLayout作為Activity布局的根

<?xml version="1.0" encoding="utf-8"?>
<cc.testsoftinputmode.RelativeLayoutSubClass
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="cc.testsoftinputmode.MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_alignParentTop="true"
        android:background="#7fb80e">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="淺綠色部分在屏幕頂部"
            android:textSize="25sp"
            android:layout_centerInParent="true"
            android:textColor="#843900"/>
    </RelativeLayout>

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="這里是一個輸入框"
        android:textSize="25sp"
        android:layout_centerInParent="true"
        android:textColor="#843900"/>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_alignParentBottom="true"
        android:background="#ffc20e">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="淺黃色部分在屏幕底部"
            android:textSize="25sp"
            android:layout_centerInParent="true"
            android:textColor="#f05b72"/>
    </RelativeLayout>

</cc.testsoftinputmode.RelativeLayoutSubClass>

效果如下:

點擊EditText,彈出軟鍵盤:

現象呢,我們已經看到了。我們再來瞅瞅當軟鍵盤狀態變化的時候RelativeLayoutSubClass中有哪些行為發生:

  1. 軟鍵盤狀態變化時會調用其onMeasure(),onLayout(),onSizeChanged()
  2. 在onSizeChanged()中可以確知軟鍵盤狀態變化前后布局寬高的數值

至此,發現一個關鍵點:onSizeChanged()

我們可以以此為切入點檢測軟鍵盤的狀態變化,所以定義一個接口OnSoftKeyboardListener提供給Activity使用,具體代碼如下:

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

/**
 * 原創作者:
 * 谷哥的小弟
 *
 * 博客地址:
 * http://blog.csdn.net/lfdfhl
 *
 */
public class MainActivity extends AppCompatActivity {
    private RelativeLayoutSubClass mRootLayout;
    private int screenHeight;
    private int screenWidth;
    private int threshold;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }
    private void init(){
        screenHeight=getResources().getDisplayMetrics().heightPixels;
        screenWidth=getResources().getDisplayMetrics().widthPixels;
        threshold=screenHeight/3;
        mRootLayout= (RelativeLayoutSubClass) findViewById(R.id.rootLayout);
        mRootLayout.setSoftKeyboardListener(new RelativeLayoutSubClass.OnSoftKeyboardListener() {
            @Override
            public void onSoftKeyboardChange(int w, int h, int oldw, int oldh) {
                if (oldh-h>threshold){
                    System.out.println("----> 軟鍵盤彈起");
                }else if(h-oldh>threshold){
                    System.out.println("----> 軟鍵盤收起");
                }
            }
        });
    }
}

請注意onSoftKeyboardChange()的回調:

  • 假若oldh-h大于屏幕高度的三分之一,則軟鍵盤彈起
  • 假若h-oldh大于屏幕高度的三分之一,則軟鍵盤收起

小結:

當軟鍵盤狀態發生改變時,通過對Activity布局文件根Layout的onSizeChanged()判斷軟鍵盤的彈起或隱藏

adjustPan

在AndroidManifest.xml中為該Activity設置

android:windowSoftInputMode=”adjustPan”

該模式下系統會將界面中的內容自動移動從而使得焦點不被鍵盤覆蓋,即用戶能總是看到輸入內容的部分

比如,還是剛才的那個布局,現在將其windowSoftInputMode設置為adjustPan再點擊EditText,效果如下:

嗯哼,看到了吧:

為了避免軟鍵盤彈起后遮擋EditText,系統將整個布局上移了,也就是我們常說的將布局頂上去了。

此時再來看看當軟鍵盤狀態變化的時候RelativeLayoutSubClass中有哪些行為發生:

  1. 軟鍵盤狀態變化時會調用其onMeasure(),onLayout()
  2. onSizeChanged()并沒有被調用
  3. 整個布局的高度也沒有變化

哦噢,這時并沒有執行onSizeChanged()方法,這也就說原本檢測軟鍵盤狀態的方法在這就行不通了,得另辟蹊徑了。

當軟鍵盤彈起時,原布局中是不是有一部分被鍵盤完全遮擋了呢?

對吧,也就是說原布局的可視范圍(更精確地說是可視高度)發生了變化:變得比以前小了。所以,我們可以以此為突破口,具體代碼如下:

public boolean isSoftKeyboardShow(View rootView) {
        screenHeight=getResources().getDisplayMetrics().heightPixels;
        screenWidth=getResources().getDisplayMetrics().widthPixels;
        threshold=screenHeight/3;
        int rootViewBottom=rootView.getBottom();
        Rect rect = new Rect();
        rootView.getWindowVisibleDisplayFrame(rect);
        int visibleBottom=rect.bottom;
        int heightDiff = rootViewBottom - visibleBottom;
        System.out.println("----> rootViewBottom="+rootViewBottom+",visibleBottom="+visibleBottom);
        System.out.println("----> heightDiff="+heightDiff+",threshold="+threshold);
        return heightDiff > threshold;
    }
  1. 獲取根布局(RelativeLayoutSubClass)原本的高度
    int rootViewBottom=rootView.getBottom();

     

  2. 獲取當前根布局的可視高度
    Rect rect = new Rect();
    
    rootView.getWindowVisibleDisplayFrame(rect);
    
    int visibleBottom=rect.bottom;

     

  3. 計算兩者的差值
    int heightDiff = rootViewBottom – visibleBottom;

     

  4. 判斷軟鍵盤是否彈起
    return heightDiff > threshold;

     

具體的方法是有了,那么該在哪里調用該方法呢?

其實,和之前的類似,只需在RelativeLayoutSubClass的onLayout()中執行即可。具體代碼如下:

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
/**
 * 原創作者:
 * 谷哥的小弟
 *
 * 博客地址:
 * http://blog.csdn.net/lfdfhl
 *
 */
public class RelativeLayoutSubClass extends RelativeLayout{
    private OnSoftKeyboardListener mSoftKeyboardListener;
    public RelativeLayoutSubClass(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        System.out.println("----> onMeasure");
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mSoftKeyboardListener.onSoftKeyboardChange();
        System.out.println("----> onLayout");
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        System.out.println("----> onSizeChanged");
    }

    public void setSoftKeyboardListener(OnSoftKeyboardListener listener){
        mSoftKeyboardListener=listener;
    }

    public interface OnSoftKeyboardListener{
        public void onSoftKeyboardChange();
    }

}

在對應的Activity中實現該Listener,具體代碼如下:

import android.graphics.Rect;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
/**
 * 原創作者:
 * 谷哥的小弟
 *
 * 博客地址:
 * http://blog.csdn.net/lfdfhl
 *
 */
public class MainActivity extends AppCompatActivity{
    private RelativeLayoutSubClass mRootLayout;
    private int screenHeight;
    private int screenWidth;
    private int threshold;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init(){
        mRootLayout= (RelativeLayoutSubClass) findViewById(R.id.rootLayout);
        mRootLayout.setSoftKeyboardListener(new RelativeLayoutSubClass.OnSoftKeyboardListener() {
            @Override
            public void onSoftKeyboardChange() {
                boolean isShow=isSoftKeyboardShow(mRootLayout);
                System.out.println("----> isShow="+isShow);
            }
        });
    }

    public boolean isSoftKeyboardShow(View rootView) {
        screenHeight=getResources().getDisplayMetrics().heightPixels;
        screenWidth=getResources().getDisplayMetrics().widthPixels;
        threshold=screenHeight/3;
        int rootViewBottom=rootView.getBottom();
        Rect rect = new Rect();
        rootView.getWindowVisibleDisplayFrame(rect);
        int visibleBottom=rect.bottom;
        int heightDiff = rootViewBottom - visibleBottom;
        System.out.println("----> rootViewBottom="+rootViewBottom+",visibleBottom="+visibleBottom);
        System.out.println("----> heightDiff="+heightDiff+",threshold="+threshold);
        return heightDiff > threshold;
    }

}

嗯哼,至此當windowSoftInputMode設置為adjustPan時軟鍵盤的狀態監聽也得到了實現

 

來自:http://www.androidchina.net/5530.html

 

 本文由用戶 vdog5u0y 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!