Android自定义九格宫手势解锁详解

在移动设备上,手势解锁已经成为一种流行的安全措施。在本文中,我们将详细介绍如何使用自定义视图实现手势解锁功能。

1. 创建 GestureLockView 类

首先,我们需要创建一个名为 GestureLockView 的类,继承自 View。这个类将负责绘制手势解锁视图,并处理用户的触摸事件。

class GestureLockView @JvmOverloads constructor(
    context: Context, attrs:
    AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    // 行数
    private val numRows = 3

    // 列数
    private val numColumns = 3

    private val mStrokeWidth = 10.dpToPx

    //画笔
    private val mPaint = Paint().apply {
        isAntiAlias = true
        strokeWidth = mStrokeWidth
        style = Paint.Style.FILL
    }

    //手势路径
    private val mPath = Path()

    //可视化的九宫格点
    private val allPoints: MutableList<PointF> = mutableListOf()

    //可视化的九宫格点
    private val choosePoints: MutableList<PointF> = mutableListOf()

    //圆点的半径
    private var pointRadius = 0f

    //手指最后移动的位置
    private var lastX = 0f
    private var lastY = 0f

    // ...
}

在这段代码中,我们定义了一些私有属性和变量。行数和列数分别为 3,表示手势解锁界面是一个 3x3 的九宫格。mStrokeWidth 表示九宫格线的宽度。mPaint 是用于绘制九宫格的画笔。mPath 是手势路径,用于绘制用户划过的路径。allPoints 是可视化的九宫格点的集合,choosePoints 是用户选择的点的集合。pointRadius 是圆点的半径。lastX 和 lastY 是手指最后一次触摸的位置。

2. 初始化手势解锁视图

接下来,在 onSizeChanged 方法中初始化手势解锁视图。

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    super.onSizeChanged(w, h, oldw, oldh)
    val offset = w / (numRows + 1).toFloat()
    pointRadius = offset / 4f
    for (y in 0 until numRows) {
        for (x in 0 until numColumns) {
            allPoints.add(PointF(offset + offset * x, offset + offset * y))
        }
    }
}

在这段代码中,我们首先计算出九宫格每个点之间的间距 offset,然后根据 offset 计算出圆点的半径 pointRadius。接着使用两个嵌套的循环,将所有的九宫格点添加到 allPoints 集合中。

3. 绘制手势解锁视图

在 onDraw 方法中,我们利用 Canvas 和 Paint 对象来绘制手势解锁视图。

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    allPoints.forEach {
        if (choosePoints.contains(it)) {
            mPaint.color = Color.BLUE
        } else {
            mPaint.color = Color.WHITE
        }
        mPaint.style = Paint.Style.FILL
        mPaint.alpha = 255
        //绘制中心的实心圆
        canvas.drawCircle(it.x, it.y, pointRadius / 2f, mPaint)
        mPaint.alpha = 153
        canvas.drawCircle(it.x, it.y, pointRadius, mPaint)
    }

    mPaint.alpha = 255
    mPath.reset()
    choosePoints.takeIf { it.isNotEmpty() }?.run {
        forEach {
            mPath.takeIf { it.isEmpty }?.run {
                moveTo(it.x, it.y)
            } ?: run {
                mPath.lineTo(it.x, it.y)
            }
        }
        mPath.lineTo(lastX, lastY)
        //绘制路径
        mPaint.color = Color.BLUE
        mPaint.style = Paint.Style.STROKE
        canvas.drawPath(mPath, mPaint)
    }
}

这段代码中,我们先根据用户选择的点决定圆点的颜色,并绘制实心和空心圆。然后,我们使用 mPath 跟踪用户选择的路径,并根据路径绘制手势解锁的路径。最后,我们使用 canvas 的 drawPath 方法将路径绘制到屏幕上。

4. 处理触摸事件

在 onTouchEvent 方法中,我们处理用户的触摸事件,并根据用户的操作更新视图。

@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            lastX = event.x
            lastY = event.y
            allPoints.forEach {
                //触摸的点和九宫格密码圆相交
                if (abs(it.x - lastX) < pointRadius && abs(it.y - lastY) < pointRadius) {
                    choosePoints.add(it)
                }
            }
        }

        MotionEvent.ACTION_MOVE -> {
            lastX = event.x
            lastY = event.y
            allPoints.forEach {
                //触摸的点和九宫格密码圆相交
                if (distanceFromPath(mPath, it.x, it.y) < pointRadius + mStrokeWidth / 2f && choosePoints.contains(it).not()
                    || (mPath.isEmpty && abs(it.x - lastX) < pointRadius && abs(it.y - lastY) < pointRadius)
                ) {
                    choosePoints.add(it)
                    lastX = it.x
                    lastY = it.y
                }
            }
        }

        MotionEvent.ACTION_UP -> {
            choosePoints.clear()
            mPath.reset()
        }
    }
    invalidate()
    return true
}

在这段代码中,我们根据用户的触摸事件类型,更新 choosePoints 和 mPath 对象。在 MotionEvent.ACTION_DOWN 中,我们根据用户触摸的位置判断是否与九宫格的圆点相交,并将相交的圆点添加到 choosePoints 集合中。在 MotionEvent.ACTION_MOVE 中,我们根据用户的触摸位置判断是否与九宫格的圆点相交,并更新 choosePoints 集合和最后触摸的位置 lastX 和 lastY。在 MotionEvent.ACTION_UP 中,我们清空 choosePoints 和 mPath 对象,表示用户完成了一次手势解锁操作。最后,我们调用 invalidate 方法,告诉系统重新绘制视图。

5. 辅助方法

在代码的末尾,我们还定义了一个辅助方法 distanceFromPath,用于计算一个点到路径的最短距离。

private fun distanceFromPath(path: Path, x: Float, y: Float): Float {
    val pathMeasure = PathMeasure(path, false)
    val pos = FloatArray(2)
    val tan = FloatArray(2)
    var distance = Float.MAX_VALUE
    var i = 0f
    while (i <= pathMeasure.length) {
        if (pathMeasure.getPosTan(i, pos, tan)) {
            val dx = pos[0] - x
            val dy = pos[1] - y
            val d = sqrt((dx * dx + dy * dy).toDouble()).toFloat()
            if (d < distance) {
                distance = d
            }
        }
        i += 1f
    }
    return distance
}

这个方法使用 PathMeasure 来测量路径的长度,并逐步遍历路径上的点,计算每个点到给定点的距离。最后返回最短的距离。

总结

在本文中,我们详细解释了如何创建一个自定义的手势解锁视图。我们通过绘制九宫格点和用户选择的路径,实现了手势解锁的功能。希望这篇文章能够帮助你理解并实现自定义视图的过程。如果你对该主题有更多的兴趣,可以进一步探索 Android 的绘图 API 和触摸事件处理机制。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题

图片

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值