效果:

代码:

package com.example.kotlin_testimport 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=falseprivate var mPoints:Array<Array<Point?>> = Array(3){Array<Point?>(3){null} }private var mSelectPoints=ArrayList<Point>()private var isTouch=falseprivate var code= listOf(0,1,2,5,8)//画笔private lateinit var mNormalPaint:Paintprivate lateinit var mPressedPaint:Paintprivate lateinit var mErrorPaint: Paintprivate lateinit var mLinePaint: Paint//颜色private val mNormalColor=Color.BLACKprivate val mPressedColor=Color.GREENprivate val mErrorColor=Color.REDprivate val mLineColor=Color.BLACK//外圆半径private var mDotRadius=0constructor(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.centerXvar dy=point.centerY-mLastPoint.centerYvar 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=0fprivate var movingY=0foverride fun onTouchEvent(event: MotionEvent?): Boolean {movingX=event!!.xmovingY=event.ywhen (event.action) {MotionEvent.ACTION_DOWN->{for (i in 0..mSelectPoints.size - 1) {mSelectPoints[i].setStatusNormal()}mSelectPoints.clear()invalidate()//先判断是不是在圆内var dd=pointif (dd != null) {dd.setStatusPressed()mSelectPoints.add(dd)isTouch=true}}MotionEvent.ACTION_MOVE->{//先判断是不是在圆内var dd=pointif (dd != null) {dd.setStatusPressed()if (!mSelectPoints.contains(dd)) {mSelectPoints.add(dd)}}}MotionEvent.ACTION_UP->{isTouch=falseif (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())<mDotRadiusreturn isIn}private fun initPaint() {//正常画笔mNormalPaint = Paint()mNormalPaint!!.color=mNormalColormNormalPaint.style=Paint.Style.STROKEmNormalPaint.isAntiAlias=truemNormalPaint.strokeWidth=mDotRadius.toFloat()/12//按下画笔mPressedPaint = Paint()mPressedPaint!!.color=mPressedColormPressedPaint.style=Paint.Style.STROKEmPressedPaint.isAntiAlias=truemPressedPaint.strokeWidth=mDotRadius.toFloat()/9//错误画笔mErrorPaint = Paint()mErrorPaint!!.color=mErrorColormErrorPaint.style=Paint.Style.STROKEmErrorPaint.isAntiAlias=truemErrorPaint.strokeWidth=mDotRadius.toFloat()/12//连线画笔mLinePaint = Paint()mLinePaint!!.color=mLineColormLinePaint.style=Paint.Style.STROKEmLinePaint.isAntiAlias=truemLinePaint.strokeWidth=mDotRadius.toFloat()/12}private fun initDot() {var width=this.widthvar height=this.heightvar offsetX=0f//九个宫格为正方形,距离布局左边的距离var offsetY=0f//九宫格为正方形,距离布局顶部的距离//兼容横竖屏if (width > height) {offsetX = (width - height).toFloat() / 2width = height} else {offsetY = (height - width).toFloat() / 2}//每个方格的大小var squareWidth=width/3mDotRadius=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.NORMALfun 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.MyLockandroid:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>

自定义View: 九宫格图形解锁(Kotlin版)相关推荐

  1. android 自定义图形,Android自定义View之图形图像(模仿360的刷新球自定

    概述: 360安全卫士的那个刷新球(姑且叫它刷新球,因为真的不知道叫什么好,不是dota里的刷新球!!),里面像住了水一样,生动可爱,看似简单,写起来不太简单,本例程只是实现了它的部分功能而已,说实话 ...

  2. android自定义View: 九宫格解锁

    本系列自定义View全部采用kt 系统:mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 废话不多说,先 ...

  3. Android 自定义 View 之 LeavesLoading,kotlin入门书籍推荐

    // 计算移动因子 float fraction = (float) intervalTime / mLeafFloatTime; leaf.x = (1-fraction)*mProgressLen ...

  4. Android自定义View之图形图像(模仿360的刷新球自定义一个SeekBar)

    概述: 360安全卫士的那个刷新球(姑且叫它刷新球,因为真的不知道叫什么好,不是dota里的刷新球!!),里面像住了水一样,生动可爱,看似简单,写起来不太简单,本例程只是实现了它的部分功能而已,说实话 ...

  5. Qt+VS 自定义对话框 九宫格手势解锁

    一个项目,同事让界面上做个密码权限控制,防止客户误操作,由于设备一般不接键盘,输密码就太麻烦了,干脆模仿手机的手势解锁做了个对话框.网上找了些资源,代码都不太全,只能按照自己的理解做了一个,退回功能实 ...

  6. android 自定义view: 蛛网/雷达图(三)

    本系列自定义View全部采用kt 系统mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 本篇效果: 蛛网 ...

  7. android 声波曲线动画,(自定义view实现)音量波形图

    标签: 自定义view 音量波形 音波 本文目的:主要是记录自己在实现自定义view的时候,一些思路和解决方案. 目标 音量波形图 绘制两个音量波形,并且能够向右运动,上面的波形移动速度慢,下面的波形 ...

  8. 自定义View之--九宫格图形密码锁

    前言: 很多金融和几大商业银行的APP,都使用了九宫格图形密码锁来增强资金账户的安全.我也是金融公司的一员,在空余的时候,写下这个view,可以说是明智之举. 效果预览 这样一个逻辑差不多可以满足基本 ...

  9. PHP直播平台源码Android自定义View:9宫格图形手势解锁

    9宫格图形解锁的操作就是在9个点上进行的,所以我们先定义一个点类,该类主要用于存储点的信息:坐标,状态,位置. public class Point {//正常状态public static int ...

  10. android自定义view之九宫格解锁

    android自定义view之九宫格解锁 更多细节请看源码 https://github.com/que123567/lockview 1. 定义一个类作为九宫格的格子 包含坐标和索引(用来记录密码) ...

最新文章

  1. webstorm设置TSlint格式化TypeScript代码快捷键Alt+X
  2. adnroid开发环境的搭建
  3. 基于语音识别的微博签到系统
  4. 读《启示录》有感-----1
  5. java tld 方法重载_自定义标签
  6. Java并发编程:同步容器
  7. Python爬虫-《神雕侠侣》
  8. [转载] python对列表单词排序_计算列表中单词的频率并按频率排序
  9. 程序员转行杀猪是种什么体验?
  10. Disruptor 极速体验
  11. C++开发坦克大战--补充(加入传送门)--附完整代码
  12. Iphone 5s/iPad Air/iPad Mini 2 降级10.3.3
  13. oracle中算百分比,Oracle百分比分析函数RATIO_TO_REPORT() OVER()实例详解
  14. 颜色空间的几种表示方法
  15. java基础第十五篇之IO流和递归算法
  16. Hexo折腾之改用Valine评论系统 - 更新域名绑定,评论后台管理
  17. vue3+echart5 遇到的报错:Cannot read properties of undefined (reading ‘type‘)
  18. 《缠中说禅108课》41:没有节奏,只有死
  19. 计算当前日期是一年中第几周
  20. 螣龙安科:迷宫勒索病毒——勒索受害者一年并且人数仍在增长

热门文章

  1. 1.10 新概念 have a cold/headache
  2. id门禁卡复制到手机_怎么把手机变成门禁卡 手机NFC复制门禁卡图文教程
  3. rollup又一打包利器,不需要的代码统统走开
  4. 论如何设计博客分类标签系统
  5. Rust学习教程30 - Panic原理剖析
  6. 软件测试是什么工作状态,软件测试,现在主要的工作内容是干什么?
  7. 每个开发人员都应该学习的5种编程语言(上)
  8. 构建基于 MCU 安全物联网系统
  9. JavaScript中unshift() 方法
  10. 数据分析常见的英文缩写(一)