Android自定義View實(shí)現(xiàn)九宮格圖形解鎖(Kotlin版)
本文實(shí)例為大家分享了Android自定義View實(shí)現(xiàn)九宮格圖形解鎖的具體代碼,供大家參考,具體內(nèi)容如下
效果:
代碼:
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->{ //先畫外圓,再畫內(nèi)圓 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] //兩點(diǎn)連線 if (mSelectPoints.size > 1) { for (i in 1..mSelectPoints.size-1) { var point = mSelectPoints[i] realDrawLine(mLastPoint, point, canvas, mLinePaint) mLastPoint=point } } //手指和某個(gè)點(diǎn)的連線 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 ) { //不是從圓心坐標(biāo)開始畫,而是距離圓心有一定的距離 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() //先判斷是不是在圓內(nèi) var dd=point if (dd != null) { dd.setStatusPressed() mSelectPoints.add(dd) isTouch=true } } MotionEvent.ACTION_MOVE->{ //先判斷是不是在圓內(nèi) 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) { //密碼不對(duì),設(shè)置為錯(cuò)誤狀態(tài) mSelectPoints[i].setStatusError() } break } } } else { for (i in 0..mSelectPoints.size - 1) { mSelectPoints[i].setStatusError() } } } } invalidate() return true } //擴(kuò)展屬性,遍歷九個(gè)圓,看手指的按在哪個(gè)圓里面 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 } //判斷是不是在圓內(nèi) 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 //錯(cuò)誤畫筆 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//九個(gè)宮格為正方形,距離布局左邊的距離 var offsetY=0f//九宮格為正方形,距離布局頂部的距離 //兼容橫豎屏 if (width > height) { offsetX = (width - height).toFloat() / 2 width = height } else { offsetY = (height - width).toFloat() / 2 } //每個(gè)方格的大小 var squareWidth=width/3 mDotRadius=squareWidth/4 //九個(gè)宮格,存于數(shù)組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) } //圓的狀態(tài) enum class PointStatus{ NORMAL,PRESSED,ERROR } class Point(var centerX: Float, var centerY: Float, var index: Int){ //默認(rèn)狀態(tài) 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>
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android程序開發(fā)通過HttpURLConnection上傳文件到服務(wù)器
這篇文章主要介紹了Android程序開發(fā)通過HttpURLConnection上傳文件到服務(wù)器的相關(guān)資料,需要的朋友可以參考下2016-01-01Android Handler 機(jī)制實(shí)現(xiàn)原理分析
本文主要介紹 Android Handle機(jī)制實(shí)現(xiàn)的原理,這里整理了詳細(xì)的關(guān)于Handler的資料以及工作流程和實(shí)際應(yīng)用,有興趣的小伙伴可以參考下2016-08-08Android 中使用 ViewPager實(shí)現(xiàn)屏幕頁面切換和頁面輪播效果
ViewPager是谷歌官方給我們提供的一個(gè)兼容低版本安卓設(shè)備的軟件包,里面包囊了只有在安卓3.0以上可以使用的api。下面我們就展示下ViewPager可以實(shí)現(xiàn)的兩種簡單效果,感興趣的朋友一起看看吧2016-12-12Android Init進(jìn)程對(duì)信號(hào)的處理流程詳細(xì)介紹
這篇文章主要介紹了Android Init進(jìn)程對(duì)信號(hào)的處理流程詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-02-02Android中Activity常用功能設(shè)置小結(jié)(包括全屏、橫豎屏等)
這篇文章主要介紹了Android中Activity常用功能設(shè)置小結(jié)(包括全屏、橫豎屏等),以簡單實(shí)例形式分析了Android實(shí)現(xiàn)全屏、豎屏及一直顯示等的技巧與注意事項(xiàng),需要的朋友可以參考下2015-10-10Android自定義dialog 自下往上彈出的實(shí)例代碼
本文通過實(shí)例代碼給大家介紹了Android自定義dialog 自下往上彈出效果,代碼簡單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-08-08大型項(xiàng)目里Flutter測試應(yīng)用實(shí)例集成測試深度使用詳解
這篇文章主要為大家介紹了大型項(xiàng)目里Flutter測試應(yīng)用實(shí)例集成測試深度使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12