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
0
投稿
猜你喜欢
- 目录1. 什么是XSS攻击?2. 如何防范?2.1 什么时候注入请求参数3. 具体处理细节1. 什么是XSS攻击? &
- 1、添加spring相关jar包2、配置ehcache jar包。3、添加ehcache mybatis 适配器jar包(在mybatis官
- Mybatis使用@Select注解sql中使用inmapper@Select("SELECT u.* , ur.ro
- 一、微服务简介 Ⅰ、我对微服务的理解微服务是软件开发的一种架构方式,由单一的应用小程序构成的小服务;一个软件系统由多个服务组成;在微服务中,
- state:比较常用,各种状态都可以用它,但是它更着重于一种心理状态或者物理状态。Status:用在人的身上一般是其身份和地位,作“状态,情
- Java画图 给图片底部添加文字标题需求给图片底部添加文字编号import java.awt.Color;import java.awt.F
- 前言在windows平台下实现高性能网络服务器,iocp(完成端口)是唯一选择。编写网络服务器面临的问题有:1 快速接收客户端的连接。2 快
- 一、VSCode安装EmmyLua 二、添加配置文件三、设置配置文件执行完第二步会弹出添加好的launch.json配置文件,这个
- 方案一.使用国内的镜像阿里仓库等首先通过maven的路径找到setting.xml的文件然后在其中修改mirror和profile保存一下就
- 介绍Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。Spring Cache提供了一
- 一、简介Mutex的突出特点是可以跨应用程序域边界对资源进行独占访问,即可以用于同步不同进程中的线程,这种功能当然这是以牺牲更多的系统资源为
- spring boot框架中已经集成了redis,在1.x.x的版本时默认使用的jedis客户端,现在是2.x.x版本默认使用的lettuc
- 题目给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。示例 1:输入: "abcabcbb&qu
- 以下实例演示了如何通过 Collections 类的 Collections.max() 和 Collections.min() 方法来查找
- 简介LinkedBlockingQueue是一个阻塞的有界队列,底层是通过一个个的Node节点形成的链表实现的,链表队列中的头节点是一个空的
- Spring Boot简介Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开
- 1.发生问题的场景我在用java获取一个接口的大JSON字符串,并赋值给String常量时,遇到了java: 常量字符串过长这个报错2.解决
- pom.xml<dependency> <groupId>org.springframework.bo
- 本文实例讲述了C#自定义缓存封装类。分享给大家供大家参考。具体如下:这个自定义的C#类封装了部分常用的缓存操作,包括写入缓存,读取缓存,设置
- 本文实例讲述了Java集合定义与用法。分享给大家供大家参考,具体如下:java集合大体可分为三类,分别是Set、List和Map,它们都继承