class VerificationCodeView : View {
val mContext: Context
var enableStr: String
var disableStr: String
var totalCountdownTime: Int
var mTickCount: Long = 0
var enablColor: Int
var disablColor: Int
var boardColor: Int
var showBoardColor = false
var mEnable = true
var textsize: Float = 0F
var padding: Float = 0F
var strokeWidth: Float = 0F
var radiusSize: Float = 0F
var fatherHeight: Float = 0F
var fatherWidth: Float = 0F
lateinit var fatherRectF: RectF
lateinit var textPaint: TextPaint
lateinit var boardPaint: Paint
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context, attrs, defStyleAttr
) {
mContext = context
val a = context.obtainStyledAttributes(attrs, R.styleable.VerificationCodeView)
totalCountdownTime = a.getInteger(R.styleable.VerificationCodeView_totalCountdownTime, 60)
textsize = a.getDimension(R.styleable.VerificationCodeView_codeTextSize, dp2px(14f))
showBoardColor = a.getBoolean(R.styleable.VerificationCodeView_showBoardColor, false)
mEnable = a.getBoolean(R.styleable.VerificationCodeView_enable, true)
enableStr = a.getString(R.styleable.VerificationCodeView_enableStr) ?: mContext.getString(
com.guide.strings.R.string.userinfo_get_code
)
disableStr = a.getString(R.styleable.VerificationCodeView_disableStr) ?: "%dS"
enablColor = a.getColor(
R.styleable.VerificationCodeView_enableColor, ContextCompat.getColor(
mContext, color.lib_common_selected
)
)
disablColor = a.getColor(
R.styleable.VerificationCodeView_disableColor, ContextCompat.getColor(
mContext, color.lib_common_color_dd
)
)
boardColor = a.getColor(
R.styleable.VerificationCodeView_boardColor, ContextCompat.getColor(
mContext, color.lib_common_selected
)
)
a.recycle()
init()
}
private fun init() {
setWillNotDraw(false)
padding = dp2px(40F)
strokeWidth = dp2px(1F)
radiusSize = dp2px(3F)
initPaint()
}
private fun initPaint() {
textPaint = TextPaint()
textPaint.color = enablColor
textPaint.textSize = textsize
textPaint.style = Paint.Style.FILL
boardPaint = Paint()
boardPaint.color = boardColor
boardPaint.isAntiAlias = true
boardPaint.style = Paint.Style.STROKE
boardPaint.strokeWidth = strokeWidth
fatherWidth = getTextW(textPaint, enableStr) + padding
fatherHeight = getTextH(textPaint, enableStr) + padding / 2
}
/**
* 开启倒计时
*/
fun startCountdown() {
mViewCountDownTimer?.cancel()
mViewCountDownTimer = ViewCountDownTimer(totalCountdownTime * 1000L, 1000L)
mViewCountDownTimer?.start()
}
fun cancelCountdown() {
mViewCountDownTimer?.cancel()
mViewCountDownTimer = null
}
override fun onDetachedFromWindow() {
cancelCountdown()
super.onDetachedFromWindow()
}
var mViewCountDownTimer: ViewCountDownTimer? = null
inner class ViewCountDownTimer(millisInFuture: Long, countDownInterval: Long) :
CountDownTimer(millisInFuture, countDownInterval) {
override fun onTick(p0: Long) {
mTickCount = p0 / 1000
mEnable = false
postInvalidate()
}
override fun onFinish() {
mEnable = true
postInvalidate()
}
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
if (mEnable) {
if (isTouch) {
textPaint.color = disablColor
boardPaint.color = disablColor
} else {
textPaint.color = enablColor
boardPaint.color = enablColor
}
if (showBoardColor) {
canvas?.drawRoundRect(fatherRectF, radiusSize, radiusSize, boardPaint)
}
canvas?.drawText(
enableStr,
(fatherWidth - getTextW(textPaint, enableStr)) / 2,
(getTextH(textPaint, enableStr) + fatherHeight) / 2,
textPaint
)
} else {
textPaint.color = disablColor
boardPaint.color = disablColor
if (showBoardColor) {
canvas?.drawRoundRect(fatherRectF, radiusSize, radiusSize, boardPaint)
}
val format = String.format(disableStr, mTickCount)
canvas?.drawText(
format,
(fatherWidth - getTextW(textPaint, format)) / 2,
(getTextH(textPaint, format) + fatherHeight) / 2,
textPaint
)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
//不能超过父布局大小
if (fatherWidth > widthSize) {
fatherWidth = widthSize.toFloat()
}
fatherRectF = RectF(0F, 0F, fatherWidth, fatherHeight)
Log.d("ssss", "widthMode " + widthMode)
Log.d("ssss", "widthSize " + widthSize)
Log.d("ssss", "heightSize " + heightSize)
setMeasuredDimension(fatherWidth.toInt(), fatherHeight.toInt())
}
private fun dp2px(dpValue: Float): Float {
val scale = Resources.getSystem().displayMetrics.density
return dpValue * scale + 0.5f
}
private fun getTextH(pFont: TextPaint, text: String): Int {
val rect = Rect()
pFont.getTextBounds(text, 0, text.length, rect)
return rect.height()
}
private fun getTextW(pFont: TextPaint, text: String): Float {
return pFont.measureText(text)
}
var isTouch = false
var lastAction: Int = -1
var actionDownTime: Long = 0
var actionUpTime: Long = 0L
var clickSpaceTime: Long = 1000L
var lastClickTime: Long = 0L
public interface OnClickListener {
fun onClick()
}
var mOnClickListener: OnClickListener? = null
override fun onTouchEvent(event: MotionEvent): Boolean {
if (MotionEvent.ACTION_DOWN == event.action) {
actionDownTime = System.currentTimeMillis()
lastAction = MotionEvent.ACTION_DOWN
isTouch = true
invalidate()
} else if (MotionEvent.ACTION_MOVE == event.action) {
lastAction = MotionEvent.ACTION_MOVE
isTouch = true
invalidate()
} else if (MotionEvent.ACTION_UP == event.action) {
lastAction = MotionEvent.ACTION_UP
actionUpTime = System.currentTimeMillis()
val canClickSpace = actionUpTime - lastClickTime > clickSpaceTime
val isClick = actionUpTime - actionDownTime < clickSpaceTime
if (mEnable && canClickSpace && isClick) {
lastClickTime = actionUpTime
mOnClickListener?.onClick()
}
isTouch = false
invalidate()
}
return true
}
}
<declare-styleable name="VerificationCodeView">
<attr name="enable" format="boolean" />
<attr name="showBoardColor" format="boolean" />
<attr name="disableColor" format="color|reference" />
<attr name="enableColor" format="color|reference" />
<attr name="backColor" format="color|reference" />
<attr name="boardColor" format="color|reference" />
<attr name="totalCountdownTime" format="integer" />
<attr name="enableStr" format="reference" />
<attr name="disableStr" format="reference" />
<attr name="codeTextSize" format="reference" />
</declare-styleable>
id | 功能 |
---|---|
enable | 可用标志 |
showBoardColor | 是否展示边框 |
disableColor | 不可用颜色 |
enableColor | 可用颜色 |
backColor | 背景色 |
boardColor | 边框颜色 |
totalCountdownTime | 倒计时总时长 |
enableStr | 可用字符串 |
disableStr | 不可用字符串 |
codeTextSize | 文字大小 |
<com.guide.userinfo.widget.VerificationCodeView
android:id="@+id/tv_get_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/lib_common_20dp"
app:codeTextSize="@dimen/lib_common_12sp"
app:disableColor="@color/lib_common_gray_999"
app:disableStr="@string/userinfo_num"
app:enable="true"
app:enableStr="@string/userinfo_get_code"
app:layout_constraintBottom_toBottomOf="@+id/mobile_code"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/mobile_code"
app:showBoardColor="false"
app:totalCountdownTime="60" />
手动开启倒计时
mViewBinding.tvGetCode.mOnClickListener = object : VerificationCodeView.OnClickListener {
override fun onClick() {
//发送验证码成功
mViewBinding.tvGetCode.startCountdown()
}
}