安卓进阶(12)之自定义数字键盘

想要源码的请戳这里:源码地址

实现效果

在这里插入图片描述

实现原理

概括:数字键盘ViewPopuwindow来呈现,通过监听Activity/Dialog中WindowdecorView焦点改变事件,以及EditText中的tag属性,来动态显示自定义的数字键盘。在Dialog中弹出自定义数字键盘,需要动态更新数字键盘的位置,和宽度。

初始化数字键盘

设置数字键盘的宽高,并监听焦点变化:

private void initKeyboard(Window window, boolean isAttachActivity) {

   ViewGroup keyboardRootView = (ViewGroup) LayoutInflater.from(window.getContext()).inflate(R.layout.keyboard_container, null);
   DisplayMetrics dm = new DisplayMetrics();
   window.getWindowManager().getDefaultDisplay().getMetrics(dm);
   int windowWidth = dm.widthPixels;
   int height = KeyboardUtils.getKeyboardHeight(window);
   mKeyboard = new BkjfKeyboard(window, keyboardRootView, windowWidth, height, isAttachActivity);
   mKeyboard.setOnKeyboardActionListener(new DefaultActionListener());
   window.getDecorView().getViewTreeObserver().addOnGlobalFocusChangeListener(new DefaultOnGlobalFocusChangeListener());
   hideKeyboard();
}

监听焦点变化

private class DefaultOnGlobalFocusChangeListener implements ViewTreeObserver.OnGlobalFocusChangeListener {
   /**
    * 是否是第一次进入
    */
   private boolean isFirstEnter = true;
   @Override
   public void onGlobalFocusChanged(View oldFocus, View newFocus) {
       if (mEditText != newFocus) {
           mEditText = null;
       }
       if (newFocus instanceof EditText) {
           mEditText = (EditText) newFocus;
           String tag = (String) mEditText.getTag();
           if (tag != null) {
               showSoftInputs(mEditText, SET_SHOW_SOFT_INPUTS_ON_FOCUS);
               setHideKeyboardListener();
               setShowKeyboardListener();

               if(isFirstEnter && mDismissWhenStart){
                   mKeyboard.dismiss();
                   isFirstEnter = false;
                   return ;
               }
               hideDefaultKeyboard();
               showKeyboard(mEditText);
               return;
           }
       }
       /**
        * 如果没有tag,就隐藏自定义键盘
        */
       hideKeyboard();
   }
   ......
}

view树的焦点变化时,并且焦点聚焦到EditText上,就去获取此时EditTexttag属性,从而显示出数字键盘。

根据tag获取数字键盘

/**
 * 根据键盘的inputType获取相应的键盘View
 * 默认提供数字键盘
 */
private IKeyboardView getKeyboardView(String rawTypes) {

    if (rawTypes == null) {
        return null;
    }
    String type = KeyboardUtils.getInputType(rawTypes);
    String flag = KeyboardUtils.getInputFlag(rawTypes);

    IKeyboardView keyboardView = keyboardViews.get(type);
    if (keyboardView == null) {
        switch (type) {
            case NumberInputType.INPUT_TYPE_NUMBER:
                keyboardView = new NumberKeyboardView(mWindow);
                keyboardViews.put(type, keyboardView);
                break;
            default: {
                try {
                    keyboardView = KeyboardUtils.getKeyboardViewFromInputType(type, mWindow);
                    keyboardViews.put(type, keyboardView);
                } catch (Exception e) {
                    throw new RuntimeException("cannot recognize tag,please refer the doc", e);
                }
            }
        }
    }

    keyboardView.setOnActionListener(mOnActionListener);
    keyboardView.setKeyFlag(flag);
    return keyboardView;
}

Dialog中弹自定义数字键盘细节

首先,需要设置dialoggravity属性,强制将dialog设置在窗口的顶部:

dialog.getWindow().setGravity(Gravity.TOP);

其次,在显示数字键盘时,需要动态更新数字键盘的位置和宽高:

IBinder windowToken = mEditText.getWindowToken();
if (windowToken != null && windowToken.isBinderAlive()) {
    Rect rect = new Rect();
    mEditText.getRootView().getGlobalVisibleRect(rect);
    Context editTextContext = mEditText.getContext();
    Resources resources = editTextContext.getApplicationContext().getResources();
    DisplayMetrics dm = resources.getDisplayMetrics();
    int y = rect.bottom - dm.heightPixels;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
        setAttachedInDecor(false);
    }
    setTouchable(true);
    showAtLocation(mEditText, Gravity.BOTTOM | Gravity.LEFT, 0, y);
    update(getWidth(), getHeight());
}

mEditText对象为Dialog实例中的EditText对象。

Dialog.dismiss()的坑

我们在监听view树的焦点变化时,是通过当前view所在window来实现的:

window.getDecorView().getViewTreeObserver().addOnGlobalFocusChangeListener(new DefaultOnGlobalFocusChangeListener());

当我们默认实现dialog时,点击外部半透明部分,它会去调用dialog.dismiss(),这时候它是会把dialog上的mDecor实例删除掉:

public void dismiss() {
    if (Looper.myLooper() == mHandler.getLooper()) {
        dismissDialog();
    } else {
        mHandler.post(mDismissAction);
    }
}
void dismissDialog() {
    ...
    try {
        mWindowManager.removeViewImmediate(mDecor);
    } finally {
        if (mActionMode != null) {
            mActionMode.finish();
        }
        mDecor = null;
        mWindow.closeAllPanels();
        onStop();
        mShowing = false;

        sendDismissMessage();
    }
}

所以,我们在每次调用dialog.show()之前,需要重新去设置一次popuwindow,重新去监听view树的焦点变化。

当然了,也可以将dialog的外部蒙层部分,设置点击无响应,或者给外部蒙层部分设置点击事件,调用dialog.hide(),毕竟dialog.hide()只是将dialog中mDecor对象的可见性设置为GONE。如果这样做的话,需要将dialog和数字键盘绑定起来,在调用dialog.hide()时,来实现隐藏数字键盘。目前写的数字键盘组件,跟dialog是完全分离的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值