Android 自定义View 之 简易输入框

news2024/12/22 22:40:41

简易输入框

  • 前言
  • 正文
      • ① 构造方法
      • ② XML样式
      • ③ 测量
      • ④ 绘制
        • 1. 绘制方框
        • 2. 绘制文字
      • ⑤ 输入
        • 1. 键盘布局
        • 2. 键盘接口
        • 3. 键盘弹窗
        • 4. 显示键盘
        • 5. 相关API
    • 四、使用自定义View
    • 五、源码

前言

  在日常工作开发中,我们时长会遇到各种各样的需求,不部分需求是可以通过Android 原生的View来解决,而有一些是无法解决的,这时候我们就需要自定义View,我们先来看看本文中这个自定义View的演示效果图。

在这里插入图片描述

正文

  依然在EasyView中进行创建,在com.easy.view下新建一个EasyEditText,继承自View ,实现里面的构造方法。

① 构造方法

然后我们继承自View,重写里面的构造方法,代码如下:

public class EasyEditText extends View implements NumberKeyboardListener {

    private Context mContext;
    
    public EasyEditText(Context context) {
        this(context, null);
    }

    public EasyEditText(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public EasyEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
		mContext = context;
    }
}

下面就可以增加样式了。

② XML样式

  在设置样式之前需要先知道我们的自定义View要做什么,这是一个简易输入框,可以用来做验证码输入或者密码输入,输入框4~6个,主要就是数字输入,在attrs.xml文件中,里面我们可以写自定义的样式,代码如下所示:

    <!--简易输入框-->
    <declare-styleable name="EasyEditText">
        <!-- 方框大小,宽高一致 -->
        <attr name="boxWidth" />
        <!-- 方框背景颜色 -->
        <attr name="boxBackgroundColor" />
        <!-- 方框描边颜色 -->
        <attr name="boxStrokeColor" />
        <!-- 方框焦点描边颜色 -->
        <attr name="boxFocusStrokeColor" format="color|reference"/>
        <!-- 方框描边宽度 -->
        <attr name="boxStrokeWidth" />
        <!--文字颜色-->
        <attr name="textColor" />
        <!--文字大小-->
        <attr name="textSize" />
        <!-- 方框数量 4-6 个-->
        <attr name="boxNum" format="integer"/>
        <!--是否密文-->
        <attr name="ciphertext" format="boolean"/>
    </declare-styleable>

  因为在此之前我还有三个View的样式,所以重复的样式就抽离了出来,可以参见源码了解。

下面我们回到View中去使用,先声明变量,代码如下:

    /**
     * 方框大小,因为它是相同的宽度和高度,它只需要一个值
     */
    private int mBoxWidth;
    /**
     * 方框背景颜色
     */
    private int mBoxBackgroundColor;
    /**
     * 方框默认描边颜色
     */
    private int mBoxStrokeColor;
    /**
     * 方框获取焦点描点颜色
     */
    private int mBoxFocusStrokeColor;
    /**
     * 方框描边大小
     */
    private final int mBoxStrokeWidth;
    /**
     * 文字颜色
     */
    private int mTextColor;
    /**
     * 文字大小
     */
    private float mTextSize;
    /**
     * 方框数量,最少4个 - 最多6个
     */
    private int mBoxNum;
    /**
     * 方框之间的间距
     */
    private int mBoxMargin = 4;
    /**
     * 方框画笔
     */
    private Paint mBoxPaint;
    /**
     * 方框描边画笔
     */
    private Paint mBoxStrokePaint;
    /**
     * 文字画笔
     */
    private Paint mTextPaint;
    /**
     * 文字矩形
     */
    private final Rect mTextRect = new Rect();
    /**
     * 方框圆角
     */
    private float mBoxCornerRadius = 8f;
    /**
     * 描边圆角
     */
    private float strokeRadius;

    /**
     * 输入长度
     */
    private final int mInputLength;
    /**
     * 输入数组
     */
    private final String[] inputArray;
    /**
     * 当前输入位置
     */
    private int currentInputPosition = 0;
    /**
     * 焦点边框列表
     */
    private final List<RectF> focusList = new ArrayList<>();
    /**
     * 是否获取焦点
     */
    private boolean isFocus = false;
    /**
     * 是否密文显示
     */
    private boolean ciphertext = false;
    /**
     * 密文显示 *
     */
    private String ciphertextContent = "*";

然后修改第三个构造函数,代码如下所示:

    public EasyEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        @SuppressLint("CustomViewStyleable")
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.EasyEditText);
        mBoxWidth = (int) typedArray.getDimensionPixelSize(R.styleable.EasyEditText_boxWidth, 48);
        mBoxBackgroundColor = typedArray.getColor(R.styleable.EasyEditText_boxBackgroundColor, ContextCompat.getColor(context, R.color.white));
        mBoxStrokeColor = typedArray.getColor(R.styleable.EasyEditText_boxStrokeColor, ContextCompat.getColor(context, R.color.box_default_stroke_color));
        mBoxFocusStrokeColor = typedArray.getColor(R.styleable.EasyEditText_boxFocusStrokeColor, ContextCompat.getColor(context, R.color.box_default_stroke_color));
        mBoxStrokeWidth = (int) typedArray.getDimensionPixelSize(R.styleable.EasyEditText_boxStrokeWidth, 2);
        mTextColor = typedArray.getColor(R.styleable.EasyEditText_textColor, ContextCompat.getColor(context, R.color.tx_default_color));
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.EasyEditText_textSize, (int) TypedValue
                .applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
        int number = typedArray.getInteger(R.styleable.EasyEditText_boxNum, 4);
        ciphertext = typedArray.getBoolean(R.styleable.EasyEditText_ciphertext, false);
        mBoxNum = (number > 6 || number < 4) ? 4 : number;
        mInputLength = mBoxNum;
        inputArray = new String[mInputLength];
        typedArray.recycle();
        //初始化画笔
        initPaint();
    }

  这里通过EasyEditText得到TypedArray,通过TypedArray获取EasyEditText中的属性,然后进行赋值,需要注意的是这里对于输入框的大小处理,输入框个数就是输入的长度,然后初始化画笔,新增initPaint()方法,代码如下:

    private void initPaint() {
        //设置边框画笔
        mBoxPaint = new Paint();
        mBoxPaint.setAntiAlias(true);// anti-aliasing
        mBoxPaint.setColor(mBoxBackgroundColor);//Set color
        mBoxPaint.setStyle(Paint.Style.FILL);//Style filling
        //设置描边画笔
        mBoxStrokePaint = new Paint();
        mBoxStrokePaint.setAntiAlias(true);
        mBoxStrokePaint.setColor(mBoxStrokeColor);
        mBoxStrokePaint.setStyle(Paint.Style.STROKE);//Style stroke
        mBoxStrokePaint.setStrokeWidth(mBoxStrokeWidth);//Stroke width
        //设置文字画笔
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);//Text size
        mTextPaint.setTextAlign(Paint.Align.CENTER);//Center the text
    }

③ 测量

  我们通过变量和属性得知了方框个数,和方框间的间距,然后我们重写onMeasure()方法,代码如下:

    /**
     * View的测量
     *
     * @param widthMeasureSpec  宽度测量
     * @param heightMeasureSpec 高度测量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = 0;
        int margin = EasyUtils.dp2px(mContext, mBoxMargin);
        switch (MeasureSpec.getMode(widthMeasureSpec)) {
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:   //wrap_content
                width = mBoxWidth * mBoxNum + margin * (mBoxNum - 1);
                break;
            case MeasureSpec.EXACTLY:   //match_parent
                width = MeasureSpec.getSize(widthMeasureSpec);
                mBoxWidth = (width - margin * (mBoxNum - 1)) / mBoxNum;
                break;
        }
        //设置测量后的值
        setMeasuredDimension(width, mBoxWidth);
    }

④ 绘制

  测量好了之后,下面就可以开始绘制了,绘制就相当于在纸上画画,而画画呢,首先要有画笔,首先声明变量,代码如下:

    private Paint mBoxPaint;
    private Paint mBoxStrokePaint;
    private Paint mTextPaint;
    private final Rect mTextRect = new Rect();

然后我们需要对3个画笔(方框、方框边框、文字)进行设置,因为绘制文字稍微有一些不同,所以加了一个Rect,下面我们在View中新增一个初始化画笔的方法,代码如下所示:

    /**
     * 初始化画笔
     */
    private void initPaint() {
        //设置方框画笔
        mBoxPaint = new Paint();
        mBoxPaint.setAntiAlias(true);// 抗锯齿
        mBoxPaint.setColor(mBoxBackgroundColor);//设置颜色
        mBoxPaint.setStyle(Paint.Style.FILL);//风格填满
        //设置方框描边画笔
        mBoxStrokePaint = new Paint();
        mBoxStrokePaint.setAntiAlias(true);
        mBoxStrokePaint.setColor(mBoxStrokeColor);
        mBoxStrokePaint.setStyle(Paint.Style.STROKE);//风格描边
        mBoxStrokePaint.setStrokeWidth(mBoxStrokeWidth);//描边宽度
        //设置文字画笔
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);//文字大小
        mTextPaint.setTextAlign(Paint.Align.CENTER);//文字居中对齐
    }

然后在第三个构造方法中去调用,如下图所示:

在这里插入图片描述

下面要进行绘制了。

    @Override
    protected void onDraw(Canvas canvas) {
        //绘制边框
        drawBox(canvas);
        //绘制文字
        drawText(canvas);
    }

这里绘制分为两步,绘制方框和绘制文字。

1. 绘制方框

  首先是绘制方框,在自定义View中新增一个drawBox()方法,代码如下:

    /**
     * 绘制方框
     */
    private void drawBox(Canvas canvas) {
        //每个方框的间距
        int margin = EasyUtils.dp2px(mContext, mBoxMargin);
        int radius = EasyUtils.dp2px(mContext, mBoxCornerRadius);
        //Draw a rounded rectangle border
        float strokeWidth = mBoxStrokeWidth / 2;
        for (int i = 0; i < mBoxNum; i++) {
            //To draw a rectangular box, you need the positions of the left, top, right and bottom points
            float left = i * mBoxWidth + i * margin;
            float top = 0f;
            float right = (i + 1) * mBoxWidth + i * margin;
            float bottom = mBoxWidth;
            RectF rectF = new RectF(left, top, right, bottom);
            //画一个圆角矩形框
            canvas.drawRoundRect(rectF, radius, radius, mBoxPaint);
            RectF strokeRectF = new RectF(left + strokeWidth, top + strokeWidth, right - strokeWidth, bottom - strokeWidth);
            //添加到列表
            focusList.add(strokeRectF);
        }

        for (int i = 0; i < mBoxNum; i++) {
            strokeRadius = radius - strokeWidth;
            //根据当前绘制位置和是否获取焦点设置画笔颜色
            if (i <= currentInputPosition && isFocus) {
                mBoxStrokePaint.setColor(mBoxFocusStrokeColor);
            } else {
                mBoxStrokePaint.setColor(mBoxStrokeColor);
            }
            //绘制边框
            canvas.drawRoundRect(focusList.get(i), strokeRadius, strokeRadius, mBoxStrokePaint);
        }
    }

  这里首先绘制圆角背景,然后通过绘制背景的参数得到绘制的边框,将边框保存起来,再根据当前绘制位置和是否获取焦点设置画笔颜色,这样就可以实现当前输入框的颜色变化,比如我没有输入的时候是黑色边框,当我输入第一个值的时候,第一个输入框的边框颜色变成绿色,输入完第一个,第二个框的边框变成绿色,表示你应该输入第二个框了,这样可以友好的提示用户。

2. 绘制文字

  现在方框有了,而文字绘制我们需要绘制在方框的中间,自定义View中新增一个drawText()方法。

    /**
     * 绘制文字
     */
    private void drawText(Canvas canvas) {
        int boxMargin = EasyUtils.dp2px(mContext, mBoxMargin);
        for (int i = 0; i < inputArray.length; i++) {
            if (inputArray[i] != null) {
                //绘制的文字
                String content = ciphertext ? ciphertextContent : inputArray[i];
                //获取绘制的文字边界
                mTextPaint.getTextBounds(content, 0, content.length(), mTextRect);
                //绘制的位置
                int offset = (mTextRect.top + mTextRect.bottom) / 2;
                //绘制文字,需要确定起始点的X、Y的坐标点
                float x = (float) (getPaddingLeft() + mBoxWidth * i + boxMargin * i + mBoxWidth / 2);
                float y = (float) (getPaddingTop() + mBoxWidth / 2) - offset;
                //绘制文字
                canvas.drawText(content, x, y, mTextPaint);
            }
        }
    }

绘制文字和之前的MacAddressEditText中的操作差不多,我就不重复说了,下面我们来看输入。

⑤ 输入

  绘制的处理已经完成了,简易地址输入框我们之前说了只输入数字,输入的数据就是:0、1、2、3、4、5、6、7、8、9,那么为了方便,我打算自己做一个键盘来进行输入。

1. 键盘布局

  首先在layout下创建一个lay_number_keyboard.xml,用于作为键盘的布局,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#eff4f9">

    <Button
        android:id="@+id/btn_1"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_1"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toStartOf="@+id/btn_2"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btn_2"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="4dp"
        android:layout_marginLeft="4dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_2"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toStartOf="@+id/btn_3"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btn_1"
        app:layout_constraintTop_toTopOf="@+id/btn_1" />

    <Button
        android:id="@+id/btn_3"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="4dp"
        android:layout_marginLeft="4dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_3"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btn_2"
        app:layout_constraintTop_toTopOf="@+id/btn_2" />

    <Button
        android:id="@+id/btn_4"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginTop="4dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_4"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toStartOf="@+id/btn_5"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@+id/btn_1"
        app:layout_constraintTop_toBottomOf="@+id/btn_1" />

    <Button
        android:id="@+id/btn_5"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="4dp"
        android:layout_marginLeft="4dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_5"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toStartOf="@+id/btn_6"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btn_4"
        app:layout_constraintTop_toTopOf="@+id/btn_4" />

    <Button
        android:id="@+id/btn_6"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="4dp"
        android:layout_marginLeft="4dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_6"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btn_5"
        app:layout_constraintTop_toTopOf="@+id/btn_5" />

    <Button
        android:id="@+id/btn_7"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginTop="4dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_7"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toStartOf="@+id/btn_8"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@+id/btn_4"
        app:layout_constraintTop_toBottomOf="@+id/btn_4" />

    <Button
        android:id="@+id/btn_8"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="4dp"
        android:layout_marginLeft="4dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_8"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toStartOf="@+id/btn_9"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btn_7"
        app:layout_constraintTop_toTopOf="@+id/btn_7" />

    <Button
        android:id="@+id/btn_9"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="4dp"
        android:layout_marginLeft="4dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_9"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="@+id/btn_6"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btn_8"
        app:layout_constraintTop_toTopOf="@+id/btn_8" />

    <Button
        android:id="@+id/btn_del"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginTop="4dp"
        android:layout_marginBottom="8dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/delete"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btn_0"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="@+id/btn_7"
        app:layout_constraintTop_toBottomOf="@+id/btn_7" />

    <Button
        android:id="@+id/btn_0"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="4dp"
        android:layout_marginLeft="4dp"
        android:backgroundTint="@color/key_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/num_0"
        android:textColor="@color/key_tx_color"
        android:textSize="16sp"
        app:layout_constraintEnd_toStartOf="@+id/btn_complete"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btn_del"

        app:layout_constraintTop_toTopOf="@+id/btn_del" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btn_complete"
        android:layout_width="0dp"
        android:layout_height="48dp"
        android:layout_marginStart="4dp"
        android:layout_marginLeft="4dp"
        android:backgroundTint="@color/key_complete_bg_color"
        android:insetTop="0dp"
        android:insetBottom="0dp"
        android:text="@string/complete"
        android:textColor="@color/white"
        android:textSize="16sp"
        app:iconGravity="start|end"
        app:layout_constraintEnd_toEndOf="@+id/btn_9"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/btn_0"
        app:layout_constraintTop_toTopOf="@+id/btn_0" />

</androidx.constraintlayout.widget.ConstraintLayout>

布局的预览效果如下图所示:

在这里插入图片描述

  这个布局从使用上来说就很简单了,基本上一目了然,这里我们可以写一个接口用来处理键盘上按钮点击的事件。

2. 键盘接口

  在com.llw.easyview下新建一个NumberKeyboardListener接口,代码如下所示:

public interface NumberKeyboardListener {

    /**
     * 数字字符
     * @param num 0~9
     */
    void onNum(String num);

    /**
     * 删除
     */
    void onDelete();

    /**
     * 完成
     */
    void onComplete();

    /**
     * 弹窗关闭
     */
    void onDialogDismiss();

    /**
     * 弹窗显示
     */
    void onDialogShow();
}

  现在接口有了,接口中的方法基本上覆盖了键盘上所有按钮点击时触发的事件处理,然后是处理弹窗关闭和显示,为什么要有这个关闭和显示呢,因为要获取是否输入的状态,下面我们来写一个弹窗,用来点击简易输入框时弹出这个键盘。

3. 键盘弹窗

  这个弹窗,我就写在EasyUtils类中了,在里面新增如下方法代码:

    /**
     * 显示数字键盘弹窗
     *
     * @param listener 数字键盘监听
     */
    public static void showNumKeyboardDialog(@NonNull Context context, @NonNull NumberKeyboardListener listener) {
        BottomSheetDialog dialog = new BottomSheetDialog(context);
        //从xml中获取布局视图
        View view = LayoutInflater.from(context).inflate(R.layout.lay_number_keyboard, null, false);
        //单击按钮触发接口回调
        view.findViewById(R.id.btn_0).setOnClickListener(v -> listener.onNum("0"));
        view.findViewById(R.id.btn_1).setOnClickListener(v -> listener.onNum("1"));
        view.findViewById(R.id.btn_2).setOnClickListener(v -> listener.onNum("2"));
        view.findViewById(R.id.btn_3).setOnClickListener(v -> listener.onNum("3"));
        view.findViewById(R.id.btn_4).setOnClickListener(v -> listener.onNum("4"));
        view.findViewById(R.id.btn_5).setOnClickListener(v -> listener.onNum("5"));
        view.findViewById(R.id.btn_6).setOnClickListener(v -> listener.onNum("6"));
        view.findViewById(R.id.btn_7).setOnClickListener(v -> listener.onNum("7"));
        view.findViewById(R.id.btn_8).setOnClickListener(v -> listener.onNum("8"));
        view.findViewById(R.id.btn_9).setOnClickListener(v -> listener.onNum("9"));
        view.findViewById(R.id.btn_del).setOnClickListener(v -> listener.onDelete());
        view.findViewById(R.id.btn_complete).setOnClickListener(v -> {
            listener.onComplete();
            dialog.dismiss();
        });
        //点击外面消失
        dialog.setCancelable(true);
        //设置内容视图
        dialog.setContentView(view);
        if (dialog.getWindow() != null) {
            //设置弹出窗口背景透明
            WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
            params.dimAmount = 0.0f;
            dialog.getWindow().setAttributes(params);
        }
        dialog.setOnShowListener(dialog1 -> listener.onDialogShow());
        dialog.setOnCancelListener(dialog12 -> listener.onDialogDismiss());
        dialog.setOnDismissListener(dialog13 -> listener.onDialogDismiss());
        dialog.show();
    }

  这里就是一个底部弹窗,然后设置布局视图,设置接口回调,设置背景透明,最后显示出来。那么下一步要做的就是点击输入框调用这个弹窗显示键盘。

4. 显示键盘

  在View中是可以获取到点击触摸事件的,那么我们可以在自定义View中新增如下代码:

    /**
     * 触摸事件
     */
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event != null) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                //显示数字键盘
                EasyUtils.showNumKeyboardDialog(mContext, this);
                return true;
            }
        }
        return super.onTouchEvent(event);
    }

  这里的代码就是当我们的手机点击这个简易输入框的时候,会先触发触摸事件,然后才是点击事件,而在这里我们就是在触摸到的时候显示键盘弹窗,然后返回 true,这里就会进行事件的拦截,这里的这个this,就是我们当前的自定义View需要实现的回调接口,将鼠标放在这个this后面,然后Alt + Enter的组合键,会出现弹窗,如下图所示:

在这里插入图片描述

  这里点击第四项,会出现一个弹窗,勾选上所有的方法,然后点OK就可以快速实现这个接口的回调,重写接口的方法,你会看到自定义View新增了5个方法,代码如下:

    @Override
    public void onNum(String num) {
        if (currentInputPosition == mInputLength) return;
        inputArray[currentInputPosition] = num;
        currentInputPosition++;
        //Refresh View
        postInvalidate();
    }

    @Override
    public void onDelete() {
        if (currentInputPosition == 0) return;
        currentInputPosition--;
        inputArray[currentInputPosition] = null;
        //Refresh View
        postInvalidate();
    }

    @Override
    public void onComplete() {
        Log.d("TAG", "onComplete: " + getText());
    }

    @Override
    public void onDialogShow() {
        isFocus = true;
        postInvalidate();
    }

    @Override
    public void onDialogDismiss() {
        isFocus = false;
        postInvalidate();
    }

  当调用postInvalidate()方法时会重新进行绘制,则按照之前的逻辑就行了。

5. 相关API

  下面再增加几个可供代码调用的API方法,如下所示:

    /**
     * 设置输入框个数
     */
    public void setBoxNum(int num) {
        if (num < 4 || num > 6) {
            throw new IllegalArgumentException("The number of input boxes ranges from 4 to 6");
        }
        mBoxNum = num;
    }

    /**
     * 获取输入总长度
     */
    public int getBoxNum() {
        return mBoxNum;
    }

    /**
     * 设置是否密文
     * @param flag true 密文、false 明文
     */
    public void setCiphertext(boolean flag) {
        ciphertext = flag;
        postInvalidate();
    }

    /**
     * 设置密文时显示的内容
     * @param content 密文内容,默认是 *
     */
    public void setCiphertextContent(String content) {
        if (content == null) return;
        if (content.isEmpty()) return;
        if (content.length() > 1) return;
        ciphertextContent = content;
    }

    /**
     * 获取输入内容
     */
    public String getText() {
        StringBuilder builder = new StringBuilder();
        for (String number : inputArray) {
            if (number == null) continue;
            if (number.isEmpty()) continue;
            builder.append(number);
        }
        return builder.toString();
    }

四、使用自定义View

   关于使用,我在些这个文章的时候这个自定义View已经加入到仓库中了,可以通过引入依赖的方式,例如在app模块中使用,则打开app模块下的build.gradle,在dependencies{}闭包下添加即可,之后记得要Sync Now

dependencies {
    implementation 'io.github.lilongweidev:easyview:1.0.3'
}

   或者你在自己的项目中完成了刚才上述的所有步骤,那么你就不用引入依赖了,直接调用就好了,不过要注意更改对应的包名,否则会爆红的。

可以使用了,修改activity_easy_edittext.xml中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".used.EasyEditTextActivity">

    <!--简易输入框-->
    <com.easy.view.EasyEditText
        android:id="@+id/et_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:boxBackgroundColor="@color/white"
        app:boxFocusStrokeColor="@color/green"
        app:boxNum="6"
        app:boxStrokeColor="@color/black"
        app:boxStrokeWidth="2dp"
        app:boxWidth="48dp"
        app:ciphertext="false"
        app:textColor="@color/black"
        app:textSize="16sp" />

    <CheckBox
        android:id="@+id/cb_flag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="明文" />

    <Button
        android:id="@+id/btn_get_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:text="获取内容" />
</LinearLayout>

  如果你发现XML预览不了,看不到这个自定义View,就Rebuild Project一下,就能看到了,预览效果如下图所示:

在这里插入图片描述

  下面进入到EasyEditTextActivity中去使用,修改代码如下所示:

public class EasyEditTextActivity extends EasyActivity<ActivityEasyEdittextBinding> {

    @Override
    protected void onCreate() {
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        binding.cbFlag.setOnCheckedChangeListener((buttonView, isChecked) -> {
            binding.etContent.setCiphertext(isChecked);
            binding.cbFlag.setText(isChecked ? "密文" : "明文");
        });
        //输入框
        binding.btnGetContent.setOnClickListener(v -> {
            String content = binding.etContent.getText();
            if (content.isEmpty()) {
                showMsg("请输入内容");
                return;
            }
            if (content.length() < binding.etContent.getBoxNum()) {
                showMsg("请输入完整内容");
                return;
            }
            showMsg("输入内容为:" + content);
        });
    }
}

  这里的代码就很简单,获取View,然后点击按钮时获取输入框的值,获取到值显示在按钮上,下面运行测试一下。

在这里插入图片描述

五、源码

如果对你有所帮助的话,不妨 Star 或 Fork,山高水长,后会有期~

源码地址:EasyView

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/493427.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在云服务器上部署jupyter服务

1.准备一台云服务器&#xff0c;阿里云、腾讯云都可以&#xff0c;并且远程登陆&#xff0c;在云服务器的安全组中配置8888的访问端口&#xff0c;因为jupyter默认的访问端口是8888&#xff0c;如下述步骤&#xff1b;以阿里云服务器的centos系统为例 2.使用以下命令在服务器…

golang 微服务容错处理是如何做的?

随着微服务的规模越来越大&#xff0c;各个微服务之间可能会存在错综复杂的调用关系 在我们实际工作中&#xff0c;确实慢慢的也出现了很多问题&#xff0c;整个系统的弊端的慢慢的展现出来 例如就会有这样的情况&#xff1a; 服务 A 去请求服务B&#xff0c;服务 B 还需要去…

HTB-Silo

HTB-Silo 信息收集立足root哈希传递攻击 信息收集 分别对smb和rpc都进行guest用户和空密码测试。 1521的Oracle TNS listener 11.2.0.2.0。 搜索可能存在的漏洞。 得到一个CVE编号cve-2012-1675。同时我们可以对其进行SID枚举&#xff0c;SID说简单点就是数据库的名字。 简单…

错题汇总04

1.以下C语言指令&#xff1a; int a[5] {1,3,5,7,9}; int *p (int *)(&a1); printf(“%d,%d”,*(a1)&#xff0c;*(p-1)); 运行结果是什么&#xff1f; A 2,1 B 3,1 C 3,9 D 运行时崩溃 数组名只有在&与sizeof之后&#xff0c;才表明数组本身&#xff0c;其余表…

平均情况时间复杂度

// n表示数组array的长度 int find(int[] array, int n, int x) {int i 0;int pos -1;for (; i < n; i) {if (array[i] x){ pos i; break;}}return pos; } 通过以上代码&#xff0c;我们分析一下平均情况时间复杂度。 以上代码要查找的变量 x 在数组中的位置&#xff…

并发编程02:CompletableFuture

文章目录 2.1 Future接口理论知识2.2 Future接口常用实现类FutureTask异步任务2.2.1 Future接口能干什么2.2.2 Future接口相关架构2.2.3 Future编码实战和优缺点分析2.2.4 完成一些复杂的任务 2.3 CompletableFuture对Future的改进2.3.1 CompletableFuture为什么会出现2.3.2 Co…

Redis持久化篇

文章目录 持久化篇1、AOF持久化是怎么实现的&#xff1f;1.1、AOF日志1.2、三种写回策略1.3、AOF重写机制1.4、AOF后台重写 2、RDB快照是怎么实现的&#xff1f;2.1、快照怎么使用2.2、执行快照时&#xff0c;数据能被修改吗&#xff1f;2.3、RDB和AOF合体 3、Redis大key对持久…

自动驾驶行业观察之2023上海车展-----智驾供应链(2)

传感器供应链发展 图达通&#xff1a;展示长距Lidar“Falcon”&#xff0c;和DeepWay签署定点协议 产品&#xff1a;主视激光雷达 Falcon 猎鹰&#xff08;2023CES曾亮相&#xff09; 核心亮点&#xff1a; • 核心性能&#xff1a;最远探测距离可达 500 米&#xff0c;为智…

《计算机网络—自顶向下方法》 第一章Wireshark实验:Wireshark软件的安装和入门

要深入理解网络协议&#xff0c;需要仔细观察协议实体之间交换的报文序列。为探究协议操作细节&#xff0c;可使协议实体执行某些动作&#xff0c;观察这些动作及其影响。这些任务可以在仿真环境下或在如因特网这样的真实网络环境中完成。观察在正在运行协议实体间交换报文的基…

万字长文详解linux内存管理,值得收藏

一、Linux内存管理概述 Linux内存管理是指对系统内存的分配、释放、映射、管理、交换、压缩等一系列操作的管理。在Linux中&#xff0c;内存被划分为多个区域&#xff0c;每个区域有不同的作用&#xff0c;包括内核空间、用户空间、缓存、交换分区等。Linux内存管理的目标是最…

经典常用的脚本讲解

目录 一&#xff1a;echo 语句 二&#xff1a;while read命令​编辑 三&#xff1a;猴子摘香蕉问题 四:斐波拉切数求前10个数的和 ​五&#xff1a;随机生成8位数的密码 六&#xff1a;二进制转换 &#xff08;1&#xff09;余数倒排法 &#xff08;2&#xff09;减法正…

Google - ISLR 比赛总结

引言 本篇主要想总结一下最近打的GISLR比赛&#xff0c;本来是没想写的&#xff0c;比赛前期感觉赛题很有意思&#xff0c;做了eda以及根据一些base改了改自己的方案&#xff0c;取得了还不错的结果&#xff0c;但因为中途被各种琐事缠身&#xff0c;发生了很多变故&#xff0…

【Linux】Libevent库

Libevent——高性能I/O框架库 底层封装了select&#xff0c;poll&#xff0c;epoll&#xff0c;便于使用 I/O框架库以库函数的形式&#xff0c;封装了较为底层的系统调用&#xff0c;给应用程序提供了一组更便于使用的接口。 特点&#xff1a;1.跨平台&#xff0c;2.统一事件源…

c++面向对象之封装、继承、和多态

一、封装 把客观事物封装成类&#xff0c;而且可以把自己的数据和方法设置为只能让可信的类或者对象操作&#xff0c;对不可信的信息进行隐藏&#xff08;利用public,private,protected,friend)实现 二、继承 2.1类与类的关系 has-a &#xff1a;描述一个类由多个部件类构成…

SpringCloud全面学习笔记之初尝美妙篇

目录 前言初识微服务单体架构分布式架构微服务架构初见SpringCloud微服务治理分布式服务架构案例 微服务组件及使用Eureka注册中心提供者和消费者Eureka的结构和作用搭建Eureka服务注册服务服务发现Eureka注册服务总结 Ribbon负载均衡原理负载均衡原理负载均衡策略懒加载 Nacos…

Qt quick基础2(包含平移旋转放缩以及qml控件大写开头啊)

Qt quick基础2&#xff08;包含平移旋转放缩以及qml控件大写开头啊&#xff09; 目录 Qt quick基础2&#xff08;包含平移旋转放缩以及qml控件大写开头啊&#xff09;前言简单的平移、旋转和放缩其他元素的一些基本使用qml文件作为控件时&#xff0c;务必以大写字母开头命名小结…

力扣题库刷题笔记682-棒球比赛

1、题目如下&#xff1a; 2、个人Python代码实现如下&#xff1a; 代码如下&#xff1a; class Solution: def calPoints(self, operations: List[str]) -> int: i 0 #用于遍历元素的下标 while i < len(operations): …

【Python入门篇】——Python基础语法(数据类型与数据类型转换)

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; Python入门&#xff0c;本专栏主要内容为Python的基础语法&#xff0c;Python中的选择循环语句…

数据结构与算法导学

文章目录 数据结构和算法导学认识数据结构认识算法总结 数据结构和算法导学 程序 数据结构 算法 认识数据结构 什么是数据结构&#xff1f; 数据结构是一门研究计算机中数据存储和数据操作的学科。 为什么要学习数据结构&#xff1f; 学习数据结构能让我们写出更加优秀的代码…

关于在线帮助中心你需要思考以下几个问题

搭建帮助中心是大多数企业都在尝试做的事情&#xff0c;它的重要性对于企业来说不言而喻。现在对于企业来说&#xff0c;搭建帮助中心或许不是什么难事&#xff0c;但是关于帮助中心&#xff0c;有几个问题需要思考清楚&#xff0c;才能让其发挥最大的价值。 一、如何让用户养成…