作者簡(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è)按鈕擋住了彼乌,用戶(hù)必須把軟鍵盤(pán)收起,才能點(diǎn)擊相應(yīng)按鈕渊迁,這樣的用戶(hù)體驗(yàn)非常不好慰照。像微信則直接把登錄按鈕做在輸入框的上面,但有很多情況下琉朽,這經(jīng)常滿(mǎn)足不了需求毒租。同時(shí)如果輸入框特別多的情況下,點(diǎn)擊輸入時(shí)箱叁,當(dāng)前輸入框沒(méi)被擋住墅垮,但是當(dāng)前輸入框下面的輸入框卻無(wú)法獲取焦點(diǎn),必須先把鍵盤(pán)收起耕漱,再去獲取下面輸入框焦點(diǎn)算色,這樣用戶(hù)體驗(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ì)于用戶(hù)體驗(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è)僭谠擃?lèi)的 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 或是界面可收縮的肾请,才起作用。這里在該類(lèi)的 AndroidMainfest.xml 中設(shè)置windowSoftInputMode屬性 為 adjustResize
發(fā)現(xiàn)效果和 1 不設(shè)置任何 windowSoftInputMode屬性 類(lèi)似更胖,其使用高度也是:屏幕高度-狀態(tài)欄高度-軟鍵盤(pán)高度
我們?cè)賮?lái)看看 windowSoftInputMode 默認(rèn)屬性值 stateUnspecified:
可以看出铛铁,系統(tǒng)將選擇合適的狀態(tài),也就是在界面最外層包含一層 ScrollView 時(shí)却妨,設(shè)置默認(rèn)屬性值 stateUnspecified 其實(shí)就是 adjustResize屬性饵逐。
但以下兩方面無(wú)法滿(mǎn)足需求:
1).當(dāng) Activity 設(shè)置成全屏 fullscreen 模式時(shí)或是使用沉浸式狀態(tài)欄時(shí),界面最外層包裹 ?ScrollView彪标,當(dāng)輸入框超過(guò)一屏倍权,當(dāng)前輸入框下面的輸入框并不能上下滑動(dòng)來(lái)輸入,情況類(lèi)似于 ScrollView+adjustPan捞烟,只能滑動(dòng)部分薄声,通過(guò) Inspect Layout 也可以看到,界面可用高度是整個(gè)屏幕高度题画,并不會(huì)進(jìn)行調(diào)整高度默辨。即使設(shè)置 adjustResize,也不起作用婴程。
2).如果是類(lèi)似于注冊(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類(lèi) 復(fù)制到項(xiàng)目中;
2?在需要使用的Activity的onCreate方法中添加 SoftHideKeyBoardUtil.assistActivity(this) 即可丛晦。
二巨缘、實(shí)現(xiàn)原理
SoftHideKeyBoardUtil類(lèi) 具體代碼如下:
它的實(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è)全局類(lèi)逐哈,其他有需求的界面直接在onCreate方法里調(diào)用此類(lèi)的全局方法,即可问顷。
缺點(diǎn):多用了一個(gè)類(lèi)昂秃。
綜上所述:
1) 當(dāng)輸入框比較少時(shí)禀梳,界面只有一個(gè)輸入框時(shí),可以通過(guò)方法一設(shè)置adjustPan肠骆;
2) 如果對(duì)于非全屏/非沉浸式狀態(tài)欄需求算途,只需要使用方法二即可;
3) 如果全屏全屏/沉浸式狀態(tài)欄界面只有一個(gè)類(lèi)有鍵盤(pán)擋入輸入框需求哗戈,可使用方法三郊艘;
4) 如果大部分界面均使用全屏或沉浸式狀態(tài)欄荷科,且有此需求唯咬,則選擇方法四更恰當(dāng)。
文章原創(chuàng)作者GuoLin 書(shū)籍推薦
郭林大神原創(chuàng)android 書(shū)籍:《第一行代碼 android》