大家好,又见面了,我是你们的朋友全栈君。
上一篇文章只是自定义了一个键盘的样式,并未和任何的输入框进行关联。只有和输入框进行关联才能是一个有用的键盘。不知道你有没有注意到应用市场上有这样一类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;
}复制代码
到此,关于自定义键盘的算是结束,
最后奉上源码。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/107292.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...