准备工作:
在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的作用