Android自定义控件通用验证码输入框的实现

2020-11-04 17:11:45 浏览数 (1)

需求

4位验证码输入框:

效果图:

1. 输入框一行可输入4位数字类型的验证码; 2. 4位数字之间有间隔(包括底线); 3. 输入框不允许有光标; 4. 底线根据输入位置显示高亮(蓝色); 6. 输入完成,回调结果,输入过程中,也进行回调;

分析

这种效果,很难直接在Edittext上处理: — 输入框均分4等份,还要有间隔; — 更难处理的是Edittext输入框禁止光标,那么,没有光标,我们如何调起虚拟键盘输入数据? — 等…

与其在一个控件上折腾,这么难受,不如自定义一个控件,实现这种效果。 自定义控件最简单的方案:使用多个控件,组合出这种效果。

1、布局如何实现?

1.禁止光标,我们直接使用TextView就解决了,而非Edittext; 2.一行显示4位数字,比较简单,可以使用线性布局的权重,对TextView进行控制为4等分; 3.每个TextView下面跟着一个底线,将来我们就能对底线设置高亮颜色了; 这样,基本的布局展示就可以了!!!

2、使用了TextView,那么我们如何接收用户的输入呢? 也很简单,我们在4个TextView的上方平铺一个EditText,设置透明, 当用户点击到该控件时,会自动调起软键盘,接收输入的文本。 EditText接收到用户输入的文本,如何显示在TextView呢?

3、我们监听EditText文本输入事件,最多仅接收4个输入字符, 每接收到一个字符,我们就赋值给对应的TextView; 底线也随要设置的文本切换显示高亮;

4、如何删除已输入的数值? 我们监听EditText按键事件,拦截DEL键,从后向前挨着删除字符即可; 底线也随要删除的文本切换显示高亮;

5、是否需要自定义属性 分析我们自己的项目,虽然是公用的控件,但是该控件比较简单,没有特别的要求,所以没必要自定义属性了! 如果大家有需要的,可根据需要自己定义; 如何定义属性?请自行查找资料;

既然,问题都分析清楚了,那我们就开始快速实现吧

具体实现

布局文件 phone_code.xml

代码语言:javascript复制
<?xml version="1.0" encoding="utf-8"? 
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" 
<LinearLayout
android:id="@ id/ll_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" 
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginRight="7dp" 
<TextView
android:id="@ id/tv_code1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#2D2D2D"
android:textSize="40sp"
android:background="@null"
android:gravity="center"/ 
<View
android:id="@ id/v1"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#3F8EED" / 
</LinearLayout 
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginRight="7dp"
android:layout_marginLeft="7dp" 
<TextView
android:id="@ id/tv_code2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#2D2D2D"
android:textSize="40sp"
android:background="@null"
android:gravity="center"/ 
<View
android:id="@ id/v2"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#999999" / 
</LinearLayout 
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginRight="7dp"
android:layout_marginLeft="7dp" 
<TextView
android:id="@ id/tv_code3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#2D2D2D"
android:textSize="40sp"
android:background="@null"
android:gravity="center"/ 
<View
android:id="@ id/v3"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#999999" / 
</LinearLayout 
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginLeft="7dp" 
<TextView
android:id="@ id/tv_code4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#2D2D2D"
android:background="@null"
android:textSize="40sp"
android:gravity="center"/ 
<View
android:id="@ id/v4"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#999999" / 
</LinearLayout 
</LinearLayout 
<EditText
android:id="@ id/et_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@ id/ll_code"
android:layout_alignBottom="@ id/ll_code"
android:background="@android:color/transparent"
android:textColor="@android:color/transparent"
android:cursorVisible="false"
android:inputType="number"/ 
</RelativeLayout 

et_code 输入框,设置了透明和无光标,仅接收数字; tv_code1~4 为显示数字的控件; v1~4 为数字文本的底线,用于设置高亮;

自定义控件代码 PhoneCode

代码语言:javascript复制
package iwangzhe.customview2.phonecode;
import android.content.Context;
import android.graphics.Color;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import iwangzhe.customview2.R;
/**
* 类:PhoneCode
* 作者: qxc
* 日期:2018/3/14.
*/
public class PhoneCode extends RelativeLayout {
private Context context;
private TextView tv_code1;
private TextView tv_code2;
private TextView tv_code3;
private TextView tv_code4;
private View v1;
private View v2;
private View v3;
private View v4;
private EditText et_code;
private List<String  codes = new ArrayList< ();
private InputMethodManager imm;
public PhoneCode(Context context) {
super(context);
this.context = context;
loadView();
}
public PhoneCode(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
loadView();
}
private void loadView(){
imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
View view = LayoutInflater.from(context).inflate(R.layout.phone_code, this);
initView(view);
initEvent();
}
private void initView(View view){
tv_code1 = (TextView) view.findViewById(R.id.tv_code1);
tv_code2 = (TextView) view.findViewById(R.id.tv_code2);
tv_code3 = (TextView) view.findViewById(R.id.tv_code3);
tv_code4 = (TextView) view.findViewById(R.id.tv_code4);
et_code = (EditText) view.findViewById(R.id.et_code);
v1 = view.findViewById(R.id.v1);
v2 = view.findViewById(R.id.v2);
v3 = view.findViewById(R.id.v3);
v4 = view.findViewById(R.id.v4);
}
private void initEvent(){
//验证码输入
et_code.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if(editable != null && editable.length() 0) {
et_code.setText("");
if(codes.size() < 4){
codes.add(editable.toString());
showCode();
}
}
}
});
// 监听验证码删除按键
et_code.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
if (keyCode == KeyEvent.KEYCODE_DEL && keyEvent.getAction() == KeyEvent.ACTION_DOWN && codes.size() 0) {
codes.remove(codes.size()-1);
showCode();
return true;
}
return false;
}
});
}
/**
* 显示输入的验证码
*/
private void showCode(){
String code1 = "";
String code2 = "";
String code3 = "";
String code4 = "";
if(codes.size() =1){
code1 = codes.get(0);
}
if(codes.size() =2){
code2 = codes.get(1);
}
if(codes.size() =3){
code3 = codes.get(2);
}
if(codes.size() =4){
code4 = codes.get(3);
}
tv_code1.setText(code1);
tv_code2.setText(code2);
tv_code3.setText(code3);
tv_code4.setText(code4);    
setColor();//设置高亮颜色
callBack();//回调
}
/**
* 设置高亮颜色
*/
private void setColor(){
int color_default = Color.parseColor("#999999");
int color_focus = Color.parseColor("#3F8EED");
v1.setBackgroundColor(color_default);
v2.setBackgroundColor(color_default);
v3.setBackgroundColor(color_default);
v4.setBackgroundColor(color_default);
if(codes.size()==0){
v1.setBackgroundColor(color_focus);
}
if(codes.size()==1){
v2.setBackgroundColor(color_focus);
}
if(codes.size()==2){
v3.setBackgroundColor(color_focus);
}
if(codes.size() =3){
v4.setBackgroundColor(color_focus);
}
}
/**
* 回调
*/
private void callBack(){
if(onInputListener==null){
return;
}
if(codes.size()==4){
onInputListener.onSucess(getPhoneCode());
}else{
onInputListener.onInput();
}
}
//定义回调
public interface OnInputListener{
void onSucess(String code);
void onInput();
}
private OnInputListener onInputListener;
public void setOnInputListener(OnInputListener onInputListener){
this.onInputListener = onInputListener;
}
/**
* 显示键盘
*/
public void showSoftInput(){
//显示软键盘
if(imm!=null && et_code!=null) {
et_code.postDelayed(new Runnable() {
@Override
public void run() {
imm.showSoftInput(et_code, 0);
}
},200);
}
}
/**
* 获得手机号验证码
* @return 验证码
*/
public String getPhoneCode(){
StringBuilder sb = new StringBuilder();
for (String code : codes) {
sb.append(code);
}
return sb.toString();
}
}

codes 集合,用于存放用户输入的所有数字。使用该集合,可简化输入框、文本关联逻辑和事件之间处理; showSoftInput方法:显示输入键盘,可被外界调用; getPhoneCode方法:获得用户输入的验证码,可被外界调用; OnInputListener接口:定义的数值输入回调,用于告诉调用者是输入中,还是输入完成;

调用者 MainActivity

布局文件

代码语言:javascript复制
<?xml version="1.0" encoding="utf-8"? 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@ id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="iwangzhe.customview2.MainActivity" 
<iwangzhe.customview2.phonecode.PhoneCode
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@ id/pc_1"
android:layout_below="@ id/fpc_1"
android:layout_marginTop="40dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"/ 
</RelativeLayout 

代码

代码语言:javascript复制
package iwangzhe.customview2;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import iwangzhe.customview2.phonecode.PhoneCode;
public class MainActivity extends AppCompatActivity {
PhoneCode pc_1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pc_1 = (PhoneCode) findViewById(R.id.pc_1);
//注册事件回调(可写,可不写)
pc_1.setOnInputListener(new PhoneCode.OnInputListener() {
@Override
public void onSucess(String code) {
//TODO:
}
@Override
public void onInput() {
//TODO:
}
});
}
private void test(){
//获得验证码
String phoneCode = pc_1.getPhoneCode();
}
}

总结:

此控件实现起来,很简单,代码量也非常少。

本文章,主要是为了让大家了解自定义控件的过程,如果想在自己的项目中使用,请根据需要自行调整优化。

以上就是本文的全部内容,希望对大家的学习有所帮助。

0 人点赞