Android自定义View实现九宫格图形解锁(Kotlin版)
作者:一个 狠人 发布时间:2022-04-09 11:10:50
标签:Android,九宫格,解锁
本文实例为大家分享了Android自定义View实现九宫格图形解锁的具体代码,供大家参考,具体内容如下
效果:
代码:
package com.example.kotlin_test
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
/**
* Created by wanglx on 2021/9/8.
*/
class MyLock : View {
private var isInit=false
private var mPoints:Array<Array<Point?>> = Array(3){Array<Point?>(3){null} }
private var mSelectPoints=ArrayList<Point>()
private var isTouch=false
private var code= listOf(0,1,2,5,8)
//画笔
private lateinit var mNormalPaint:Paint
private lateinit var mPressedPaint:Paint
private lateinit var mErrorPaint: Paint
private lateinit var mLinePaint: Paint
//颜色
private val mNormalColor=Color.BLACK
private val mPressedColor=Color.GREEN
private val mErrorColor=Color.RED
private val mLineColor=Color.BLACK
//外圆半径
private var mDotRadius=0
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defaultStyle: Int):super(context,attrs,defaultStyle)
override fun onDraw(canvas: Canvas?) {
//初始化
if (!isInit) {
initDot()
initPaint()
isInit=true
}
//绘制
drawShow(canvas)
}
private fun drawShow(canvas: Canvas?) {
for (i in 0..2) {
for (j in 0..2) {
var point = mPoints[i][j]
when(point!!.status){
PointStatus.NORMAL->{
//先画外圆,再画内圆
canvas!!.drawCircle(point.centerX,point.centerY,
mDotRadius.toFloat(),mNormalPaint)
canvas!!.drawCircle(point.centerX,point.centerY,
mDotRadius.toFloat()/6,mNormalPaint)
}
PointStatus.PRESSED->{
canvas!!.drawCircle(point.centerX,point.centerY,
mDotRadius.toFloat(),mPressedPaint)
canvas!!.drawCircle(point.centerX,point.centerY,
mDotRadius.toFloat()/6,mPressedPaint)
}
PointStatus.ERROR->{
canvas!!.drawCircle(point.centerX,point.centerY,
mDotRadius.toFloat(),mErrorPaint)
canvas!!.drawCircle(point.centerX,point.centerY,
mDotRadius.toFloat()/6,mErrorPaint)
}
}
}
}
//画连线
drawLine(canvas)
}
private fun drawLine(canvas: Canvas?) {
if (mSelectPoints.size > 0) {
var mLastPoint = mSelectPoints[0]
//两点连线
if (mSelectPoints.size > 1) {
for (i in 1..mSelectPoints.size-1) {
var point = mSelectPoints[i]
realDrawLine(mLastPoint, point, canvas, mLinePaint)
mLastPoint=point
}
}
//手指和某个点的连线
var isInner=checkInRound(mLastPoint.centerX,mLastPoint.centerY,movingX,movingY,mDotRadius/6)
if (!isInner&&isTouch) {
realDrawLine(mLastPoint,Point(movingX,movingY,-1),canvas,mLinePaint)
}
}
}
private fun realDrawLine(
mLastPoint: Point,
point: Point,
canvas: Canvas?,
mLinePaint: Paint
) {
//不是从圆心坐标开始画,而是距离圆心有一定的距离
var dx=point.centerX-mLastPoint.centerX
var dy=point.centerY-mLastPoint.centerY
var pointDistance = Math.sqrt((dx * dx + dy * dy).toDouble())
var offsetX = (dx / pointDistance) * (mDotRadius / 6)
var offsetY=(dy/pointDistance)*(mDotRadius/6)
canvas!!.drawLine((mLastPoint.centerX+offsetX).toFloat(),
(mLastPoint.centerY+offsetY).toFloat(),
(point.centerX-offsetX).toFloat(), (point.centerY-offsetY).toFloat(),mLinePaint)
}
private var movingX=0f
private var movingY=0f
override fun onTouchEvent(event: MotionEvent?): Boolean {
movingX=event!!.x
movingY=event.y
when (event.action) {
MotionEvent.ACTION_DOWN->{
for (i in 0..mSelectPoints.size - 1) {
mSelectPoints[i].setStatusNormal()
}
mSelectPoints.clear()
invalidate()
//先判断是不是在圆内
var dd=point
if (dd != null) {
dd.setStatusPressed()
mSelectPoints.add(dd)
isTouch=true
}
}
MotionEvent.ACTION_MOVE->{
//先判断是不是在圆内
var dd=point
if (dd != null) {
dd.setStatusPressed()
if (!mSelectPoints.contains(dd)) {
mSelectPoints.add(dd)
}
}
}
MotionEvent.ACTION_UP->{
isTouch=false
if (mSelectPoints.size == code.size) {
for (i in 0..mSelectPoints.size - 1) {
if (mSelectPoints[i].index != code[i]) {
for (i in 0..mSelectPoints.size - 1) {
//密码不对,设置为错误状态
mSelectPoints[i].setStatusError()
}
break
}
}
} else {
for (i in 0..mSelectPoints.size - 1) {
mSelectPoints[i].setStatusError()
}
}
}
}
invalidate()
return true
}
//扩展属性,遍历九个圆,看手指的按在哪个圆里面
val point:Point?
get() {
for (i in 0..2) {
for (j in 0..2) {
var point = mPoints[i][j]
if (checkInRound(point!!.centerX, point.centerY, movingX, movingY, mDotRadius)) {
return point
}
}
}
return null
}
//判断是不是在圆内
private fun checkInRound(
centerX: Float,
centerY: Float,
movingX: Float,
movingY: Float,
mDotRadius: Int
): Boolean {
var isIn=Math.sqrt(((centerX-movingX)*(centerX-movingX)+(centerY-movingY)*(centerY-movingY)).toDouble())<mDotRadius
return isIn
}
private fun initPaint() {
//正常画笔
mNormalPaint = Paint()
mNormalPaint!!.color=mNormalColor
mNormalPaint.style=Paint.Style.STROKE
mNormalPaint.isAntiAlias=true
mNormalPaint.strokeWidth=mDotRadius.toFloat()/12
//按下画笔
mPressedPaint = Paint()
mPressedPaint!!.color=mPressedColor
mPressedPaint.style=Paint.Style.STROKE
mPressedPaint.isAntiAlias=true
mPressedPaint.strokeWidth=mDotRadius.toFloat()/9
//错误画笔
mErrorPaint = Paint()
mErrorPaint!!.color=mErrorColor
mErrorPaint.style=Paint.Style.STROKE
mErrorPaint.isAntiAlias=true
mErrorPaint.strokeWidth=mDotRadius.toFloat()/12
//连线画笔
mLinePaint = Paint()
mLinePaint!!.color=mLineColor
mLinePaint.style=Paint.Style.STROKE
mLinePaint.isAntiAlias=true
mLinePaint.strokeWidth=mDotRadius.toFloat()/12
}
private fun initDot() {
var width=this.width
var height=this.height
var offsetX=0f//九个宫格为正方形,距离布局左边的距离
var offsetY=0f//九宫格为正方形,距离布局顶部的距离
//兼容横竖屏
if (width > height) {
offsetX = (width - height).toFloat() / 2
width = height
} else {
offsetY = (height - width).toFloat() / 2
}
//每个方格的大小
var squareWidth=width/3
mDotRadius=squareWidth/4
//九个宫格,存于数组Point[3][3]
mPoints[0][0] = Point(squareWidth/2+offsetX,squareWidth/2+offsetY,0)
mPoints[0][1] = Point(squareWidth*3/2+offsetX,squareWidth/2+offsetY,1)
mPoints[0][2] = Point(squareWidth*5/2+offsetX,squareWidth/2+offsetY,2)
mPoints[1][0] = Point(squareWidth/2+offsetX,squareWidth*3/2+offsetY,3)
mPoints[1][1] = Point(squareWidth*3/2+offsetX,squareWidth*3/2+offsetY,4)
mPoints[1][2] = Point(squareWidth*5/2+offsetX,squareWidth*3/2+offsetY,5)
mPoints[2][0] = Point(squareWidth/2+offsetX,squareWidth*5/2+offsetY,6)
mPoints[2][1] = Point(squareWidth*3/2+offsetX,squareWidth*5/2+offsetY,7)
mPoints[2][2] = Point(squareWidth*5/2+offsetX,squareWidth*5/2+offsetY,8)
}
//圆的状态
enum class PointStatus{
NORMAL,PRESSED,ERROR
}
class Point(var centerX: Float, var centerY: Float, var index: Int){
//默认状态
var status = PointStatus.NORMAL
fun setStatusNormal() {
status=PointStatus.NORMAL
}
fun setStatusPressed() {
status=PointStatus.PRESSED
}
fun setStatusError() {
status=PointStatus.ERROR
}
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.kotlin_test.MyLock
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
来源:https://blog.csdn.net/WLX10428/article/details/120175305


猜你喜欢
- 摘要今天用compose来构建一个气泡上升粘连动画和水滴下坠动画,Github源码点击这里知识点compose动画贝塞尔曲线缓动函数comp
- JDBC Statement对象实例以下是利用以下三种查询以及打开和关闭说明的例子:boolean execute(String SQL)
- GZip是常用的无损压缩算法实现,在Linux中较为常见,像我们在Linux安装软件时,基本都是.tar.gz格式。.tar.gz格式文
- 1.常用属性Name:名称;BackColor:设置控件背景颜色;Enabled:是否可用;FlayStyle:控件样式;Image:设置控
- 一、Lambda表达式 1.1 函数式编程思想概述在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿
- 本文实例为大家分享了Java Socket编程实现多人交互聊天室的具体代码,供大家参考,具体内容如下本项目由三个.java文件(
- 一、jaxb是什么 JAXB是Java Architecture for XML Bindi
- 1.启动项目的时候报错1.Error starting ApplicationContext. To display the auto-co
- 本文实例讲述了C#根据反射和特性实现ORM 映射的方法。分享给大家供大家参考。具体如下:(一)关于反射什么是反射?反射就是在运行时,动态获取
- 1.过滤器:所谓过滤器顾名思义是用来过滤的,在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一
- 之前用View Pager做了一个图片切换的推荐栏(就类似与淘宝、头条客户端顶端的推荐信息栏),利用View Pager很快就能实现,但是一
- 本文实例讲述了Java编程使用卡片布局管理器。分享给大家供大家参考,具体如下:运行效果:完整示例代码:package com.han;imp
- 本文实例讲述了java取两个字符串的最大交集的实现方法,分享给大家供大家参考。具体实现方法如下:package com.itheima.ne
- 小编对微信开发颇感兴趣,查阅了网上相关文章进行整理,方便大家一起学习。1、注册帐号--填写服务器配置在https://mp.weixin.q
- 原文是 java ,现在将它翻译成 C# ,并对代码重新编排整理,博主是一个今年刚出来的应届毕业生,不足之处请多多包涵。根据银行卡号判断所属
- 1、深度总结引用一位网友的话,说的非常好,如果别人问你static的作用;如果你说静态修饰 类的属性 和 类的方法 别人认为你是合格的;如果
- 我们知道android是基于Looper消息循环的系统,我们通过Handler向Looper包含的MessageQueue投递Message
- 本文为大家分享了Android使用线程获取网络图片的具体代码,供大家参考,具体内容如下AndroidManifest.xml &n
- InheritableThreadLocal的作用: 当我们需要在子线程中使用父线程中的值得时候我们就可以像使用ThreadLocal那样来
- 本文实例讲述了java简单列出文件夹下所有文件的方法。分享给大家供大家参考,具体如下:import Java.io.*;public cla