Android自定义view实现滑动解锁九宫格控件
作者:捡一晌贪欢 发布时间:2021-12-28 02:24:16
标签:Android,滑动解锁,九宫格
前言
上一篇文章用贝塞尔曲线画了一个看起来不错的小红点功能,技术上没什么难度,主要就是数学上的计算。这篇文章也差不多,模仿了一个常用的滑动解锁的九宫格控件。
需求
用过安卓的都知道,用过苹果的也知道,这里就是一个滑动解锁的控件。核心思想如下:
1、摆放九个圆,当手指经过圆附近的时候选取该点,手指移动的时候将选中点连线
2、预设一个正确的连线,当手指抬起时的连线与预设连线一致,验证通过
3、通过layout参数可以设置圆和线的颜色
效果图
这里功能勉强可以吧,感觉选中点的时候不是很流畅。
代码
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.silencefly96.module_common.R
import java.util.*
import kotlin.math.sqrt
/**
* 九宫格控件
*
* @author silence
* @date 2022-11-09
*/
class PatternLockView @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null,
defStyleAttr: Int = 0
): View(context, attributeSet, defStyleAttr){
/**
* 预设值
*/
var preData = LinkedList<Int>()
/**
* 回调接口
*/
var listener: OnMoveUpListener? = null
// 当前值
private var curData = LinkedList<Int>()
// 圆的颜色
private val mCircleColor: Int
// 线的颜色
private val mLineColor: Int
// 圆半径占最短宽高的比例
private val mRadiusPercent: Float
// 两点之间的距离
private var mBetweenLength = 0f
// 第一个圆所在位置
private var mStartX = 0f
private var mStartY = 0f
// 圆半径
private var mRadius = 0f
// 当前手指所在的位置
private var mCurPosX = 0f
private var mCurPosY = 0f
// 是否在移动的状态
private var isMoving = false
// 路径
private var mPath = Path()
// 校验结果, -1失败,0未验证,1验证成功,根据验证结果修改线条颜色
private var mCheckResult = 0
// 画笔
private val mPaint = Paint().apply {
strokeWidth = 5f
style = Paint.Style.STROKE
flags = Paint.ANTI_ALIAS_FLAG
// 连接处样式: 平斜接
strokeJoin = Paint.Join.MITER
strokeMiter = 4f
// 落笔和结束时那点(point)的样式: 添加半圆
strokeCap = Paint.Cap.ROUND
}
init {
// 获取布局参数
val typedArray =
context.obtainStyledAttributes(attributeSet, R.styleable.PatternLockView)
mCircleColor = typedArray.getColor(R.styleable.PatternLockView_circleColor,
Color.LTGRAY)
mLineColor = typedArray.getColor(R.styleable.PatternLockView_lineColor, Color.YELLOW)
mRadiusPercent = typedArray.getFraction(R.styleable.PatternLockView_circleRadiusPercent,
1, 1, 0.05f)
typedArray.recycle()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = getDefaultSize(100, widthMeasureSpec)
val height = getDefaultSize(100, heightMeasureSpec)
// 设置参数
mBetweenLength = (if (width < height) width else height) * 0.25f
mRadius = (if (width < height) width else height) * mRadiusPercent
mStartX = width / 2f - mBetweenLength
mStartY = height / 2f - mBetweenLength
setMeasuredDimension(width, height)
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
when(event.action) {
MotionEvent.ACTION_DOWN -> {
// 清除旧数据
isMoving = true
curData.clear()
mCheckResult = 0
invalidate()
}
MotionEvent.ACTION_MOVE -> {
// 判断是否进入哪个点的范围
val index = getEventCircleIndex(event.x, event.y)
if (index != -1 && !curData.contains(index)) {
curData.add(index)
}
mCurPosX = event.x
mCurPosY = event.y
// 触发绘制
invalidate()
}
MotionEvent.ACTION_UP -> {
isMoving = false
// 判断是否符合设置的值
if (curData == preData) {
mCheckResult = 1
listener?.onMoveUp(true)
}else {
// 没有连线不触发判断
if (curData.size > 1) {
mCheckResult = -1
listener?.onMoveUp(false)
}
}
// 最后更新下,把移动的那部分线条去掉
invalidate()
}
}
return true
}
private fun getEventCircleIndex(x: Float, y: Float): Int {
var curX: Float
var curY: Float
for (i in 0 until 9) {
curX = mStartX + mBetweenLength * (i % 3)
curY = mStartY + mBetweenLength * (i / 3)
if (getDistance(curX, curY, x, y) <= mRadius) {
return i
}
}
return -1
}
private fun getDistance(x1: Float, y1: Float, x2: Float, y2: Float): Float {
// 平方和公式
@Suppress("ReplaceJavaStaticMethodWithKotlinAnalog")
return sqrt((Math.pow((x1 - x2).toDouble(), 2.0)
+ Math.pow((y1 - y2).toDouble(), 2.0)).toFloat())
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 先绘制九个点
var curX: Float
var curY: Float
mPaint.color = mCircleColor
mPaint.style = Paint.Style.FILL
mPaint.strokeWidth = 5f
for (i in 0 until 9) {
curX = mStartX + mBetweenLength * (i % 3)
curY = mStartY + mBetweenLength * (i / 3)
canvas.drawCircle(curX, curY, mRadius, mPaint)
}
// 再绘制线,先画固定的线,再画移动中的线
mPaint.color = when(mCheckResult) {
-1 -> Color.RED
1 -> Color.GREEN
else -> mLineColor
}
mPaint.style = Paint.Style.STROKE
mPaint.strokeWidth = mRadius / 3f
mPath.reset()
for (i in curData) {
// 当前点坐标
curX = mStartX + mBetweenLength * (i % 3)
curY = mStartY + mBetweenLength * (i / 3)
if (curData.indexOf(i) == 0) {
mPath.moveTo(curX, curY)
}else {
mPath.lineTo(curX, curY)
}
}
// 再画最后一点
if (curData.size > 0 && isMoving) {
mPath.lineTo(mCurPosX, mCurPosY)
}
canvas.drawPath(mPath, mPaint)
}
interface OnMoveUpListener{
fun onMoveUp(success: Boolean)
}
}
对应的style文件: pattern_lock_view_style.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PatternLockView">
<attr name="circleColor" format="color"/>
<attr name="lineColor" format="color"/>
<attr name="circleRadiusPercent" format="fraction"/>
</declare-styleable>
</resources>
主要问题
实际上,这里没什么有难度的地方,就是画了九个圆,加上几段根据触发的点构成的连线,就不多写了,可以看代码注释。
来源:https://blog.csdn.net/lfq88/article/details/127776114


猜你喜欢
- 前言随着使用es场景的增多,工作当中避免不了去使用es进行数据的存储,在数据存储到es当中以后就需要使用DSL语句进行数据的查询、聚合等操作
- 当jvm虚拟机被关闭的时候,可能我们需要做一些处理,比如对连接的关闭,或者对一些必要信息的存储等等操作,这里就可以借助于虚拟机提供的钩子函数
- 前言消息推送功能可以说移动APP不可缺少的功能之一,一般简单的推送我们可以使用第三方推送的SDK,比如极光推送、信鸽推送等,但是对于消息聊天
- Android获取分享应用列表详解及实例如果在应用的AndroidManifest.xml中含有 ACTION_SEND 属性,那就证明该应
- 前言之前写的progress其实根本没有起到进度条的作用,太显眼,而且并不好看,所以有了新的想法,我们将ProgressBar控件换成See
- 目录前言:对文章出现的一些名词进行解释一、插入排序1.基本思想2.直接插入排序3.希尔排序(缩小增量排序)二、选择排序1.基本思想2.直接选
- 目录第一种第二种第三种随机数的产生在一些代码中很常用,也是我们必须要掌握的。而java中产生随机数的方法主要有三种:第一种:new Rand
- #region 监视文件夹的变化
- 本文实例为大家分享了Android自定义View实现遥控器按钮的具体代码,供大家参考,具体内容如下效果图:原理:onSizeChanged拿
- 1.查找数据库中表的列名<pre name="code" class="html">St
- 记录web项目部署到阿里云服务器步骤(使用 web项目、阿里云服务器、Xftp、Xshell),敬请参考和指正1.将要部署的项目打包成WAR
- 对象是对类的实例化。对象具有状态和行为,变量用来表明对象的状态,方法表明对象所具有的行为。Java 对象的生命周期包括创建、使用和清除。一、
- 本文实例讲述了C#利用Windows自带gdi32.dll实现抓取屏幕功能,是C#应用程序设计中一个非常实用的功能,现分享给大家供大家参考借
- 代理模式的应用:远程代理,为一个对象在不同的地址空间提供局部代表,可以隐藏一个对象存在于不同地质空间的事实。虚拟代理,根据需要创建开销很大的
- summarydetail传统的Spring项目会有很多的配置文件,比如我们要使用Redis,一般除了对应的依赖的jar包我们还需要在app
- import java.util.HashMap;import java.util.Map;import org.apache.common
- 开场白我本来是一名android开发者,突然就对java后端产生了浓烈的兴趣。所以,立马就转到了后端。第一个项目使用的使用Spring Da
- 对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收;而使用的内存是由JVM控制的。那么,什么
- 本文实例为大家分享了java使用Cookie判断用户登录情况的方法,供大家参考,具体内容如下1.判断是否登录public boolean i
- 一、问题描述换了一台电脑,重新进行idea安装配置。然后打开原来的项目结果引入spring-boot-maven-plugin出现爆红,而且