Android各種鍵盤(pán)擋住輸入框解決辦法

作者簡(jiǎn)介? 創(chuàng)微信公眾號(hào)郭霖 WeChat ID: guolin_blog

瀟瀟鳳兒的博客地址:

http://blog.csdn.net/smileiam

正文

在開(kāi)發(fā)中,經(jīng)常會(huì)遇到鍵盤(pán)擋住輸入框的情況,比如登錄界面或注冊(cè)界面,彈出的軟鍵盤(pán)把登錄或注冊(cè)按鈕擋住了,用戶必須把軟鍵盤(pán)收起,才能點(diǎn)擊相應(yīng)按鈕,這樣的用戶體驗(yàn)非常不好。像微信則直接把登錄按鈕做在輸入框的上面,但有很多情況下,這經(jīng)常滿足不了需求。同時(shí)如果輸入框特別多的情況下,點(diǎn)擊輸入時(shí),當(dāng)前輸入框沒(méi)被擋住,但是當(dāng)前輸入框下面的輸入框卻無(wú)法獲取焦點(diǎn),必須先把鍵盤(pán)收起,再去獲取下面輸入框焦點(diǎn),這樣用戶體驗(yàn)也非常不好,那有什么辦法呢?

系統(tǒng)的 adjustResize 和 adjustPan 有什么區(qū)別,他們使用時(shí)的注意事項(xiàng),有什么系統(tǒng)要求及蔽端呢?

下面對(duì)幾種在開(kāi)發(fā)中常用的方法進(jìn)行總結(jié):

方法一:windowSoftInputMode:adjustResize和adjustPan

主要實(shí)現(xiàn)方法:在 AndroidManifest.xml 對(duì)應(yīng)的Activity里添加 android:windowSoftInputMode=”adjustPan” 或是 android:windowSoftInputMode=”adjustResize”屬性

這兩種屬性的區(qū)別,官方的解釋是:


這兩個(gè)屬性作用都是為了調(diào)整界面使鍵盤(pán)不擋住輸入框 ,我這里對(duì)這兩種屬性使用場(chǎng)景、優(yōu)缺點(diǎn)、注意事項(xiàng)進(jìn)行了全方面總結(jié),不知大家平時(shí)使用時(shí)是否注意到了。


adjustResize失效情況:activity 設(shè)置了全屏屬性指 Theme.Light.NotittleBar.Fullscreen 或者設(shè)置了 activity 對(duì)應(yīng)的主題中 android:windowTranslucentStatus 屬性,設(shè)置方式為:android:windowTranslucentStatus=true,這時(shí)如果對(duì)應(yīng)的頁(yè)面上含有輸入框,將會(huì)導(dǎo)致點(diǎn)擊輸入框時(shí)軟鍵盤(pán)彈出后鍵盤(pán)覆蓋輸入框,導(dǎo)致輸入框看不見(jiàn)。

fitsSystemWindows=”true”,只有初始的view起作用:如果在布局中不是最外層控件設(shè)置 fitsSystemWindows=”true”, 那么設(shè)置的那個(gè)控件高度會(huì)多出一個(gè)狀態(tài)欄高度。若有多個(gè)view設(shè)置了,因第一個(gè)view已經(jīng)消耗掉 insect,其他 view 設(shè)置了也會(huì)被系統(tǒng)忽略。

假設(shè)原始界面是一個(gè) LinearLayout 包含若干 EditText,如下圖所示,在分別使用兩種屬性時(shí)的表現(xiàn):


1?adjustPan

整個(gè)界面向上平移,使輸入框露出,它不會(huì)改變界面的布局;界面整體可用高度還是屏幕高度,這個(gè)可以通過(guò)下面的截圖看出,如點(diǎn)擊 輸入框6,輸入框會(huì)被推到鍵盤(pán)上方,但 輸入框1 被頂出去了,如果界面包含標(biāo)題欄,也會(huì)被頂出去。


2?adjustResize

需要界面的高度是可變的,或者說(shuō) Activity 主窗口的尺寸是可以調(diào)整的,如果不能調(diào)整,則不會(huì)起作用。

例如:Activity 的xml布局中只有一個(gè) LinearLayout 包含若干EditText,在Activity的 AndroidMainfest.xml 中設(shè)置 android:windowSoftInputMode=”adjustResize” 屬性,點(diǎn)擊 輸入框6, 發(fā)現(xiàn)軟鍵盤(pán)擋住了 輸入框6,并沒(méi)有調(diào)整,如下圖所示:


但使用這兩種屬性,我們可以總結(jié)以下幾點(diǎn):

1).使用 adjustPan, 如果需要輸入的項(xiàng)比較多時(shí),點(diǎn)擊輸入框,當(dāng)前輸入項(xiàng)會(huì)被頂?shù)杰涙I盤(pán)上方,但若當(dāng)前輸入框下面還有輸入項(xiàng)時(shí),卻需要先收起鍵盤(pán),再點(diǎn)擊相應(yīng)的輸入項(xiàng)才能輸入。這樣操作太繁瑣了,對(duì)于用戶體驗(yàn)不大好;

2).adjustResize 的使用,需要界面本身可顯示的窗口內(nèi)容能調(diào)整,如果不能,不起作用;

方法二:在界面最外層布局包裹ScrollView

1?只使用ScrollView

在相應(yīng)界面的xml布局中,最外層添加一個(gè) ScrollView,不在 AndroidMainfest.xml 中設(shè)置任何 android:windowSoftInputMode屬性,此時(shí)點(diǎn)擊輸入框,輸入框均不會(huì)被軟鍵盤(pán)檔住。即使當(dāng)前輸入框下方也有輸入框,在鍵盤(pán)顯示的情況下,也可以通過(guò)上下滑動(dòng)界面來(lái)輸入,而不用先隱藏鍵盤(pán),點(diǎn)擊下方輸入框,再顯示鍵盤(pán)輸入。

我們可以根據(jù) Android Studio 的 Inspect Layout 工具來(lái)查看界面真正占用的布局高度,工具在:


通過(guò)該工具,我們看到:

界面真正能用的高度=屏幕高度-狀態(tài)欄高度-軟鍵盤(pán)高度

界面中藍(lán)框是真正界面所用的高度:


2?ScrollView +?adjustPan

我們?cè)僭谠擃惖?AndroidMainfest.xml 中設(shè)置 windowSoftInputMode屬性 為 adjustPan:


發(fā)現(xiàn)當(dāng)前輸入框不會(huì)被擋住,但是輸入框比較多時(shí),在有鍵盤(pán)顯示時(shí),界面上下滑動(dòng),但只能滑動(dòng)部分,且如果輸入框在界面靠下方時(shí),點(diǎn)擊輸入框,標(biāo)題欄也會(huì)被頂出去,如下圖所示:


我們借助 Inspect Layout 工具查看此設(shè)置布局可用高度,從下圖可以看出,此時(shí)布局可用高度是屏幕的高度,上下滑動(dòng)也只是此屏的高度,在 輸入框9 以下的輸入框滑不出來(lái),向上滑動(dòng),也只能滑到 輸入框1。


3?ScrollView+adjustResize

我們前面說(shuō)過(guò) adjustResize 的使用必須界面布局高度是可變的,如最外層套個(gè) ScrollView 或是界面可收縮的,才起作用。這里在該類的 AndroidMainfest.xml 中設(shè)置windowSoftInputMode屬性 為 adjustResize


發(fā)現(xiàn)效果和 1 不設(shè)置任何 windowSoftInputMode屬性 類似,其使用高度也是:屏幕高度-狀態(tài)欄高度-軟鍵盤(pán)高度


我們?cè)賮?lái)看看 windowSoftInputMode 默認(rèn)屬性值 stateUnspecified:


可以看出,系統(tǒng)將選擇合適的狀態(tài),也就是在界面最外層包含一層 ScrollView 時(shí),設(shè)置默認(rèn)屬性值 stateUnspecified 其實(shí)就是 adjustResize屬性。

但以下兩方面無(wú)法滿足需求:

1).當(dāng) Activity 設(shè)置成全屏 fullscreen 模式時(shí)或是使用沉浸式狀態(tài)欄時(shí),界面最外層包裹 ?ScrollView,當(dāng)輸入框超過(guò)一屏,當(dāng)前輸入框下面的輸入框并不能上下滑動(dòng)來(lái)輸入,情況類似于 ScrollView+adjustPan,只能滑動(dòng)部分,通過(guò) Inspect Layout 也可以看到,界面可用高度是整個(gè)屏幕高度,并不會(huì)進(jìn)行調(diào)整高度。即使設(shè)置 adjustResize,也不起作用。

2).如果是類似于注冊(cè)界面或是登錄界面,鍵盤(pán)會(huì)擋住輸入框下面的登錄按鈕。

沉浸式狀態(tài)欄下

自android系統(tǒng)4.4(API>=19)就開(kāi)始支持沉浸式狀態(tài)欄,當(dāng)使用 System windows(系統(tǒng)窗口)顯示系統(tǒng)一些屬性和操作區(qū)域,如最上方的狀態(tài)及沒(méi)有實(shí)體按鍵的最下方的虛擬導(dǎo)航欄。

android:fitsSystemWindows=“true”會(huì)使得屏幕上的可布局空間位于狀態(tài)欄下方與導(dǎo)航欄上方。

方法三:當(dāng)鍵盤(pán)彈起時(shí),讓界面整體上移;鍵盤(pán)收起,讓界面整體下移

使用場(chǎng)景:針對(duì)界面全屏或是沉浸式狀態(tài)欄,輸入框不會(huì)被鍵盤(pán)遮擋。主要用于一些登錄界面,或是需要把界面整體都頂上去的場(chǎng)景。

1?主要實(shí)現(xiàn)步驟

(1). 獲取Activity布局xml的最外層控件,如xml文件如下:


先獲取到最外層控件:

RelativeLayoutmain=(RelativeLayout) findViewById(R.id.main);

(2).獲取到最后一個(gè)控件,如上面的xml文件,最后一個(gè)控件是Button:

Buttonlogin_btn=(Button) findViewById(R.id.login_btn);

(3).給最外層控件和最后一個(gè)控件添加監(jiān)聽(tīng)事件:


2?實(shí)現(xiàn)原理

此方法通過(guò)監(jiān)聽(tīng) Activity 最外層布局控件來(lái)檢測(cè)軟鍵盤(pán)是否彈出,然后去手動(dòng)調(diào)用控件的 scrollTo方法 達(dá)到調(diào)整布局目的。

方法四:監(jiān)聽(tīng)Activity頂層View,判斷軟鍵盤(pán)是否彈起,對(duì)界面重新繪制

此方法的實(shí)現(xiàn)來(lái)自android中提出的issue 5497

https://code.google.com/p/android/issues/detail?id=5497

使用場(chǎng)景:針對(duì)界面全屏或是沉浸式狀態(tài)欄,界面包含比較多輸入框,界面即使包裹了一層 ScrollView,在鍵盤(pán)顯示時(shí),當(dāng)前輸入框下面的輸入不能通過(guò)上下滑動(dòng)界面來(lái)輸入。

一、實(shí)現(xiàn)步驟

1?把 SoftHideKeyBoardUtil類 復(fù)制到項(xiàng)目中;

2?在需要使用的Activity的onCreate方法中添加 SoftHideKeyBoardUtil.assistActivity(this) 即可。

二、實(shí)現(xiàn)原理

SoftHideKeyBoardUtil類 具體代碼如下:


它的實(shí)現(xiàn)原理主要是:

(1).找到 Activity 的最外層布局控件,我們知道所有的 Activity 都是 DecorView,它就是一個(gè) FrameLayout控件,該控件id是系統(tǒng)寫(xiě)死叫 R.id.content,就是我們 setContentView 時(shí),把相應(yīng)的 View 放在此 FrameLayout 控件里

FrameLayoutcontent=(FrameLayout) activity.findViewById(android.R.id.content);

所以 content.getChildAt(0) 獲取到的 mChildOfContent,也就是我們用 setContentView 放進(jìn)去的 View。

(2).給我們的 Activity 的xml布局View設(shè)置一個(gè) Listener 監(jiān)聽(tīng):


View.getViewTreeObserver() 可以獲取一個(gè) ViewTreeObserver對(duì)象——它是一個(gè)觀察者,用以監(jiān)聽(tīng)當(dāng)前 View樹(shù) 所發(fā)生的變化。這里所注冊(cè)的 addOnGlobalLayoutListener,就是會(huì)在當(dāng)前的 View樹(shù) 的全局布局(GlobalLayout)發(fā)生變化、或者其中的 View 可視狀態(tài)有變化時(shí),進(jìn)行通知回調(diào)。『軟鍵盤(pán)彈出/隱 』都能監(jiān)聽(tīng)到。

(3).獲取當(dāng)前界面可用高度


如下圖所示:


(4).重設(shè)高度, 我們計(jì)算出的可用高度,是目前在視覺(jué)效果上能看到的界面高度。但當(dāng)前界面的實(shí)際高度是比可用高度要多出一個(gè)軟鍵盤(pán)的距離的。

通過(guò)上面的這種方法,一般布局輸入鍵盤(pán)擋住輸入框的問(wèn)題基本都能解決。即使界面全屏或是沉浸式狀態(tài)欄情況。

總結(jié)

下面對(duì)上面幾種方法進(jìn)行對(duì)比:

方法一:

優(yōu)點(diǎn):使用簡(jiǎn)單,只需在Activity的AndroidMainfest.xml中設(shè)置windowSoftInput屬性即可。

注意點(diǎn):adjustResize屬性必須要界面大小可以自身改變;

缺點(diǎn):當(dāng)輸入框比較多時(shí),當(dāng)前輸入框下方的輸入框會(huì)初鍵盤(pán)擋住,須收起鍵盤(pán)再進(jìn)入輸入;使用adjustPan,輸入框較多時(shí),因它是把界面當(dāng)成一個(gè)整體,只會(huì)顯示一屏的高度,會(huì)把ActionBar頂上去。

方法二:

優(yōu)點(diǎn):使用簡(jiǎn)單,只需在Activity的最外層布局包裹一個(gè)ScrollView即可。

注意點(diǎn):不可使用adjustPan屬性,否則ScrollView失效;

缺點(diǎn):對(duì)于全屏?xí)r,在鍵盤(pán)顯示時(shí),無(wú)法上下滑動(dòng)界面達(dá)到輸入的目的;

方法三:

優(yōu)點(diǎn):可以解決全屏?xí)r,鍵盤(pán)擋入按鈕問(wèn)題。

缺點(diǎn):只要有此需求的Activity均需要獲取到最外層控件和最后一個(gè)控件,監(jiān)測(cè)鍵盤(pán)是否彈出,再調(diào)用控件的scrollTo方法對(duì)界面整體上移或是下移。代碼冗余。

方法四:

優(yōu)點(diǎn):可以解決全屏?xí)r,鍵盤(pán)擋入輸入框問(wèn)題。只需要寫(xiě)一個(gè)全局類,其他有需求的界面直接在onCreate方法里調(diào)用此類的全局方法,即可。

缺點(diǎn):多用了一個(gè)類。

綜上所述:

1) 當(dāng)輸入框比較少時(shí),界面只有一個(gè)輸入框時(shí),可以通過(guò)方法一設(shè)置adjustPan;

2) 如果對(duì)于非全屏/非沉浸式狀態(tài)欄需求,只需要使用方法二即可;

3) 如果全屏全屏/沉浸式狀態(tài)欄界面只有一個(gè)類有鍵盤(pán)擋入輸入框需求,可使用方法三;

4) 如果大部分界面均使用全屏或沉浸式狀態(tài)欄,且有此需求,則選擇方法四更恰當(dāng)。


文章原創(chuàng)作者GuoLin 書(shū)籍推薦

郭林大神原創(chuàng)android 書(shū)籍:《第一行代碼 android》

淘寶鏈接: https://s.click.taobao.com/t?e=m%3D2%26s%3DgKUfuKdAZKocQipKwQzePOeEDrYVVa64K7Vc7tFgwiHjf2vlNIV67p2n%2BQBNMyE6Rku8%2Bpj6eJall3bs%2B3NRhNHnsKI%2BqxhyM0iVZhTFBom4YIorMPnmg8G0g2OJi%2FzmXHfenomYtn5EW9vzeG8LzfPUwktUBEmkxg5p7bh%2BFbQ%3D&pvid=10_106.6.161.154_3367_1490163222155

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容