1. 程式人生 > >Android軟鍵盤遮擋問題解決

Android軟鍵盤遮擋問題解決

在開發登入介面時,在點選某個EditText準備輸入時,彈出的軟鍵盤遮擋了按鈕或者下面的輸入框,在完成這個文字框的輸入後,想要繼續下面的操作,需要先隱藏軟鍵盤。這會影響使用者操作的流暢感,所以需要解決。在嘗試了網上的幾種處理方法後,最終選擇了一種比較滿意的方式。

下面先給一個圖,來講解下問題所在:

這裡寫圖片描述

這個圖中有四個場景:
第一個場景,是沒有彈出軟鍵盤的登入介面,可以看出所有view都正常展示出來了;
第二個場景,彈出了軟鍵盤,此時,下面的密碼輸入框、登入按鈕都被遮擋了;
第三個場景,彈出了軟鍵盤,輸入了一個字母,此時,軟鍵盤又增長了一部分,但是使用者名稱輸入框也上移了,並沒有被遮蓋。不同的手機的軟鍵盤不同,我是在華為手機上遇到這種情況,在解決遮擋問題時也要考慮這個場景;
第四個場景,是我們的目標,彈出了軟鍵盤,原來的檢視整體上移了,顯示出來我們要操作的view。

下面說下解決軟鍵盤遮蓋問題的過程:

首先,是希望Android系統有自帶的屬性,一設定就搞定問題的。

搜尋了下,都是說使用WindowSoftInputMode屬性,設定為”adjustPan|stateHidden”或者”adjustResize|stateHidden”,經過測試,這種設定對我的佈局沒有效果,基本與不設定的預設效果相同。軟鍵盤正好在編輯的EditText的下面,會遮擋登入按鈕。

第二種方法,是使用ScrollView:

在整體佈局的外面加一個ScrollView,在文字框的onTouch監聽動作中,滾動整個檢視。
實測問題:
1,監聽到onTouch動作後,進行滾動整個檢視操作,需要延時才有效。
原因:onTouch時,軟鍵盤還沒有彈出,此時滾動到底部與不滾動是一樣的效果。需要等軟鍵盤彈出後,介面中的內容超過了一螢幕,此時的滾動才有效。
2,這個延時時間是多少?
不同手機的延時時間不一致,好的手機100ms就可以了,慢的手機延時500ms也不一定每次都有效果。
3,延時帶來的問題:
若延時超過300ms,人眼就能感覺出來了:先是軟鍵盤彈出,然後才是檢視滾動。使用者會感覺有些奇怪:為什麼要動作兩次呢?
這種方案,可以湊合使用了,可是,我們要做優雅的開發者,繼續尋找!

第三種方法:監聽佈局變化;

既然使用延時不是一個優雅的方案,那麼,滾動檢視的最好的時間,當然是在佈局變化的時刻。
這裡,可以使用OnGlobalLayoutListener來實現。我們來學習下:
OnGlobalLayoutListener 是ViewTreeObserver的內部類,當一個檢視樹的佈局發生改變時,可以被ViewTreeObserver監聽到,這是一個註冊監聽檢視樹的觀察者(observer),在檢視樹的全域性事件改變時得到通知。
總結為簡單一句話:使用OnGlobalLayoutListener可以監聽到佈局的變化。
監聽到佈局變化後,我們就可以自由的操作了:
可以外包一個ScrollView來進行滾動,使用一個

scrollView.fullScroll(ScrollView.FOCUS_DOWN)

來滾動到底部。
不過,不使用ScrollView也能進行滾動的,例如LinearLayout也是可以滾動的,你還不知道吧,其實,我也是才知道:最基礎的View就有個ScrollTo()函式的。
好了,基礎知識準備好了,剩下就是滾動的距離計算了,我們這樣來計算:
通過窗體的根View求出總的區域和可視區域,這樣就可以計算出被遮擋的區域的高度,如果超過一定的值就判斷為軟鍵盤彈出了,然後將根View ScrollTo到一個位置,將被遮擋的View展示出來。
這裡還有個要注意的地方,就是ScrollTo的引數,先看看函式原型:

public void scrollTo(int x, int y)

兩個引數x、y,是要滾動到位置的座標,注意,它們是絕對座標。
而我們計算滾動距離的時候,是計算的相對滾動距離。還記得上面的場景2與場景3麼,點選輸入框,滾動檢視,進入場景2,然後點選一個字母,進入場景3,此時,就是一個比較小的相對滾動距離。後面,我們在程式碼中也有相應註釋,要注意理解下哦。

下面是貼程式碼的時間了,先展示我的佈局xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_loginView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FAFAFA"
    android:orientation="vertical" >

    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="350dp"
        android:background="#8fE095"
        android:scaleType="centerInside"
        android:src="@drawable/logo" />

    <View
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:focusable="true"
        android:focusableInTouchMode="true" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="300dp"
        android:orientation="vertical" >

        <EditText
            android:id="@+id/userName"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:hint="請輸入使用者名稱"
            android:inputType="text"
            android:maxLength="18"
            android:paddingBottom="10dip"
            android:singleLine="true"
            android:textColor="#808080"
            android:text=""
            android:textSize="18sp" />

        <EditText
            android:id="@+id/userPwd"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:hint="請輸入密碼"
            android:inputType="numberPassword"
            android:maxLength="20"
            android:paddingBottom="10dip"
            android:singleLine="true"
            android:textColor="#808080"
            android:text=""
            android:textSize="18sp" >
        </EditText>

        <LinearLayout
            android:id="@+id/layout03"
            android:layout_width="fill_parent"   
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp" 
            android:orientation="horizontal"
            >

            <CheckBox
                android:id="@+id/login_check"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" 
                android:checked="true"
                />

            <TextView
                android:id="@+id/rememberPwd"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="記住使用者名稱"
                android:textColor="#808080"
                android:textSize="15sp" />
        </LinearLayout>

        <Button
            android:id="@+id/btn_login"
            android:layout_width="300dp"
            android:layout_height="50dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="40dp"
            android:background="@drawable/login_buton"
            android:gravity="center"
            android:text="立 即 登 錄"
            android:textColor="@android:color/background_light" />

    </LinearLayout>

</LinearLayout>

然後是主Activity:

package com.example.loginTest;

import android.app.Activity;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.Button;
import android.widget.LinearLayout;
import com.example.loginTest.R;

public class MainActivity extends Activity {

    private Button btn_login;
    private LinearLayout ll_loginView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        btn_login = (Button) findViewById(R.id.btn_login);
        ll_loginView = (LinearLayout) findViewById(R.id.ll_loginView );

        autoScrollView(ll_loginView, btn_login);//彈出軟鍵盤時滾動檢視        

    }   

    /**
    * @param root 最外層的View
    * @param scrollToView 不想被遮擋的View,會移動到這個Veiw的可見位置
    */
    private int scrollToPosition=0;
    private void autoScrollView(final View root, final View scrollToView) {
       root.getViewTreeObserver().addOnGlobalLayoutListener(
               new ViewTreeObserver.OnGlobalLayoutListener() {
           @Override
           public void onGlobalLayout() {

               Rect rect = new Rect();

               //獲取root在窗體的可視區域
               root.getWindowVisibleDisplayFrame(rect);

               //獲取root在窗體的不可視區域高度(被遮擋的高度)
               int rootInvisibleHeight = root.getRootView().getHeight() - rect.bottom;

               //若不可視區域高度大於150,則鍵盤顯示
               if (rootInvisibleHeight > 150) {

                 //獲取scrollToView在窗體的座標,location[0]為x座標,location[1]為y座標
                   int[] location = new int[2];                   
                   scrollToView.getLocationInWindow(location);

                   //計算root滾動高度,使scrollToView在可見區域的底部
                   int scrollHeight = (location[1] + scrollToView.getHeight()) - rect.bottom;

                   //注意,scrollHeight是一個相對移動距離,而scrollToPosition是一個絕對移動距離
                   scrollToPosition += scrollHeight;

               } else {
                   //鍵盤隱藏
                   scrollToPosition = 0;                   
               }
               root.scrollTo(0, scrollToPosition);

           }
       });
   }    

}

demo程式碼下載地址:

參考: