使用Android實(shí)現(xiàn)一個(gè)懸浮在軟鍵盤上的輸入欄
前言
我們要實(shí)現(xiàn)一個(gè)懸浮在軟鍵盤上的輸入欄(即一個(gè)懸浮欄),過程中遇到了很多問題,查閱了一些網(wǎng)上的文章,結(jié)果發(fā)現(xiàn)不少是錯(cuò)誤的,走了一些彎路,這里就一一記錄一下。
懸浮欄
實(shí)現(xiàn)懸浮欄很簡單
chatInputPanel.setVisibility(View.VISIBLE); chatInputEt.setFocusable(true); chatInputEt.setFocusableInTouchMode(true); chatInputEt.requestFocus(); InputMethodManager inputManager = (InputMethodManager)chatInputEt.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.showSoftInput(chatInputEt, 0);
chatInputPanel就是懸浮欄整個(gè)layout,mChatPanelContent才是懸浮欄實(shí)際部分,chatInputEt是其中的EditText,對(duì)它做一些設(shè)置就可以實(shí)現(xiàn)將chatInputPanel懸浮在軟件盤上。
這里chatInputPanel是全屏的(點(diǎn)擊mChatPanelContent以外部分隱藏鍵盤),mChatPanelContent是在它的bottom底部,默認(rèn)隱藏(INVISIBLE)。
橫屏?xí)r軟鍵盤全屏
橫屏?xí)r,安卓默認(rèn)會(huì)將軟鍵盤全屏顯示,這樣無法實(shí)現(xiàn)懸浮欄。所以需要取消全屏顯示
在EditText中使用android:imeOptinos可對(duì)Android自帶的軟鍵盤進(jìn)行一些界面上的設(shè)置
android:imeOptions="flagNoExtractUi" //使軟鍵盤不全屏顯示,只占用一部分屏幕
android:imeOptions="actionNone" //輸入框右側(cè)不帶任何提示
android:imeOptions="actionGo" //右下角按鍵內(nèi)容為'開始'
android:imeOptions="actionSearch" //右下角按鍵為放大鏡圖片,搜索
android:imeOptions="actionSend" //右下角按鍵內(nèi)容為'發(fā)送'
android:imeOptions="actionNext" //右下角按鍵內(nèi)容為'下一步'
android:imeOptions="actionDone" //右下角按鍵內(nèi)容為'完成'
所以我們?yōu)镋ditText設(shè)置android:imeOptions="flagNoExtractUi"即可實(shí)現(xiàn)在橫屏?xí)r不全屏顯示。同時(shí),可能EditText添加相應(yīng)的監(jiān)聽器,捕捉用戶點(diǎn)擊了軟鍵盤右下角按鈕的監(jiān)聽事件,以便進(jìn)行處理。
editText.setOnEditorActionListener(new OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { Toast.makeText(MainActivity.this, "text2", Toast.LENGTH_SHORT).show(); return false; } });
監(jiān)聽軟鍵盤(該方法不可靠,廢棄,下面有靠譜的)
注意:這是網(wǎng)上的一個(gè)錯(cuò)誤方法,所以特意拿出來說一下,不感興趣直接去看(3)即可。
顯示沒問題了,但是軟鍵盤隱藏的時(shí)候要求懸浮欄同步隱藏起來。
系統(tǒng)并沒有提供監(jiān)聽軟鍵盤收起的api,所以我們只能自己實(shí)現(xiàn)。
chatInputPanel.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if(chatInputPanel.getBottom() > container.getRootView().getHeight() / 2){ chatInputPanel.setVisibility(View.INVISIBLE); } else{ chatInputPanel.setVisibility(View.VISIBLE); } } });
監(jiān)聽chatInputPanel(懸浮欄整體布局)的布局變化,當(dāng)?shù)撞看笥趓ootview高度一半的時(shí)候隱藏,否則顯示。
因?yàn)槲覀兊墓δ苁菣M屏的,所以鍵盤彈起時(shí),chatInputPanel因?yàn)閼腋≡阪I盤上,所以底部一定小于rootview高度(屏幕寬度)一半。
當(dāng)收起鍵盤,chatInputPanel會(huì)回到最底部(設(shè)置是在父布局底部),所以底部一定大于一半。
這個(gè)方法不靠譜,而且重繪會(huì)導(dǎo)致onGlobalLayout頻繁的執(zhí)行,雖然可以加上一個(gè)時(shí)間來控制,但是不推薦使用這個(gè)方式來監(jiān)聽軟鍵盤,下面看看另外一種方式。
靠譜的監(jiān)聽軟鍵盤的方法
上面的方法為什么不考慮,是因?yàn)槿溜@示FLAG_FULLSCREEN(隱藏通知欄)導(dǎo)致問題
當(dāng)我們需要全屏顯示隱藏通知欄時(shí),會(huì)使用FLAG_FULLSCREEN屬性
getActivity().getWindow().setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
但是會(huì)影響上面的懸浮欄,因?yàn)榘l(fā)現(xiàn)chatInputPanel.getBottom()始終沒變化,但是我們判斷顯示隱藏就靠這個(gè)變化。
沒變化是因?yàn)閍ndroid在全屏FLAG_FULLSCREEN的處理方式導(dǎo)致的,全屏?xí)r軟鍵盤會(huì)出現(xiàn)很多問題,這個(gè)網(wǎng)上有很多。
如何解決?
我們換一種方式監(jiān)聽軟鍵盤即可
getActivity().getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect rect = new Rect(); rootView.getWindowVisibleDisplayFrame(rect); int rootHeight=rootView.getRootView().getHeight(); int displayHeight=rect.height(); int diffHeight=rootHeight-displayHeight; if(diffHeight==0){ //鍵盤收起 chatInputPanel.setVisibility(View.INVISIBLE); }else{ //鍵盤彈出 chatInputPanel.setVisibility(View.VISIBLE); } } });
通過監(jiān)聽根布局種的content布局的變化來判斷,目前這個(gè)方法是最靠譜的。
但是還存在一個(gè)小問題,就是全屏狀態(tài)下鍵盤會(huì)覆蓋懸浮欄底部的一小部分,這個(gè)怎么辦?
終極懸浮方式
上面解決了軟鍵盤的監(jiān)聽問題,但是全屏狀態(tài)下懸浮欄總會(huì)被遮住一部分,那怎么辦?
其實(shí)這里還有一個(gè)問題,當(dāng)顯示鍵盤后,app中的布局整體被向上推起,這樣導(dǎo)致部分組件縮小等情況。
我們要首先解決這個(gè)問題,讓app的布局整體保持不動(dòng),鍵盤覆蓋在其上面,這需要在彈起鍵盤前手動(dòng)設(shè)置一下,如下:
mChatInput.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DisplayMetrics metric = new DisplayMetrics(); getActivity().getWindowManager().getDefaultDisplay().getMetrics(metric); chatInputPanel.setY(-metric.heightPixels);//解決首次可能向上推的問題 chatInputEt.setFocusable(true); chatInputEt.setFocusableInTouchMode(true); chatInputEt.requestFocus(); InputMethodManager inputManager = (InputMethodManager)chatInputEt.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.showSoftInput(chatInputEt, 0); } });
這樣將懸浮欄移到了最頂部以上,就不會(huì)出現(xiàn)上推的情況了(猜測與鍵盤的機(jī)制有關(guān),因?yàn)殒I盤彈出如果遮擋了有焦點(diǎn)的輸入組件就好重新調(diào)整窗口,我們將懸浮窗放在最上面,鍵盤怎么也不會(huì)遮擋到焦點(diǎn)的EditText,所以不會(huì)重新調(diào)整窗口)。
但是這樣懸浮欄就一直看不見了,而且我們可以看到在這里去掉了chatInputPanel.setVisibility(View.VISIBLE);代碼,那么如何顯示?
上面我們提到使用OnGlobalLayoutListener方式監(jiān)聽鍵盤,我們就在這里顯示即可,同時(shí)優(yōu)化一下顯示的位置,在這里計(jì)算窗口顯示區(qū)域上移多少,讓chatInputPanel也上移相應(yīng)位置即可,如:
private int mLastHeight = 0; getActivity().getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect rect = new Rect(); getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); int height = rect.height(); int rawHeight = getResources().getDisplayMetrics().heightPixels - rect.top; if (height == mLastHeight) return; if (height < rawHeight) { UiThreadHandler.postDelayed(new Runnable() { @Override public void run() { chatInputPanel.setVisibility(View.VISIBLE); chatInputPanel.setTranslationY(-(rawHeight - height)); } }, 200); } else { UiThreadHandler.postDelayed(new Runnable() { @Override public void run() { chatInputPanel.setVisibility(View.GONE); } }, 100); } mLastHeight = height; } });
可以看到先得到當(dāng)前窗口的顯示高度和屏幕的實(shí)際高度(窗口部分)
然后先判斷窗口顯示區(qū)域是否變化了,如果沒變化則不處理。
如果有變化,則判斷變大還是變小了。
如果變小了
說明鍵盤彈起,這時(shí)候顯示chatInputPanel,同時(shí)設(shè)置translationY為-(rawHeight - height)
首先chatInputPanel初始位置底部是與屏幕底部對(duì)齊的,雖然設(shè)置了setY,但是setY實(shí)際上就是setTranslationY,初始位置沒變,源碼:
public void setY(float y) { setTranslationY(y - mTop); }
而彈起鍵盤后想要顯示在鍵盤以上,那么就需要從最底部向上移動(dòng)一個(gè)鍵盤的高度,鍵盤高度就是rawHeight - height,所以向上移動(dòng)是將translationY設(shè)置為-(rawHeight - height)。
如果變大了
說明鍵盤收起,隱藏chatInputPanel即可。
這樣不僅解決了窗口推起的問題,也同時(shí)解決了軟鍵盤遮擋部分懸浮欄的問題,因?yàn)閼腋诘奈恢檬峭ㄟ^計(jì)算得到的,不是通過軟鍵盤上推導(dǎo)致布局調(diào)整而改變位置的。
最終代碼
最后想將這個(gè)形成一個(gè)獨(dú)立的組件,直接可用,再編寫過程中發(fā)現(xiàn)出現(xiàn)好多問題,解決所有問題后發(fā)現(xiàn)與上面的代碼都不一樣,不過思路是一致的,只不過細(xì)節(jié)調(diào)整了,比如獲取鍵盤高度等。
總結(jié)
到此這篇關(guān)于使用Android實(shí)現(xiàn)一個(gè)懸浮在軟鍵盤上的輸入欄的文章就介紹到這了,更多相關(guān)Android實(shí)現(xiàn)懸浮輸入欄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android編程設(shè)計(jì)模式之訪問者模式詳解
這篇文章主要介紹了Android編程設(shè)計(jì)模式之訪問者模式,詳細(xì)分析了訪問者模式的概念、功能、原理、使用場景并結(jié)合實(shí)例形式給出了Android訪問者模式的具體實(shí)現(xiàn)技巧與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2017-12-12Android自定義view Path 的高級(jí)用法之搜索按鈕動(dòng)畫
這篇文章主要介紹了Android自定義view Path 的高級(jí)用法之搜索按鈕動(dòng)畫,需要的朋友可以參考下2017-06-06Android自定義View圓形和拖動(dòng)圓跟隨手指拖動(dòng)
這篇文章主要介紹了Android自定義View圓形和拖動(dòng)圓跟隨手指拖動(dòng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Android獲得設(shè)備狀態(tài)信息、Mac地址、IP地址的方法
今天小編就為大家分享一篇關(guān)于Android獲得設(shè)備狀態(tài)信息、Mac地址、IP地址的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12基于Android實(shí)現(xiàn)個(gè)性彩色好看的二維碼
二維碼在我們?nèi)粘I钪袩o處不在,今天小編通過本教程給大家介紹基于Android實(shí)現(xiàn)個(gè)性彩色好看的二維碼,需要的朋友參考下吧2016-02-02Android 滑動(dòng)監(jiān)聽的實(shí)例詳解
這篇文章主要介紹了Android 滑動(dòng)監(jiān)聽的實(shí)例詳解的相關(guān)資料,希望通過本能幫助到大家,需要的朋友可以參考下2017-09-09Android開發(fā)中調(diào)用系統(tǒng)相冊(cè)上傳圖片到服務(wù)器OPPO等部分手機(jī)上出現(xiàn)短暫的顯示桌面問題的解決方法
這篇文章主要介紹了Android開發(fā)中調(diào)用系統(tǒng)相冊(cè)上傳圖片到服務(wù)器OPPO等部分手機(jī)上出現(xiàn)短暫的顯示桌面問題的解決方法,需要的朋友可以參考下2016-12-12