首頁 > 軟體

使用Android實現一個懸浮在軟鍵盤上的輸入欄

2022-04-08 19:01:06

前言

我們要實現一個懸浮在軟鍵盤上的輸入欄(即一個懸浮欄),過程中遇到了很多問題,查閱了一些網上的文章,結果發現不少是錯誤的,走了一些彎路,這裡就一一記錄一下。

懸浮欄

實現懸浮欄很簡單

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就是懸浮欄整個layout,mChatPanelContent才是懸浮欄實際部分,chatInputEt是其中的EditText,對它做一些設定就可以實現將chatInputPanel懸浮在軟體盤上。

這裡chatInputPanel是全螢幕的(點選mChatPanelContent以外部分隱藏鍵盤),mChatPanelContent是在它的bottom底部,預設隱藏(INVISIBLE)。

橫屏時軟鍵盤全螢幕

橫屏時,安卓預設會將軟鍵盤全螢幕顯示,這樣無法實現懸浮欄。所以需要取消全螢幕顯示

在EditText中使用android:imeOptinos可對Android自帶的軟鍵盤進行一些介面上的設定

  • android:imeOptions="flagNoExtractUi" //使軟鍵盤不全螢幕顯示,只佔用一部分螢幕

  • android:imeOptions="actionNone" //輸入框右側不帶任何提示

  • android:imeOptions="actionGo" //右下角按鍵內容為'開始'

  • android:imeOptions="actionSearch" //右下角按鍵為放大鏡圖片,搜尋

  • android:imeOptions="actionSend" //右下角按鍵內容為'傳送'

  • android:imeOptions="actionNext" //右下角按鍵內容為'下一步'

  • android:imeOptions="actionDone" //右下角按鍵內容為'完成'

所以我們為EditText設定android:imeOptions="flagNoExtractUi"即可實現在橫屏時不全螢幕顯示。同時,可能EditText新增相應的監聽器,捕捉使用者點選了軟鍵盤右下角按鈕的監聽事件,以便進行處理。

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;   
        }   
    });  

監聽軟鍵盤(該方法不可靠,廢棄,下面有靠譜的)

注意:這是網上的一個錯誤方法,所以特意拿出來說一下,不感興趣直接去看(3)即可。

顯示沒問題了,但是軟鍵盤隱藏的時候要求懸浮欄同步隱藏起來。

系統並沒有提供監聽軟鍵盤收起的api,所以我們只能自己實現。

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);
        }
    }
});

監聽chatInputPanel(懸浮欄整體佈局)的佈局變化,當底部大於rootview高度一半的時候隱藏,否則顯示。

因為我們的功能是橫屏的,所以鍵盤彈起時,chatInputPanel因為懸浮在鍵盤上,所以底部一定小於rootview高度(螢幕寬度)一半。

當收起鍵盤,chatInputPanel會回到最底部(設定是在父佈局底部),所以底部一定大於一半。

這個方法不靠譜,而且重繪會導致onGlobalLayout頻繁的執行,雖然可以加上一個時間來控制,但是不推薦使用這個方式來監聽軟鍵盤,下面看看另外一種方式。

靠譜的監聽軟鍵盤的方法

上面的方法為什麼不考慮,是因為全螢幕顯示FLAG_FULLSCREEN(隱藏通知欄)導致問題

當我們需要全螢幕顯示隱藏通知欄時,會使用FLAG_FULLSCREEN屬性

getActivity().getWindow().setFlags(
        WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);

但是會影響上面的懸浮欄,因為發現chatInputPanel.getBottom()始終沒變化,但是我們判斷顯示隱藏就靠這個變化。

沒變化是因為android在全螢幕FLAG_FULLSCREEN的處理方式導致的,全螢幕時軟鍵盤會出現很多問題,這個網上有很多。

如何解決?

我們換一種方式監聽軟鍵盤即可

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);
        }
    }
});

通過監聽根佈局種的content佈局的變化來判斷,目前這個方法是最靠譜的。

但是還存在一個小問題,就是全螢幕狀態下鍵盤會覆蓋懸浮欄底部的一小部分,這個怎麼辦?

終極懸浮方式

上面解決了軟鍵盤的監聽問題,但是全螢幕狀態下懸浮欄總會被遮住一部分,那怎麼辦?

其實這裡還有一個問題,當顯示鍵盤後,app中的佈局整體被向上推起,這樣導致部分元件縮小等情況。

我們要首先解決這個問題,讓app的佈局整體保持不動,鍵盤覆蓋在其上面,這需要在彈起鍵盤前手動設定一下,如下:

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);
    }
});

這樣將懸浮欄移到了最頂部以上,就不會出現上推的情況了(猜測與鍵盤的機制有關,因為鍵盤彈出如果遮擋了有焦點的輸入元件就好重新調整視窗,我們將懸浮窗放在最上面,鍵盤怎麼也不會遮擋到焦點的EditText,所以不會重新調整視窗)。

但是這樣懸浮欄就一直看不見了,而且我們可以看到在這裡去掉了chatInputPanel.setVisibility(View.VISIBLE);程式碼,那麼如何顯示?

上面我們提到使用OnGlobalLayoutListener方式監聽鍵盤,我們就在這裡顯示即可,同時優化一下顯示的位置,在這裡計算視窗顯示區域上移多少,讓chatInputPanel也上移相應位置即可,如:

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;    
    }
});

可以看到先得到當前視窗的顯示高度和螢幕的實際高度(視窗部分)

然後先判斷視窗顯示區域是否變化了,如果沒變化則不處理。

如果有變化,則判斷變大還是變小了。

如果變小了

說明鍵盤彈起,這時候顯示chatInputPanel,同時設定translationY為-(rawHeight - height)

首先chatInputPanel初始位置底部是與螢幕底部對齊的,雖然設定了setY,但是setY實際上就是setTranslationY,初始位置沒變,原始碼:

public void setY(float y) {
    setTranslationY(y - mTop);
}

而彈起鍵盤後想要顯示在鍵盤以上,那麼就需要從最底部向上移動一個鍵盤的高度,鍵盤高度就是rawHeight - height,所以向上移動是將translationY設定為-(rawHeight - height)。

如果變大了

說明鍵盤收起,隱藏chatInputPanel即可。

這樣不僅解決了視窗推起的問題,也同時解決了軟鍵盤遮擋部分懸浮欄的問題,因為懸浮欄的位置是通過計算得到的,不是通過軟鍵盤上推導致佈局調整而改變位置的。

最終程式碼

最後想將這個形成一個獨立的元件,直接可用,再編寫過程中發現出現好多問題,解決所有問題後發現與上面的程式碼都不一樣,不過思路是一致的,只不過細節調整了,比如獲取鍵盤高度等。

總結

到此這篇關於使用Android實現一個懸浮在軟鍵盤上的輸入欄的文章就介紹到這了,更多相關Android實現懸浮輸入欄內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com