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一下代码,就可以了)

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

(0)

相关推荐

  • Android高仿微信支付密码输入控件

    像微信支付密码控件,在app中是一个多么司空见惯的功能.最近,项目需要这个功能,于是乎就实现这个功能. 老样子,投篮需要找准角度,变成需要理清思路.对于这个"小而美"的控件,我们思路应该这样子. Ⅰ.将要输入密码数量动态通过代码加载出来. Ⅱ.利用Gridview模拟产生一个输入数字键盘,并且按照习惯从屏幕底部弹出来. Ⅲ.对输入数字键盘进行事件监听,将这个输入数字填入到这个密码框中,并且当您输入密码长度一致的时候,进行事件回调. 这个思维导图应该是这样的: 首先,我们要根据需求动态加

  • Android 带清除功能的输入框控件实例详解

    Android 带清除功能的输入框控件实例详解 今天,看到一个很好的自定义输入框控件,于是记录一下. 效果很好: 一,自定义一个类,名为ClearEditText package com.example.clearedittext; import android.content.Context; import android.graphics.drawable.Drawable; import android.text.Editable; import android.text.TextWatc

  • Android用户输入自动提示控件AutoCompleteTextView使用方法

    一.简介 1.AutoCompleteTextView的作用 2.AutoCompleteTextView的类结构图 也就是拥有EditText的各种功能 3.AutoCompleteTextView工作原理 AutoCompleteTextView的自动提示功能肯定需要适配器提供数据 4.Android里的适配器 5.适合AutoCompleteTextView的适配器 ArrayAdapter 二.AutoCompleteTextView实现自动提示的方法 1)AutoCompleteTex

  • Android输入框控件ClearEditText实现清除功能

    本文给大家带来一个很实用的小控件ClearEditText,就是在Android系统的输入框右边加入一个小图标,点击小图标可以清除输入框里面的内容,IOS上面直接设置某个属性就可以实现这一功能,但是Android原生EditText不具备此功能,所以要想实现这一功能我们需要重写EditText,接下来就带大家来实现这一小小的功能 我们知道,我们可以为我们的输入框在上下左右设置图片,所以我们可以利用属性android:drawableRight设置我们的删除小图标,如图 我这里设置了左边和右边的图

  • Android如何禁止向EditText控件中输入内容详解

    前言 在Android开发中经常会遇到EditText控件,而在App开发过程中.遇到了这样一个问题.那就是Android EditText控件如何禁止往里面输入内容? 最开始找到修改版解决方法.但是当想输入的时候就有问题了.可以参考一下.但不建议这样写 EditText editText = (EditText) findViewById(R.id.editText1); editText.setKeyListener(null); 看到这个问题大家可能有点奇怪了.EditText的功能不就是

  • Android WebView控件捕获用户输入的信息

    WebView可所谓是Android中最强大的控件之一,无所不能. 于是有这么一个需求,用户在app之中内嵌的WebView中输入帐号密码的时候,App需要捕获已经输入的帐号密码. 当用户输入帐号密码,一般情况下会进行页面转跳,在页面转跳之前执行js脚本,通过js脚本来获取这个帐号密码的value值.要先获取各个元素的class值,需要解析整个html页面,那么我们可以重写 onLoadResource 这个方法,代码如下: webview.setWebViewClient(new WebVie

  • Android自定义view实现输入控件

    本文实例为大家分享了Android自定义view实现输入控件的具体代码,供大家参考,具体内容如下 网络上大部分的输入控件都是多个EditText组合而成,本例中采用的是: 单个EditText作为输入的捕捉控件 多个ImageView的子类作为显示的控件,绘制EditText中的数据 如上图: 输入前和输入后输入框需要发生响应的改变 点击自定义控件要弹出软键盘 EditText数据捕捉,以及EditView不能操作(如果可以操作,数据处理会混乱) 输完后会得到相应的提示 ImageView的子类

  • Android带清除功能的输入框控件EditTextWithDel

    记录下一个很实用的小控件EditTextWithDel,就是在Android系统的输入框右边加入一个小图标,点击小图标可以清除输入框里面的内容,由于Android原生EditText不具备此功能,所以要想实现这一功能我们需要重写EditText. 效果图如下: 主要的思路就是为右边的图片设置监听,点击右边的图片清除输入框的内容并隐藏删除图标,因为我们不能直接给EditText设置点击事件,所以我们用记住我们按下的位置来模拟点击事件,用输入框的的onTouchEvent()方法来模拟. packa

  • Android实现EditText控件禁止输入内容的方法(附测试demo)

    本文实例讲述了Android实现EditText控件禁止输入内容的方法.分享给大家供大家参考,具体如下: 问题: android如何实现EditText控件禁止往里面输入内容? 修改版解决方法: EditText editText = (EditText) findViewById(R.id.editText1); editText.setKeyListener(null); 看到这个问题大家可能有点奇怪了,EditText的功能不就是往上面写入内容吗? 再者,如果真要禁止输入文本,在布局文件中

  • Android开发中给EditText控件添加TextWatcher监听实现对输入字数的限制(推荐)

    做这个功能是因为开发项目的时候,由于后台接口的一些参数的值的长度有要求,不能超过多少个字符,所以在编辑框中输入的字符是要有限制的. 下面就来看一下demo的实现过程: 首先,在xml控件中放置一个EditText控件,然后初始化该控件并对该控件添加文本监听.xml自己简单的设计一下,代码较为简单,直接上代码: package com.example.edittext; import android.app.Activity; import android.os.Bundle; import an

随机推荐