效果图:
注:完整代码在最下面
使用:
布局文件中:
<com.ts_xiaoa.lib.widget.InputCodeView
android:id="@+id/input_code_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal"
android:layout_marginEnd="@dimen/margin_normal"
app:inputCodeBackground="@drawable/ts_view_input_code"
app:inputCodeCount="4"
app:inputCodeSpace="15dp"
app:inputCodeTextColor="@color/colorPrimary"
app:inputCodeTextSize="15sp"
app:inputCodeTextStyle="bold" />
样式我通过selector实现的。设置边框背景,我这里设置的选择蓝色边框、未选中灰色边框。就是效果图中的样式app:inputCodeBackground="@drawable/ts_view_input_code"
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<shape>
<stroke android:width="1dp" android:color="@color/colorPrimary" />
<corners android:radius="4dp" />
</shape>
</item>
<item android:state_selected="false">
<shape>
<stroke android:width="1dp" android:color="#CCCCCC" />
<corners android:radius="4dp" />
</shape>
</item>
</selector>
代码中:
binding.inputCodeView.setOnInputFinishListener(inputCode -> {
ToastUtil.showShort("输入完了 内容:" + inputCode);
});
一、实现思路:
组合实现、n个TextView + 一个EditText。TextView用于输入后的显示。EditText用于输入相关操作如:唤起软键盘,监听输入、删除、输入完成等。
二、属性设置
乍一想也就这么几个需要控制的。有需要
- InputCodeCount:输入的验证码数量(默认4位)
- inputCodeSpace:每个验证码间的间距
- inputCodeTextSize:验证码文字大小
- inputCodeTextColor:验证码文字颜色
- inputCodeTextStyle:验证码文字风格(其实就是设置是否加粗)
- inputCodeBackGround:输入框的背景,单个的背景。就是效果图中的边框
代码定义这些属性:
<!--验证码输入框-->
<declare-styleable name="InputCodeView">
<!--验证码位数-->
<attr name="inputCodeCount" format="integer" />
<!--验证码间隔-->
<attr name="inputCodeSpace" format="dimension" />
<!--背景样式-->
<attr name="inputCodeBackground" format="reference" />
<!--验证码文字大小-->
<attr name="inputCodeTextSize" format="dimension" />
<!--验证码文字颜色-->
<attr name="inputCodeTextColor" format="color" />
<!--验证码文字style 加粗正常-->
<attr name="inputCodeTextStyle" format="enum">
<enum name="bold" value="0" />
<enum name="normal" value="1" />
</attr>
</declare-styleable>
三、代码实现步骤
- 创建类继承ViewGroup
- 获取定义的属性,给属性默认值、创建子View(TextView和EditText)
- 实现onMeasure方法,用于测量view大小以及限制子view(TextView)大小
- 实现onLayout方法,用于摆放子view(TextView)
- 实现输入完成后的回调监听
四、关键代码
实现onMeasure方法,用于测量view大小以及限制子view(TextView)大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for (TextView textView : textViewList) {
//计算单个验证码的大小
int paddingSize = getPaddingStart() + getPaddingEnd();
int spaceSize = (inputCodeCount - 1) * inputCodeSpace;
inputTextViewSize = (getMeasuredWidth() - paddingSize - spaceSize) / inputCodeCount;
//设置textView的大小
int childMeasureSpec = MeasureSpec.makeMeasureSpec(inputTextViewSize, MeasureSpec.EXACTLY);
measureChild(textView, childMeasureSpec, childMeasureSpec);
}
//计算高度(这里就没管固定高度的情况了,都通过计算得到高度)
heightMeasureSpec = MeasureSpec.makeMeasureSpec(getPaddingTop() + inputTextViewSize + getPaddingBottom(), MeasureSpec.EXACTLY);
measureChild(editText, widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
实现onLayout方法,用于摆放子view(TextView)
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//TextView从左到右挨个摆放、中间加上间距就完事.EditText随便摆,反正看不见
int left = getPaddingStart();
for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i) instanceof EditText) {
View childAt = getChildAt(i);
childAt.layout(l, t, r, b);
} else {
TextView textView = (TextView) getChildAt(i);
textView.layout(left, getPaddingTop(), left + inputTextViewSize, getPaddingTop() + inputTextViewSize);
left += inputTextViewSize + inputCodeSpace;
}
}
}