自定義鍵盤(二)

稀飯_發表於2018-09-27
一引言

上一篇文章只是自定義了一個鍵盤的樣式,並未和任何的輸入框進行關聯。只有和輸入框進行關聯才能是一個有用的鍵盤。不知道你有沒有注意到應用市場上有這樣一類app:第三方輸入法app,比如訊飛輸入法,搜狗輸入法;

第三方輸入法app:設定完成之後,手機上所有的輸入框都會彈出第三方鍵盤。它們實現都是通過系統的InputMethodManager類去做的擴充套件。安裝第三方輸入法的手機,可以在設定--高階設定-語言和輸入法中找到。----系統級別的輸入法

那麼有沒有app級別的輸入法擴充套件呢?

如果有的話會讓關於鍵盤的開發變得更加容易,於是,筆者就去下載一些炒股app,它們都是實現了類似app級別的鍵盤,發現設定中並未找到他們關於鍵盤的定義。筆者也沒有google到關於app級別的鍵盤。筆者認定炒股app中的自定義鍵盤的實現思路應該也是封裝然後關聯輸入框。

二需求

我們如何能封裝一個沒有耦合性的自定義鍵盤,筆者能想到的需求如下:

  • 動態新增到任何佈局中
  • 解決和系統鍵盤顯示衝突
  • 動態繫結系統輸入框
  • 有show和hide動畫,讓鍵盤顯示更加優雅
  • 沒有耦合,使用方便,儘可能讓原生屬性有效
  • 鍵盤特殊按鈕監聽
  • 解決鍵盤覆蓋輸入框的問題
  • 點選非鍵盤,非輸入框區域,讓鍵盤消失。

三實現需求

3.1動態新增到任何佈局中

android中每個頁面佈局都有一個DecorView包裹著,我們可以獲取這個DecorView,然後把我們的鍵盤佈局檔案新增到這個跟佈局下:

(activity.getWindow().getDecorView().findViewById(android.R.id.content));複製程式碼

但是這樣會有一個問題,假如有這樣一種佈局,頁面巢狀ViewPager,ViewPager中巢狀多個Fragment,而他們共同擁有一個DecorView。如果把鍵盤掛載到這樣一個佈局中,勢必會造成頁面之間互相影響。於是,筆者就提供方法讓掛載鍵盤的根佈局通過外部傳入,至於你是傳遞DecorView還是傳遞一個fragment的根佈局,由外部決定。

3.2解決和系統鍵盤顯示衝突

這就需要我們把頁面中所有的EditText傳遞到封裝的工具類中,呼叫這個方法隱藏系統鍵盤

/**
 * 隱藏系統鍵盤
 *
 * @param editText
 */
public static void hideSystemSoftKeyboard(EditText editText) {
    int sdkInt = Build.VERSION.SDK_INT;
    if (sdkInt >= 11) {
        try {
            Class<EditText> cls = EditText.class;
            Method setShowSoftInputOnFocus;
            setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
            setShowSoftInputOnFocus.setAccessible(true);
            setShowSoftInputOnFocus.invoke(editText, false);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    } else {
        editText.setInputType(InputType.TYPE_NULL);
    }
}複製程式碼

然後對應的頁面清單檔案設定

android:windowSoftInputMode="stateHidden|stateUnchanged"複製程式碼

讓系統鍵盤不彈出來

3.3動態繫結輸入框

系統的輸入框是當EditText獲取焦點的時候會彈出來,所以這裡我們要給傳遞進來的EditText設定焦點改變監聽,通過焦點改變來顯示鍵盤。

key.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View view, boolean b) {
        if (b) {
            currentEditText = (EditText) view;
            showSoftKeyboard();
        }
    }
});複製程式碼

裡有一點需要注意,我們點選確定按鈕的時候,當前獲取焦點的EditText仍然在獲取焦點,再次點選這個EditText,鍵盤並未彈出。因為焦點沒有改變。這就需要我們在我們傳遞過來的佈局檔案中新增一個寬高是0的EditText。讓使用者點選完成的時候,這個EditText獲取焦點。

FrameLayout.LayoutParams params2 = new FrameLayout.LayoutParams(0, 0, Gravity.BOTTOM);
rootView.addView(frameLayout, params);
focusReplace = new EditText(context);
rootView.addView(focusReplace, params2);複製程式碼

3.4有show和hide動畫,讓鍵盤顯示更加優雅

這無非是系統的View動畫,實現起來比較簡單

3.5沒有耦合,使用方便,儘可能讓原生屬性有效

這就需要我們用EditText,獲取他的屬性,然後根據屬性去設定鍵盤的變換

3.6鍵盤特殊按鈕監聽

我們寫一個外部回撥方法即可實現這個需求。

3.7鍵盤遮擋

這裡就需要首先判斷是否已經被遮擋,如果被遮擋,需要算出來整個佈局需要移動多少,當然鍵盤佈局不能移動。移動方式可以通過屬性動畫或者scrollBy方法。這裡我選擇屬性動畫。

在鍵盤show出的時候:

//獲取傳遞過來的跟佈局的寬高
Rect rect = new Rect();
frameLayout.getWindowVisibleDisplayFrame(rect);
//計算當前獲取焦點的輸入框在螢幕中的位置
int[] vLocation = new int[2];
currentEditText.getLocationOnScreen(vLocation);
int keyboardTop = vLocation[1] + currentEditText.getHeight() / 2 + currentEditText.getPaddingBottom() + currentEditText.getPaddingTop();
//輸入框或基線View的到螢幕的距離 + 鍵盤高度 如果 超出了螢幕的承載範圍, 就需要移動.
int moveHeight = rect.bottom - keyboardTop - keyboardView.getHeight();
moveHeight = moveHeight > 0 ? 0 : moveHeight;
if (moveHeight != 0) {
    rootView.setTag("move");
    //遍歷所有的子View,讓其向上移動改移動的高度
    for (int i = 0; i < rootView.getChildCount(); i++) {
        if (rootView.getChildAt(i) != frameLayout) {
            rootView.getChildAt(i).setTranslationY(moveHeight);
            ObjectAnimator.ofFloat(rootView.getChildAt(i), "translationY", 0, moveHeight).setDuration(400).start();
        }
    }
}複製程式碼

在鍵盤隱藏的時候:

Object tag = rootView.getTag();
if (tag != null) {
    //遍歷所有的子View,讓其向上移動改移動的高度
    for (int i = 0; i < rootView.getChildCount(); i++) {
        if (rootView.getChildAt(i) != frameLayout) {
            ObjectAnimator.ofFloat(rootView.getChildAt(i), "translationY", 0).setDuration(400).start();
        }
    }
}複製程式碼

基本使用:

keyboardViewManager = KeyboardViewManager
        .builder()
        .bindEditText(edit1, edit2, edit3)//需要使用自定義鍵盤的控制元件
        .showSystemKeyboard(edit4)//需要使用系統鍵盤的控制元件
        .bindEditTextCallBack(edit1, new KeyboardViewManager.onSureClickListener() {
            @Override
            public void onSureClick() {

            }
        })//自定義鍵盤確定回撥
        .bindEditTextCallBack(edit2, new KeyboardViewManager.onSureClickListener() {
            @Override
            public void onSureClick() {

            }
        })//自定義鍵盤確定回撥
        .build(this)
        .addKeyboardView(rootView);//需要傳入的跟佈局複製程式碼

四.細節使用

4.1鍵盤預設情況下彈出,我們可以設定輸入框的父佈局新增一下屬性:

android:focusable="true"
android:focusableInTouchMode="true"複製程式碼

4.2我們可以設定這個輸入框只輸入數字。模式是輸入英文字母

android:inputType="number" 複製程式碼

4.3點選非鍵盤和焦點區域,讓鍵盤消失

重寫activity的方法,然後呼叫隱藏鍵盤方法

@Override
public boolean onTouchEvent(MotionEvent event) {
    keyboardViewManager.hideSoftKeyboard();
    return super.onTouchEvent(event);
}複製程式碼

假如在Fragment中,在Fragment的onCreateView方法中呼叫Activity中的onTouchEvent方法:

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.frag_home_tab, container, false);
    //點選空白區域系統軟鍵盤消失 
    view.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            getActivity().onTouchEvent(motionEvent);
            return false;
        }
    });
    return view;
}複製程式碼

到此,關於自定義鍵盤的算是結束,

自定義鍵盤(二)

最後奉上原始碼


相關文章