根据Android系统源码拨号应用的键盘的实现方式实现进行实现 之前有个需求是和手机拨号键盘一样类似的需求,虽然之前也做过类似的需求,但是觉得之前的实现不够优雅和完善,刚好因为一些原因在对源码进行各种探究,于是顺便看看源码中的拨号键盘是如何实现. 最开始也在网上进行了一些查找,不过比较细致和完善的也没有,这里直接把系统源码中的实现进行完整展示. 拨号的系统应用是Dialer,对这部分的处理在它的依赖库PhoneCommon和InCallUI中,具体路径如下:
PhoneCommon com.android.phone.common.dialpad.DigitsEditText PhoneCommon\res\layout\dialpad_view_unthemed.xml InCallUI com.android.phone.common.dialpad.DialpadView
处理要点
进入界面和点击EditText时不弹出系统输入法
保持EditText按住时左右滑动文字也进行滑动效果
未输入和在最末尾输入时一般不显示光标,在中间时进行显示
有时是设置为可以点击将光标移动到中央,有时是禁用点击将光标移动到文字中
禁用点击移动光标时进行滑动到中间后继续输入会自动跳转到末尾
将选择的字符输入到EditText
禁用系统输入法 1. EditText简单自定义 这里直接贴出PhoneCommon中的源码
public class DigitsEditText extends EditText { public DigitsEditText (Context context, AttributeSet attrs) { super (context, attrs); setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); setShowSoftInputOnFocus(false ); } @Override protected void onFocusChanged (boolean focused, int direction, Rect previouslyFocusedRect) { super .onFocusChanged(focused, direction, previouslyFocusedRect); final InputMethodManager imm = ((InputMethodManager) getContext() .getSystemService(Context.INPUT_METHOD_SERVICE)); if (imm != null && imm.isActive(this )) { imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0 ); } } @Override public boolean onTouchEvent (MotionEvent event) { final boolean ret = super .onTouchEvent(event); final InputMethodManager imm = ((InputMethodManager) getContext() .getSystemService(Context.INPUT_METHOD_SERVICE)); if (imm != null && imm.isActive(this )) { imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0 ); } return ret; } }
SUGGESTIONS是禁用拼写检查,其他的处理和网上的一些帖子说的一样,就是在点击效果时隐藏掉输入法
<DigitsEditText android:background ="@android:color/transparent" android:cursorVisible ="false" android:focusableInTouchMode ="true" android:freezesText ="true" android:gravity ="center" android:maxLines ="1" android:scrollHorizontally ="true" android:singleLine ="true" android:textCursorDrawable ="@null" />
这是源码中进行使用时的一些配置 cursorVisible是光标默认显示隐藏 freezesText是旋转时会保存已输入的内容
2. 进入界面时不弹出软键盘 即使自定义了EditText,但是在进入界面时如果未进行配置还是会因为光标会默认找寻EditText进行选中弹出 这里有2种方式进行解决:
界面打开时焦点默认选中到别处 在EditText的父控件上设置两个属性
android:focusable="true" android:focusableInTouchMode="true"
在AndroidManifest中对该界面的输入法弹出行为进行配置 在Activity配置android:windowSoftInputMode="stateAlwaysHidden|adjustNothing"
即可
点击和光标的处理 点击和光标由以下几个方法进行控制:
EditText.setClickable(Boolean); EditText.setLongClickable(Boolean); EditText.setFocusableInTouchMode(Boolean); EditText.setCursorVisible(Boolean);
1. 可进行点击来移动光标位置,长按具有选中全部效果 具体效果就是手机中拨号键盘输入后未播出时的效果
将上述4个方法中前3个全部置为true
因为开始默认设置了光标不显示,如果有输入内容时点击显示光标 设置点击效果: if (content.etInput.length() != 0 ) { content.etInput.isCursorVisible = true }
输入内容清空时隐藏光标etInput.addTextChangedListener(this ) override fun afterTextChanged (s: Editable ) { if (etInput.length() == 0 ) { etInput.isCursorVisible = false } }
在末尾继续输入时隐藏光标 参加下面进行输入的keyPressed方法中的处理
2. 只能继续末尾输入和滑动查看,滑动到中间进行输入自动跳到末尾 具体效果也就是手机中播出接通后进行输入时的效果 将上面的四个方法全部置为false即可
优雅的进行输入 虽然直接setText()也是可以进行输入控制,不过源码中的方式更为优雅,并且直接setText()在是插入时需要进行额外处理
android.view.KeyEvent
中定义了键盘输入的所有键值,比如0-9对应KeyEvent.KEYCODE_0-9
EditText继承于TextView的onKeyDown(int keyCode, KeyEvent event)
方法即可进行输入,并且根据光标位置进行插入
R.id.tvNum0 -> keyPressed(KeyEvent.KEYCODE_0) R.id.tvNum1 -> keyPressed(KeyEvent.KEYCODE_1) R.id.tvNum2 -> keyPressed(KeyEvent.KEYCODE_2) R.id.tvNum3 -> keyPressed(KeyEvent.KEYCODE_3) R.id.tvNum4 -> keyPressed(KeyEvent.KEYCODE_4) R.id.tvNum5 -> keyPressed(KeyEvent.KEYCODE_5) R.id.tvNum6 -> keyPressed(KeyEvent.KEYCODE_6) R.id.tvNum7 -> keyPressed(KeyEvent.KEYCODE_7) R.id.tvNum8 -> keyPressed(KeyEvent.KEYCODE_8) R.id.tvNum9 -> keyPressed(KeyEvent.KEYCODE_9) R.id.tvSymbolAsterisk -> keyPressed(KeyEvent.KEYCODE_STAR) R.id.tvSymbolPoundSign -> keyPressed(KeyEvent.KEYCODE_POUND) private fun keyPressed (keyCode: Int ) { etInput.onKeyDown(keyCode, KeyEvent(KeyEvent.ACTION_DOWN, keyCode)) val length = content.etInput.length() if (length == etInput.selectionStart && length == etInput.selectionEnd) { etInput.isCursorVisible = false } }
源码中对实现EditText自定义键盘进行输入的基本内容如上述,在源码中还有很多额外处理,比如按键音的播放也在其中,有兴趣和需要的可以自行查看.