自定义控件(二)验证码控件

准备工作:
在value文件夹下创建attrs.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="titleText" format="string" />
    <attr name="titleTextColor" format="color" />
    <attr name="titleTextSize" format="dimension" />

    <!--声明可以修改的,在控件进行xml初始化的时候可以使用-->
    <declare-styleable name="AuthCodeView">
        <attr name="titleText" />
        <attr name="titleTextColor" />
        <attr name="titleTextSize" />
    </declare-styleable>
</resources>

在value文件夹下string.xml文件添加:

<resources>
    <string name="app_name">MyViewTest</string>
    <string name="text1">点击验证码,换一张</string>
    <string name="text2">输入验证码</string>
    <string name="text3">验证</string>
</resources>

在主activity UI下添加控件,如下:

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20dp">

        <com.example.myviewtest.AuthCodeView
            android:id="@+id/AuthCodeView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            authcodeview:titleText="3712"
            authcodeview:titleTextColor="#00ffff"
            authcodeview:titleTextSize="40sp" />

        <TextView
            android:layout_margin="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/text1"
            />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_margin="20dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/text2" />
        <EditText
            android:id="@+id/edit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="number" >
            <!--<requestFocus />会获得焦点-->
            <requestFocus />
        </EditText>
    </LinearLayout>

    <Button
        android:id="@+id/button"
        android:layout_margin="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/text3" />
</LinearLayout>

在这里插入图片描述

准备工作完毕

添加自定义文件AuthCodeView.kt

class AuthCodeView : View{

    // 点数设置
    val POINT_NUM = 100

    // 线段数设置
    val LINE_NUM = 2

    //文本
    private var mTitleText: String? = null

    // 文本的颜色
    private var mTitleTextColor = 0

    // 文本的大小
    private var mTitleTextSize = 0
    var mCheckNum = arrayOfNulls<String>(4)
    var random: Random = Random()

    //绘制时控制文本绘制的范围
    private var mBound: Rect? = null  //边界
    private var mPaint: Paint? = null  //油漆

@JvmOverloads constructor(context: Context?,  attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(
        context,
        attrs,
        defStyleAttr
    ){
        /*获取样式化属性
      * 第一个参数:属性集
      * 第二个参数:可变的样式,即控件的参数
      * 第三个参数:定义样式属性
      * 第四个参数:定义样式
      * */
        val tyA = context?.theme?.obtainStyledAttributes(attrs
            ,R.styleable.AuthCodeView,0,0)
        val i = tyA?.indexCount
        for (n in 0 until i!!){
            when(val attr= tyA.getIndex(n)){
                R.styleable.AuthCodeView_titleText->{
                    mTitleText = tyA.getString(attr)
                }
                R.styleable.AuthCodeView_titleTextColor->{
                    // 默认颜色设置为黑色
                    mTitleTextColor = tyA.getColor(attr, Color.BLACK)
                }
                R.styleable.AuthCodeView_titleTextSize->{
                    mTitleTextSize = tyA.getDimensionPixelSize(
                        attr, TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 16f, resources.displayMetrics
                        ).toInt()
                    )
                }
            }
        }
        //回收
        tyA.recycle()
        //随机设置验证码
        mTitleText = randomText()
        mTitleText = randomText()
        //获得绘制文本的宽和高
        mPaint = Paint()
        //设置字体大小
        mPaint!!.textSize = mTitleTextSize.toFloat()
        //创建一个矩形
        mBound = Rect()
        //获取文本边界
        mPaint!!.getTextBounds(mTitleText, 0, mTitleText!!.length, mBound)

        this.setOnClickListener(OnClickListener {
            mTitleText = randomText()
            //页面刷新
            postInvalidate()
        })
    }

    private fun randomText(): String? {
        val sbReturn = StringBuffer()
        for (i in 0..3) {
            val sb = StringBuffer()
            val randomInt = random.nextInt(10)
            mCheckNum[i] = sb.append(randomInt).toString()
            sbReturn.append(randomInt)
        }
        return sbReturn.toString()
    }

    //获取验证码
    fun getAuthCode(): String? {
        return mTitleText
    }

    //重写这个方法
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        var width = 0
        var height = 0
        /**
         * 设置宽度
         */
        var specMode = MeasureSpec.getMode(widthMeasureSpec)  //规范模式
        var specSize = MeasureSpec.getSize(widthMeasureSpec)

        when (specMode) {
            //指定好确定的宽度
            MeasureSpec.EXACTLY -> width = paddingLeft + paddingRight + specSize
            //最大的
            MeasureSpec.AT_MOST -> width = paddingLeft + paddingRight + mBound!!.width()
            //未指明的
            MeasureSpec.UNSPECIFIED -> {
                TODO()
            }
        }

        /**
         * 设置高度
         */
        specMode = MeasureSpec.getMode(heightMeasureSpec)  //规范模式
        specSize = MeasureSpec.getSize(heightMeasureSpec)
        when (specMode) {
            MeasureSpec.EXACTLY -> height = paddingTop + paddingBottom + specSize
            MeasureSpec.AT_MOST -> height = paddingTop + paddingBottom + mBound!!.height()
            MeasureSpec.UNSPECIFIED -> {
                TODO()
            }
        }
        //这个方法决定了当前View的大小
        setMeasuredDimension(width, height)
    }

    override fun onDraw(canvas: Canvas?) {
        //画背景颜色
        mPaint!!.color = Color.BLUE
        canvas!!.drawRect(
            0F, 0F,
            measuredWidth.toFloat(),
            measuredHeight.toFloat(),
            mPaint!!
        )

        //划线
        mPaint!!.color = mTitleTextColor
        var line: IntArray
        for (i in 0 until LINE_NUM) {
            //设置线宽
            mPaint!!.strokeWidth = 5f
            line = getLine(measuredHeight, measuredWidth)
            canvas.drawLine(
                line[0].toFloat(),
                line[1].toFloat(),
                line[2].toFloat(),
                line[3].toFloat(),
                mPaint!!
            )
        }
        
         // 绘制小圆点
        var point: IntArray
        var randomInt: Int
        for (i in 0 until POINT_NUM) {
            //随机获取点的大小
            randomInt = random.nextInt(5)
            point = getPoint(measuredHeight, measuredWidth)
            canvas.drawCircle(
                point[0].toFloat(),
                point[1].toFloat(),
                randomInt.toFloat(),
                mPaint!!
            )
        }

        //绘制验证控件上的文本
        var dx = 20
        for (i in 0..3) {
            canvas.drawText(
                "" + mCheckNum[i],
                dx.toFloat(),
                (height / 2 + getPositon(mBound!!.height() / 2)).toFloat(),
                mPaint!!
            )
            dx += width / 2 - mBound!!.width() / 2 + i / 5 + 20
        }
    }

    // 随机产生点的圆心点坐标
    private fun getPoint(measuredHeight: Int, measuredWidth: Int): IntArray {
        val tempCheckNum = intArrayOf(0, 0, 0, 0)
        tempCheckNum[0] = (Math.random() * measuredWidth).toInt()
        tempCheckNum[1] = (Math.random() * measuredHeight).toInt()
        return tempCheckNum
    }

    private fun getPositon(i: Int): Byte {
        var tempPositoin = (Math.random() * i).toInt()
        if (tempPositoin < 20) {
            tempPositoin += 20
        }
        return tempPositoin.toByte()
    }

    //随机产生划线的起始点坐标和结束点坐标
    private fun getLine(measuredHeight: Int, measuredWidth: Int): IntArray {
        var tempCheckNum =intArrayOf( 0, 0, 0, 0)
        for (i in 0..3 step 2){
            tempCheckNum[i] = (Math.random() * measuredWidth).toInt()
            tempCheckNum[i + 1] = (Math.random() * measuredHeight).toInt()
        }
        return tempCheckNum
    }
}

特别注意:

在Kotlin中@JvmOverloads注解的作用就是:在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。

最后应用自定义控件,并监听验证监听事件:

class MainActivity : AppCompatActivity() {

    private var mauthCodeView: AuthCodeView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)



        button.setOnClickListener(View.OnClickListener {
            val codeString = edit.text.toString().trim()
            mauthCodeView=AuthCodeView
            //获取验证码
            if (codeString == mauthCodeView!!.getAuthCode()) {
                Toast.makeText(this, "验证码验证正确!", Toast.LENGTH_LONG).show();
            }else {
                Toast.makeText(this, "验证码错误!", Toast.LENGTH_LONG).show();
            }
        })
    }
}

这个是我参考的资料:
@JvmOverloads的作用

优秀的自定义控件

简书上的优秀文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值