红橙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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na