欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

自定义自带下划线的EditText输入框

程序员文章站 2022-06-01 11:04:02
...

先看看效果

自定义自带下划线的EditText输入框

 

 图片中间就是实现的输入框样式了,输入框的个数,每个输入框之间的间距,输入文字和底部线条的颜色都可以动态改变,接下来就是撸代码的时刻了

1.现在values文件夹下创建attrs.xml ,复制样式

 <declare-styleable name="VerifyEditText">
        <!--验证码的个数-->
        <attr name="totalCount" format="integer" />
        <!--  每个验证码的间隔间距-->
        <attr name="intervalLength" format="integer" />
        <!--底部的线的颜色-->
        <attr name="lineColor" format="color" />
        <!--输入框文字的颜色-->
        <attr name="textColor" format="color" />
    </declare-styleable>

2.接下来写一个类继承EditText

/**
 * Created by ytt on 2018/7/27.
 * 自定义验证码输入框
 */
public class VerifyEditText extends android.support.v7.widget.AppCompatEditText {
    /**
     * 本控件的宽高
     */
    private int width;
    private int height;
    private OnVerifyInputCompleteListener listener;
    //TODO 保存输入的验证码集合
    private StringBuffer sb = new StringBuffer();
    //底部的线 画笔
    private Paint mLinePaint;
    //文字的画笔
    private Paint mTextPaint;
    //计算出的每一个输入框的宽度
    private int textLineLength;

    //验证码的数量
    private int totalCount;
    //每一个验证码之间的距离
    private int intervalLength;
    //底部线的颜色
    private int lineColor;
    //输入文字的颜色
    private int textColor;

    public VerifyEditText(Context context) {
        this(context, null);
    }

    public VerifyEditText(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.editTextStyle);
    }

    public VerifyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.VerifyEditText, defStyleAttr, 0);
        totalCount = typedArray.getInt(R.styleable.VerifyEditText_totalCount, 4);
        intervalLength = typedArray.getInt(R.styleable.VerifyEditText_intervalLength, 30);
        lineColor = typedArray.getColor(R.styleable.VerifyEditText_lineColor, getColor(R.color.textE6));
        textColor = typedArray.getColor(R.styleable.VerifyEditText_textColor, getColor(R.color.text3));
        typedArray.recycle();
        initView();
    }

    private void initView() {
        setBackground(null);
        setMaxLines(1);
        setLines(1);
        /**
         * 必须重写setOnTouchListener 自己处理触摸事件,否则光标可以移动
         * 必须重写setOnLongClickListener 否则长按可以复制验证码
         * 不然光标就可以移动啦~~
         */
        setOnLongClickListener(new OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                return true;
            }
        });
        setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.showSoftInput(VerifyEditText.this, 0);
                return true;
            }
        });
        //TODO 设置输入的字体透明 不可见
        setTextColor(getColor(android.R.color.transparent));

        mLinePaint = new Paint();
        mLinePaint.setColor(lineColor);
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStrokeWidth(3);

        mTextPaint = new Paint();
        mTextPaint.setTextSize(sp2px(22));
        mTextPaint.setFakeBoldText(true);
        mTextPaint.setColor(textColor);
        mTextPaint.setAntiAlias(true);
        //TODO 增加输入字符的监听
        addTextChangedListener(textWatcher);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        //每一个输入文字的宽度
        textLineLength = (width - (totalCount - 1) * intervalLength) / totalCount;
        //定位光标的初始显示位置
        setPadding(textLineLength / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (totalCount <= 0) {
            return;
        }
        int currentLength = 0;
        //TODO 绘制文字
        for (int i = 0; i < totalCount; i++) {
            String textString = String.valueOf((sb != null && sb.length() > i) ? sb.charAt(i) : "");
            canvas.drawText(textString, currentLength + textLineLength / 2 - mTextPaint.measureText(textString) / 2, height / 2 + mTextPaint.getTextSize() / 2, mTextPaint);
            currentLength += textLineLength + intervalLength;
        }
        //TODO 绘制文字底部的线
        currentLength = 0;
        for (int i = 0; i < totalCount; i++) {
            canvas.drawLine(currentLength, height - 3, textLineLength * (i + 1) + intervalLength * i, height - 3, mLinePaint);
            currentLength += textLineLength + intervalLength;
        }
    }

    private TextWatcher textWatcher = new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            sb.delete(0, sb.length());
            if (!TextUtils.isEmpty(s.toString())) {
                //TODO 只能输入指定totalCount个数的字多出的需要删除
                if (s.toString().length() > totalCount) {
                    s.delete(totalCount, s.length());
                    return;
                }
                sb.append(s);
                if (s.toString().length() == totalCount && listener != null) {
                    listener.onCompleteInput(sb.toString());
                }

            }
            int paddingLeft;
            /**
             * 计算验证码输入之后光标显示的位置
             * 如果已经输完验证码则光标需要显示在最后一个字符的后面而不是下一个文字输入框
             * */
            if (sb.length() < totalCount) {
                paddingLeft = (int) ((textLineLength + intervalLength) * sb.length()
                        + textLineLength / 2 - getPaint().measureText(!TextUtils.isEmpty(sb.toString()) ? sb.toString() : ""));

            } else {
                paddingLeft = (int) ((textLineLength + intervalLength) * (sb.length() - 1)
                        + (mTextPaint.measureText(sb.substring(sb.length() - 2, sb.length() - 1)) / 2)
                        + textLineLength / 2 - getPaint().measureText(!TextUtils.isEmpty(sb.toString()) ? sb.toString() : ""));
            }
            setPadding(paddingLeft, getPaddingTop(), getPaddingRight(), getPaddingBottom());
        }
    };

    /**
     * 设置输入完成监听
     */
    public void setOnVerifyInputCompleteListener(OnVerifyInputCompleteListener listener) {
        this.listener = listener;
    }

    /**
     * 获取输入的验证码
     */
    public String getVerifyCode() {
        return sb.toString();
    }

    public int getColor(int id) {
        return ContextCompat.getColor(getContext(), id);
    }

    public int sp2px(float spValue) {
        final float fontScale = getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }
}

 3.使用VerifyEditText

 <cn.test.view.VerifyEditText
        android:id="@+id/verifyEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        android:layout_marginRight="50dp"
        android:inputType="text" />

xml中显示的样式自定义自带下划线的EditText输入框

 遇到的问题总结:

1.输入之后因为是根据计算输入的个数给光标设置padding所以光标才可以显示在相应位置,但是输入的文字只是设置成透明色,其实是存在的,这样的话光标就可以点击长按全选复制。。所以必须重写ontouch和onlongclick事件,ontouch事件只允许调起键盘不允许进行其他操作,代码自动实现的操作详情可以查看TextView中的OnTouch事件

2.输入文字之后可以换行,这个吧需要设置lines

其他的暂时也没什么,代码直接拷贝就可以用,噢啦