上一篇文章只是自定義了一個鍵盤的樣式,並未和任何的輸入框進行關聯。只有和輸入框進行關聯才能是一個有用的鍵盤。不知道你有沒有注意到應用市場上有這樣一類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;
}複製程式碼
到此,關於自定義鍵盤的算是結束,
最後奉上原始碼。