Android自定义控件通用验证码输入框的实现
需求
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
<?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
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
布局文件
<?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>
代码
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(); } }
总结:
此控件实现起来,很简单,代码量也非常少。
本文章,主要是为了让大家了解自定义控件的过程,如果想在自己的项目中使用,请根据需要自行调整优化。
Demo下载地址:CustomView2_jb51.rar
(为了减小Demo大小,我删除了build下的文件,大家获取后rebuild一下代码,就可以了)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。