700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Android自定义九宫格密码解锁

Android自定义九宫格密码解锁

时间:2019-06-02 14:14:48

相关推荐

Android自定义九宫格密码解锁

最终效果

相关代码

布局文件

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.crystal.view.LockPatternViewandroid:id="@+id/lockPattern"android:layout_width="match_parent"android:layout_height="match_parent" /></androidx.constraintlayout.widget.ConstraintLayout>

LockPatternActivity

class LockPatternActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_lock_pattern)val context = thisval lockPattern = findViewById<LockPatternView>(R.id.lockPattern)lockPattern.setDefaultPassWord("0124678")lockPattern.setLockPatternCallBack(object : LockPatternView.LockPatternCallBack {override fun checkSuccess() {Toast.makeText(context, "password check success!", Toast.LENGTH_SHORT).show()}override fun checkError() {Toast.makeText(context,"password check error!",Toast.LENGTH_SHORT).show()}})}}

自定义LockPatternView

package com.crystal.viewimport android.content.Contextimport android.graphics.Canvasimport android.graphics.Paintimport android.graphics.Paint.Styleimport android.graphics.Pathimport android.graphics.Rectimport android.util.AttributeSetimport android.util.Logimport android.view.MotionEventimport android.view.View/*** 九宫格锁屏控件* on /10/28*/class LockPatternView : View {/*** 绘制默认状态下画笔*/private lateinit var normalPaint: Paint/*** 绘制选中状态画笔*/private lateinit var pressPaint: Paint/*** 绘制错误状态画笔*/private lateinit var errorPaint: Paint/*** 绘制箭头画笔*/private lateinit var arrowPaint: Paint/*** 绘制连线画笔*/private lateinit var linePaint: Paint/*** 绘制选中状态下外圆颜色*/private val outerPressColor = 0xff8cbad8.toInt()/*** 绘制选中状态下内圆颜色*/private val innerPressColor = 0xff0596f6.toInt()/*** 绘制默认状态下外圆颜色*/private val outerNormalColor = 0xffd9d9d9.toInt()/*** 绘制默认状态下内圆颜色*/private val innerNormalColor = 0xff929292.toInt()/*** 绘制错误状态下外圆颜色*/private val outerErrorColor = 0xff901032.toInt()/*** 绘制错误状态下内圆颜色*/private val innerErrorColor = 0xff901032.toInt()/*** 当前密码*/private var setPassword: String? = null/*** 二维数组*/private var points: Array<Array<Point?>> = Array(3) {Array(3) {null }}/*** 外圆的半径*/private var outerRadius: Float = 0f/*** 按下的时候 是否是按在一个点上*/private var isTouchPoint = false/*** 选中的点集合*/private var selectPoints = ArrayList<Point>()/*** 是否已初始化*/private var isInit: Boolean = falseconstructor(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)private fun initPaint() {normalPaint = Paint()normalPaint.isAntiAlias = truenormalPaint.style = Style.STROKEnormalPaint.strokeWidth = outerRadius / 9pressPaint = Paint()pressPaint.isAntiAlias = truepressPaint.style = Style.STROKEpressPaint.strokeWidth = outerRadius / 6errorPaint = Paint()errorPaint.isAntiAlias = trueerrorPaint.style = Style.STROKEerrorPaint.strokeWidth = outerRadius / 6arrowPaint = Paint()arrowPaint.isAntiAlias = truearrowPaint.style = Style.FILLarrowPaint.color = innerPressColorlinePaint = Paint()linePaint.isAntiAlias = truelinePaint.style = Style.STROKElinePaint.color = innerPressColorlinePaint.strokeWidth = outerRadius / 9}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)if (!isInit) {//避免多次初始化isInit = trueinitPoints()initPaint()}}private fun initPoints() {//九宫格布局为match_parent,计算显示位置距离屏幕上方距离offset,同时需要考虑横屏情况var width = this.measuredWidthvar height = this.measuredHeightvar offsetX = 0fvar offsetY = 0fif (height > width) {//当前为竖屏offsetY = (height - width) / 2fheight = width} else {offsetX = (width - height) / 2f//设置九宫格宽度和高度一致width = height}//每一个方块的宽以及高val squareWidth = width / 3//则第一个中心点的坐标为【offsetX + squareWidth /2 ,offsetY+ squareWidth /2】 后续坐标依次类推points[0][0] = Point(offsetX + squareWidth / 2, offsetY + squareWidth / 2, 0)points[0][1] = Point(offsetX + squareWidth * 3 / 2, offsetY + squareWidth / 2, 1)points[0][2] = Point(offsetX + squareWidth * 5 / 2, offsetY + squareWidth / 2, 2)points[1][0] = Point(offsetX + squareWidth / 2, offsetY + squareWidth * 3 / 2, 3)points[1][1] = Point(offsetX + squareWidth * 3 / 2, offsetY + squareWidth * 3 / 2, 4)points[1][2] = Point(offsetX + squareWidth * 5 / 2, offsetY + squareWidth * 3 / 2, 5)points[2][0] = Point(offsetX + squareWidth / 2, offsetY + squareWidth * 5 / 2, 6)points[2][1] = Point(offsetX + squareWidth * 3 / 2, offsetY + squareWidth * 5 / 2, 7)points[2][2] = Point(offsetX + squareWidth * 5 / 2, offsetY + squareWidth * 5 / 2, 8)/*** 外圆的大小为宽度的1/12*/outerRadius = width / 12f}override fun onDraw(canvas: Canvas) {for (i in 0..2) {for (point in points[i]) {if (point!!.isNormalStatus()) {//普通状态 绘制外圆normalPaint.color = outerNormalColorcanvas.drawCircle(point.centerX, point.centerY, outerRadius, normalPaint)//普通状态 绘制内圆normalPaint.color = innerNormalColorcanvas.drawCircle(point.centerX, point.centerY, outerRadius / 6, normalPaint)}if (point!!.isPressStatus()) {// 选中状态 绘制外圆pressPaint.color = outerPressColorcanvas.drawCircle(point.centerX, point.centerY, outerRadius, pressPaint)//选中状态 绘制内圆pressPaint.color = innerPressColorcanvas.drawCircle(point.centerX, point.centerY, outerRadius / 6, pressPaint)}if (point!!.isErrorStatus()) {// 错误状态 绘制外圆errorPaint.color = outerErrorColorcanvas.drawCircle(point.centerX, point.centerY, outerRadius, errorPaint)//选中状态 绘制内圆errorPaint.color = innerErrorColorcanvas.drawCircle(point.centerX, point.centerY, outerRadius / 6, errorPaint)}}}//绘制两个点之间的连线以及箭头drawLineAndArrow(canvas)}private fun drawLineAndArrow(canvas: Canvas) {if (selectPoints.size < 1) {//没有选中的点,清除画布重绘canvas.save()canvas.clipRect(Rect(0, 0, width, height))canvas.restore()return}var lastPoint = selectPoints[0]for (i in 1 until selectPoints.size) {//两个点之间绘制一条线drawLine(lastPoint, selectPoints[i], canvas, linePaint)//两个点之间绘制一个箭头drawArrow(canvas, arrowPaint, lastPoint, selectPoints[i], outerRadius / 5.0, 38)lastPoint = selectPoints[i]}//绘制最后一个点到手指当前位置的连线,同时如果手指在圆内就不需要绘制val isInnerPoint =MathUtil.checkInRound(lastPoint.centerX,lastPoint.centerY,outerRadius / 4,moveX,moveY)if (!isInnerPoint && isTouchPoint) {drawLine(lastPoint, Point(moveX, moveY, -1), canvas, linePaint)}}private fun drawArrow(canvas: Canvas,arrowPaint: Paint,start: LockPatternView.Point,end: LockPatternView.Point,arrowHeight: Double,angle: Int) {val d = MathUtil.distance(start.centerX.toDouble(),start.centerY.toDouble(),end.centerX.toDouble(),end.centerY.toDouble())val sinB = ((end.centerX - start.centerX) / d).toFloat()val cosB = ((end.centerY - start.centerY) / d).toFloat()val tanA = Math.tan(Math.toRadians(angle.toDouble())).toFloat()val h = (d - arrowHeight - outerRadius * 1.1).toFloat()val l = arrowHeight * tanAval a = l * sinBval b = l * cosBval x0 = h * sinBval y0 = h * cosBval x1 = start.centerX + (h + arrowHeight) * sinBval y1 = start.centerY + (h + arrowHeight) * cosBval x2 = start.centerX + x0 - bval y2 = start.centerY + y0 + aval x3 = start.centerX + x0 + bval y3 = start.centerY + y0 - aval path = Path()path.moveTo(x1.toFloat(), y1.toFloat())path.lineTo(x2.toFloat(), y2.toFloat())path.lineTo(x3.toFloat(), y3.toFloat())path.close()canvas.drawPath(path, arrowPaint)}/*** 绘制连线*/private fun drawLine(startPoint: LockPatternView.Point,endPoint: LockPatternView.Point,canvas: Canvas,linePaint: Paint) {//两个点之间的距离val pointDistance = MathUtil.distance(startPoint.centerX.toDouble(),startPoint.centerY.toDouble(),endPoint.centerX.toDouble(),endPoint.centerY.toDouble())// 圆心距离val dx = endPoint.centerX - startPoint.centerXval dy = endPoint.centerY - startPoint.centerY// rx = sin@ * 内圆半径val rx = (dx / pointDistance * outerRadius / 6.0).toFloat()// ry = cos@ * 内圆半径val ry = (dy / pointDistance * outerRadius / 6.0).toFloat()canvas.drawLine(startPoint.centerX + rx,startPoint.centerY + ry,endPoint.centerX - rx,endPoint.centerY - ry,linePaint)}//手指按下的位置private var moveX = 0fprivate var moveY = 0foverride fun onTouchEvent(event: MotionEvent): Boolean {moveX = event.xmoveY = event.ywhen (event.action) {MotionEvent.ACTION_DOWN -> {//判断手指是否是在9个点里面if (point != null) {isTouchPoint = trueselectPoints.add(point!!)point!!.setStatusPress()}}MotionEvent.ACTION_MOVE -> {if (isTouchPoint) {//按下的时候一定要先选中一个点,手指不断移动的时候,不断去找其他的点if (point != null) {if (!selectPoints.contains(point)) {selectPoints.add(point!!)}point!!.setStatusPress()}}}MotionEvent.ACTION_UP -> {isTouchPoint = false//判断密码是否与设置密码一致checkPasswordRight()}}invalidate()return true}/*** 检查密码是否正确*/private fun checkPasswordRight() {Log.e("TAG", "${getSelectPassword()} --> $setPassword")if (getSelectPassword() == setPassword) {lockPatternCallBack?.checkSuccess()} else {lockPatternCallBack?.checkError()for (i in 0 until selectPoints.size) {selectPoints[i].setStatusError()}invalidate()delayResetStatus()}}/*** 延迟重置状态*/private fun delayResetStatus() {handler.postDelayed({for (i in 0 until selectPoints.size) {selectPoints[i].setStatusNormal()}selectPoints.clear()invalidate()}, 1000)}/*** 获取按下的点*/private val point: Point?get() {for (i in 0..2) {for (point in points[i]) {//for循环 判断手指是否在9个点里面if (MathUtil.checkInRound(point!!.centerX, point!!.centerY, outerRadius, moveX, moveY)) {return point}}}return null}/*** 各个圆心坐标点* @centerX 中心点横坐标* @centerY 中心点纵坐标* index 点对应的下标 用于后续密码校验*/private inner class Point(var centerX: Float, var centerY: Float, var index: Int) {//默认状态private val STATUS_NORMAL = 1//选中状态private val STATUS_PRESS = 2//错误状态private val STATUS_ERROR = 3private var status = STATUS_NORMAL/*** 设置为选中状态*/fun setStatusPress() {status = STATUS_PRESS}/*** 设置为选中状态*/fun setStatusNormal() {status = STATUS_NORMAL}/*** 设置为选中状态*/fun setStatusError() {status = STATUS_ERROR}/*** 是否为选中状态*/fun isPressStatus(): Boolean {return status == STATUS_PRESS}fun isNormalStatus(): Boolean {return status == STATUS_NORMAL}fun isErrorStatus(): Boolean {return status == STATUS_ERROR}}/*** 设置默认密码*/fun setDefaultPassWord(password: String) {setPassword = password}/*** 获取选中的密码*/private fun getSelectPassword(): String {var pass = ""for (i in 0 until selectPoints.size) {pass += selectPoints[i].index}return pass}private var lockPatternCallBack: LockPatternCallBack? = nullfun setLockPatternCallBack(lockPatternCallBack: LockPatternCallBack) {this.lockPatternCallBack = lockPatternCallBack}/*** 密码检查回调*/interface LockPatternCallBack {fun checkSuccess()fun checkError()}}

工具类

package com.crystal.view;public class MathUtil {/*** * @param x1* @param y1* @param x2* @param y2* @return*/public static double distance(double x1, double y1, double x2, double y2) {return Math.sqrt(Math.abs(x1 - x2) * Math.abs(x1 - x2)+ Math.abs(y1 - y2) * Math.abs(y1 - y2));}/*** * @param x* @param y* @return*/public static double pointTotoDegrees(double x, double y) {return Math.toDegrees(Math.atan2(x, y));}public static boolean checkInRound(float sx, float sy, float r, float x,float y) {// x的平方 + y的平方 开根号 < 半径return Math.sqrt((sx - x) * (sx - x) + (sy - y) * (sy - y)) < r;}}

总结

实现九宫格密码解锁效果,进一步学习了事件分发的处理,自定义View的能力得到进一步的提升。

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。