红橙Darren视频笔记 九宫格解锁 Java版 IntDef使用

2024-05-30 12:32

本文主要是介绍红橙Darren视频笔记 九宫格解锁 Java版 IntDef使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考链接
https://www.jianshu.com/p/74e760ef8d10
花了接近一天 终于完工
最终效果:
在这里插入图片描述
九宫格看起来复杂 将步骤分解 其实不是很复杂
本文先讲思路 后贴代码

一 定义结构体 测量宽高

结构体Point用于记录各个点 点的位置 index用于记录密码 status记录按下的状态
覆盖onMeasure方法 取宽高中的较小者 绘制一个正方形

二 绘制默认状态的9个圆圈

我们需要一个画笔以及各个圆圈的大小 位置等信息来绘制,因此先做第三步。完成第三步继续做这一步,需要考虑内圆和外圆的半径,可以先随便指定大小,然后根据美观度调整值,当然这是在没有设计的情况下。

三 初始化圆和画笔

需要先计算9个圆圈的位置 用二维数组记录位置以及状态。完成第四步就可以接着初始化圆和画笔了,需要准备三种颜色的画笔

四 计算9个圆圈的位置

一开始我还搞错了圆圈的位置 错误思路:
计算x坐标
第二列的x坐标都是viewWidth/2
第一列的x坐标就是viewWidth/4
第三列的x坐标就是viewWidth3/4
计算y坐标
计算过x坐标后发现 y坐标和x坐标几乎一样就是位置换了 注意前提 viewHeight=viewWidth
第二行的圆的x坐标都是viewWidth/2+dy (viewHeight/2 )第一行的x坐标就是viewWidth/4+dy (viewHeight/4 )第三行的x坐标就是viewWidth
3/4+dy(viewHeight*3/4)
dy=(screenHeight-viewHeight)/2
注意:因为这里用到了view的宽高 所以初始化圆的时候 应该是能拿到宽高的时刻,因此至少要在onMesure方法之后

在这里插入图片描述
之前的计算有误 如果按照我之前的思路写 最终效果:
在这里插入图片描述
出现的问题有两个
1 所有的圆都偏下
这是因为我在第一步重写了onMeasure方法 因此就不再需要+dy了
2 第一列和第三列的圆心错误
我一开始只是简单认为 等分屏幕即可 但是我忽略了中间圆的直径
明显1/4 viewWidth不够作为间距 那么调整变化值 调整多少合适呢,假如各个圆之间的距离等于半径那么有下图
在这里插入图片描述
很明显 屏幕可以分割为10等分
所以
计算x坐标
第二列的x坐标都是viewWidth/2
第一列的x坐标就是viewWidth2/10
第三列的x坐标就是viewWidth
8/10
y坐标省略

五 根据状态绘制不同的圆圈

六 根据手指的状态修改各个圆的状态

判断手指是不是在圆内 初中数学知识 判断点到圆心的距离是否大于半径
在按下和move的时候 得到手指的x和y坐标,遍历9个圆,判断点是否在圆内 如果在,则将其状态更改为pressed
修改绘制圆圈的方法 让其根据圆的状态绘制

七 画线

7.1 绘制两个圆圈之间的连线

使用一个数组保存按下状态的圆圈,更新数组的时机可以放在检查手指是否在圆中的时候,当数组数目大于1时开始绘制,绘制时机可放在画圆之后
绘制时取列表的两个圆的圆心绘制线 但是如果从圆心绘制 效果不是很好
在这里插入图片描述
我们要从内圆的边缘开始绘制 这里又涉及一点数学知识了
在这里插入图片描述
我们已知x y radius求 dx dy
dx/x= radius/两圆心距离
dx = radius/两圆心距离x
那么 dy = radius/两圆心距离
y
计算完毕之后还要考虑一下是+dx dy还是-dx dy

7.2 绘制手与最后一个圆圈之间的连线

有了上面的计算 这个相对简单了 起点的计算仍然需要用到上面的算法
注意如果选中的圆数目为0 不需要绘制
取最后一个选择的点 绘制到手指的线 注意把第7.1中第二个点改为手指的坐标 并且drawLine的终点不需要加或者减dx dy了
注意手指如果在内圈 不要绘制线

7.3 松开手指 去掉最后到手指的线

可以在action up的将手指坐标清空 然后在绘制最后一个选择的圆与手指的线的时候判断手指的坐标是否存在

八 判断手势是否正确

在主界面传入正确的密码
在action up的时候check密码
如果密码太短 弹出提示
如果密码错误 弹出提示
如果密码正确 弹出提示
且提示出现是 屏幕锁住(设置标志位 屏蔽touch事件) 不让用户继续滑动

九 部分代码

自定义view

public class LockPatternView extends View {private Point[][] mPoints;private boolean hasInit = false;private Paint mPaintNormal, mPaintPressed, mPaintError;//这里就不搞那么多画笔了 内外圈一样的颜色private int mRadiusOut, mRadiusInner;private ArrayList<Point> mPressedPoints = new ArrayList<>();private PassListener mPassListener;private float mFingerX = -1;private float mFingerY = -1;private boolean mNeedIntercept = false;public LockPatternView(Context context) {super(context);}public LockPatternView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public LockPatternView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//在这里写死 采用宽高中较短的那个作为边,绘制区域为这个边长的正方形int height = MeasureSpec.getSize(heightMeasureSpec);int width = MeasureSpec.getSize(widthMeasureSpec);int smaller = Math.min(height, width);setMeasuredDimension(smaller, smaller);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (!hasInit) {//只初始化一次hasInit = true;initPoints();//初始化圆 以及位置intPaint();//初始化画笔}drawCircle(canvas);drawLine(canvas);drawLine2Finger(canvas);}private void drawLine2Finger(Canvas canvas) {//注意如果选中的圆数目为0 不需要绘制if (mPressedPoints.size() == 0) {return;}//手指离开屏幕 直接returnif (mFingerX < 0 || mFingerY < 0) {return;}//手指在内圈 直接returnif (isFingerInCircle(mFingerX, mFingerY, mPressedPoints.get(mPressedPoints.size() - 1).positionX, mPressedPoints.get(mPressedPoints.size() - 1).positionY)) {return;}double x = mPressedPoints.get(mPressedPoints.size() - 1).positionX - mFingerX;double y = mPressedPoints.get(mPressedPoints.size() - 1).positionY - mFingerY;double centerDistance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));//dx = radius/两圆心距离*xdouble dx = mRadiusInner / centerDistance * x;//int dy =  radius/两圆心距离*ydouble dy = mRadiusInner / centerDistance * y;canvas.drawLine((float) (mPressedPoints.get(mPressedPoints.size() - 1).positionX - dx), (float) (mPressedPoints.get(mPressedPoints.size() - 1).positionY - dy), mFingerX, mFingerY, mPaintPressed);}private void drawLine(Canvas canvas) {if (mPressedPoints.size() < 2) {return;}for (int i = 1; i < mPressedPoints.size(); i++) {//绘制前一个圆和当前的圆的连线//注意符号 不要调用绝对值函数了 因为手势可以从左上向右下 也可以反过来,这时是+dx dy还是-dx dy值得考虑double x = mPressedPoints.get(i - 1).positionX - mPressedPoints.get(i).positionX;double y = mPressedPoints.get(i - 1).positionY - mPressedPoints.get(i).positionY;double centerDistance = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));//dx = radius/两圆心距离*xdouble dx = mRadiusInner / centerDistance * x;//int dy =  radius/两圆心距离*ydouble dy = mRadiusInner / centerDistance * y;if (mPressedPoints.get(i).status == PointStatus.NORMAL) {return;}canvas.drawLine((float) (mPressedPoints.get(i - 1).positionX - dx), (float) (mPressedPoints.get(i - 1).positionY - dy), (float) (mPressedPoints.get(i).positionX + dx), (float) (mPressedPoints.get(i).positionY + dy),mPressedPoints.get(i).status == PointStatus.PRESSED ? mPaintPressed : mPaintError);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mNeedIntercept) {return true;}switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mFingerX = event.getX();mFingerY = event.getY();traversingCheckInPoints(mFingerX, mFingerY);return true;case MotionEvent.ACTION_MOVE:mFingerX = event.getX();mFingerY = event.getY();traversingCheckInPoints(mFingerX, mFingerY);break;case MotionEvent.ACTION_UP:mNeedIntercept = true;mFingerX = -1;mFingerY = -1;//checkPassWord(); 检查密码应该交由外部比较StringBuilder passBuffer = new StringBuilder();//拼凑密码for (Point point : mPressedPoints) {passBuffer.append(point.index);}mPassListener.notifyPass(passBuffer.toString());//抬起手指后两秒无法操作postDelayed(new Runnable() {@Overridepublic void run() {mNeedIntercept = false;}}, 2000);break;}invalidate();return super.onTouchEvent(event);}//不应该在控件内部判断密码 维持控件的纯洁度
//    private void checkPassWord() {
//        mNeedIntercept = true;
//        clearStatusDelay(2000);
//        //如果密码太短 弹出提示 并将所有点状态跟新为error 设置屏幕无法触碰 两秒后清空状态
//        if (mPressedPoints.size() <= 4) {
//            Toast.makeText(getContext(), "密码太短!", Toast.LENGTH_SHORT).show();
//            for (Point point : mPressedPoints) {
//                point.setStatus(PointStatus.ERROR);
//            }
//            return;
//        }
//
//        StringBuffer passBuffer = new StringBuffer();//拼凑密码
//        for (Point point : mPressedPoints) {
//            passBuffer.append(point.index);
//        }
//        //如果密码正确 弹出提示 设置屏幕无法触碰 两秒之后清空状态
//        Log.d("TAG", "checkPassWord: mPassWord " + mPassWord + " passBuffer " + passBuffer);
//        if (passBuffer.toString().equals(mPassWord)) {
//            Toast.makeText(getContext(), "密码正确!", Toast.LENGTH_SHORT).show();
//        } else {//如果密码错误 弹出提示 并将所有点状态跟新为error 设置屏幕无法触碰 两秒后清空状态
//            Toast.makeText(getContext(), "密码错误!", Toast.LENGTH_SHORT).show();
//            for (Point point : mPressedPoints) {
//                point.setStatus(PointStatus.ERROR);
//            }
//        }
//    }private void drawCircle(Canvas canvas) {for (Point[] pointArr : mPoints) {for (Point point : pointArr) {switch (point.getStatus()) {case PointStatus.NORMAL:canvas.drawCircle(point.positionX, point.positionY, mRadiusOut, mPaintNormal);canvas.drawCircle(point.positionX, point.positionY, mRadiusInner, mPaintNormal);break;case PointStatus.PRESSED:canvas.drawCircle(point.positionX, point.positionY, mRadiusOut, mPaintPressed);canvas.drawCircle(point.positionX, point.positionY, mRadiusInner, mPaintPressed);break;case PointStatus.ERROR:canvas.drawCircle(point.positionX, point.positionY, mRadiusOut, mPaintError);canvas.drawCircle(point.positionX, point.positionY, mRadiusInner, mPaintError);break;}}}}private void intPaint() {mRadiusOut = getMeasuredWidth() / 10;mRadiusInner = mRadiusOut / 4;mPaintNormal = getPaintByColor(0xFFFFFFFF);mPaintPressed = getPaintByColor(0xFFCCFFFF);mPaintError = getPaintByColor(0xFFCC3333);}private Paint getPaintByColor(int color) {Paint paint = new Paint();paint.setColor(color);paint.setAntiAlias(true);paint.setDither(true);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(4);return paint;}private void initPoints() {mPoints = new Point[3][3];//第一行 每一行y坐标一样mPoints[0][0] = new Point(getMeasuredWidth() / 5, getMeasuredWidth() / 5, 0, PointStatus.NORMAL);mPoints[0][1] = new Point(getMeasuredWidth() * 2 / 4, getMeasuredWidth() / 5, 1, PointStatus.NORMAL);mPoints[0][2] = new Point(getMeasuredWidth() * 4 / 5, getMeasuredWidth() / 5, 2, PointStatus.NORMAL);//第二行mPoints[1][0] = new Point(getMeasuredWidth() / 5, getMeasuredWidth() / 2, 3, PointStatus.NORMAL);mPoints[1][1] = new Point(getMeasuredWidth() * 2 / 4, getMeasuredWidth() / 2, 4, PointStatus.NORMAL);mPoints[1][2] = new Point(getMeasuredWidth() * 4 / 5, getMeasuredWidth() / 2, 5, PointStatus.NORMAL);//第三行mPoints[2][0] = new Point(getMeasuredWidth() / 5, getMeasuredWidth() * 4 / 5, 6, PointStatus.NORMAL);mPoints[2][1] = new Point(getMeasuredWidth() * 2 / 4, getMeasuredWidth() * 4 / 5, 7, PointStatus.NORMAL);mPoints[2][2] = new Point(getMeasuredWidth() * 4 / 5, getMeasuredWidth() * 4 / 5, 8, PointStatus.NORMAL);}@Retention(RetentionPolicy.SOURCE)@IntDef({PointStatus.NORMAL, PointStatus.PRESSED, PointStatus.ERROR})public @interface PointStatus {int NORMAL = 0;int PRESSED = 1;int ERROR = 2;}void traversingCheckInPoints(float fingerX, float fingerY) {for (Point[] pointArr : mPoints) {for (Point point : pointArr) {if (isFingerInCircle(fingerX, fingerY, point.positionX, point.positionY)) {point.setStatus(PointStatus.PRESSED);if (!mPressedPoints.contains(point)) {mPressedPoints.add(point);}//Log.d("TAG", "traversingCheckInPoints: in!!");return;}}}}boolean isFingerInCircle(float fingerX, float fingerY, int circleX, int circleY) {//初中知识 判断点到圆心的距离//Log.d("TAG", "traversingCheckInPoints: fingerX->"+fingerX+" fingerY "+fingerY+" circleX "+circleX+" circleY "+circleY);float dx = Math.abs(fingerX - circleX);float dy = Math.abs(fingerY - circleY);return Math.sqrt(dx * dx + dy * dy) - mRadiusOut < 0;}static class Point {int positionX;int positionY;int index;@PointStatusint status;Point(int positionX, int positionY, int index, @PointStatus int status) {this.positionX = positionX;this.positionY = positionY;this.index = index;this.status = status;}void setStatus(@PointStatus int status) {this.status = status;}int getStatus() {return status;}}public void setPointsStatus(@PointStatus int pointsStatus) {for (LockPatternView.Point point : mPressedPoints) {point.setStatus(pointsStatus);}invalidate();}public void resetLockPatternView() {setPointsStatus(PointStatus.NORMAL);mPressedPoints.clear();}public void setPassListener(PassListener mPassListener) {this.mPassListener = mPassListener;}interface PassListener {void notifyPass(String pass);}
}

activity

public class MainActivity extends AppCompatActivity {String mPassWord = "123456";LockPatternView mLockPattern;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mLockPattern = findViewById(R.id.lockPattern);mLockPattern.setPassListener(new LockPatternView.PassListener() {@Overridepublic void notifyPass(String pass) {checkPassWord(pass);}});}private void checkPassWord(String passWord) {//如果密码太短 弹出提示 并将所有点状态跟新为error 设置屏幕无法触碰 两秒后清空状态if (passWord.length() <= 4) {Toast.makeText(MainActivity.this, "密码太短!", Toast.LENGTH_SHORT).show();mLockPattern.setPointsStatus(LockPatternView.PointStatus.ERROR);delayResetLockPattern(2000);//2s后恢复正常状态return;}//如果密码正确 弹出提示 设置屏幕无法触碰 两秒之后清空状态Log.d("TAG", "checkPassWord: mPassWord " + mPassWord + " passWord " + passWord);if (passWord.equals(mPassWord)) {Toast.makeText(MainActivity.this, "密码正确!", Toast.LENGTH_SHORT).show();delayResetLockPattern(2000);} else {//如果密码错误 弹出提示 并将所有点状态跟新为error 设置屏幕无法触碰 两秒后清空状态Toast.makeText(MainActivity.this, "密码错误!", Toast.LENGTH_SHORT).show();delayResetLockPattern(2000);}}void delayResetLockPattern(int delayMillisecond) {mLockPattern.postDelayed(new Runnable() {@Overridepublic void run() {mLockPattern.resetLockPatternView();}}, delayMillisecond);}
}

activity布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.example.lockpatternview.LockPatternViewandroid:id="@+id/lockPattern"android:layout_centerInParent="true"android:background="#ccc"android:layout_width="match_parent"android:layout_height="match_parent" />
</RelativeLayout>

全代码:
https://github.com/caihuijian/learn_darren_android.git
flag:还缺少绘制三角形的部分,没看懂 等日后看懂再补

这篇关于红橙Darren视频笔记 九宫格解锁 Java版 IntDef使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1016219

相关文章

Spring boot整合dubbo+zookeeper的详细过程

《Springboot整合dubbo+zookeeper的详细过程》本文讲解SpringBoot整合Dubbo与Zookeeper实现API、Provider、Consumer模式,包含依赖配置、... 目录Spring boot整合dubbo+zookeeper1.创建父工程2.父工程引入依赖3.创建ap

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

SpringBoot结合Docker进行容器化处理指南

《SpringBoot结合Docker进行容器化处理指南》在当今快速发展的软件工程领域,SpringBoot和Docker已经成为现代Java开发者的必备工具,本文将深入讲解如何将一个SpringBo... 目录前言一、为什么选择 Spring Bootjavascript + docker1. 快速部署与

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

Spring Boot spring-boot-maven-plugin 参数配置详解(最新推荐)

《SpringBootspring-boot-maven-plugin参数配置详解(最新推荐)》文章介绍了SpringBootMaven插件的5个核心目标(repackage、run、start... 目录一 spring-boot-maven-plugin 插件的5个Goals二 应用场景1 重新打包应用

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu