android九宫格布局解锁,Android onDraw()--九宫格解锁
onDraw这个方法在自定义中尤其重要,我们可以measure之后通过Canvas进行绘制,九宫格解锁这个View现在已经被人脸跟指纹给替代了,但是做起来还是有点东西的。
下面就是做这个View的思路:
九个格子的布局
格子之间的连线
格子之间的连线所要考虑的问题
一 . 格子布局
之前写过onlayout来进行View的排放,那要继承于ViewGroup,这次我继承之View来实现。在onDraw中进行位置的摆放。整个的思路就是整出一个正方形,然后均与摆放9个格子的位置。本次摆放的方法是摆放一个5*5的矩形,即两个圆之间的间隔也是一个圆,这样方便了后期的计算。首先是计算每个球的圆心:
radius = (measureWidth - 100) / 5;
innerPadding = 50;
radius = radius / 2;
int centerX = innerPadding + radius, centerY = innerPadding + radius;
for (int i = 0; i < 9; i++) {
pointList.add(new Point(centerX, centerY));
centerX += 4 * radius;
if (centerX > (measureWidth - 100)) {
centerX = innerPadding + radius;
centerY += 4 * radius;
}
}
这里的操作就跟之前的Android onLayout()摆放位置是一个道理,然后在onDraw()中:
for (Point mPoint : pointList) {
if (mPoint.isSelected) {
paint.setColor(Color.BLUE);
} else {
paint.setColor(Color.WHITE);
}
canvas.drawCircle(mPoint.x, mPoint.y, radius, paint);
}
这里默认的是白色的,当被选中的时候换成蓝色的。很简单我们的九宫格就画出来了,那么看看如何进行交互的。
二. 格子之间的连线
第一反应是用canvas.drawLine()来实现,实际操作并没有什么用,因为当画到一个点的时候,出发点就是当前的这个点了,之前的线段用drawLine()并不好用绘制,这里要用Path来实现,通过LineTo到某个点然后drawPath():
canvas.drawPath(path, linePaint);
canvas.drawLine(startX, startY, endX, endY, linePaint);
onDraw中的剩余代码,第一句表示的是画线段,记录已经画出的线段,第二个则是记录画出的点,这里固定(startX,startY),改变 (endX, endY)来实现伸缩的线段。
我们来看一下onTouch方法。onTouch有Down, Move,Up,这三种是比较常用的方法,这个后面再进行拓展。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
看一下Down里面的方法:
setAllunSelected();
path.reset();
int i = getPoint(x, y);
if (DEFAULT_NUM == i) {
invalidate();
return false;
} else {
//说明点在点子上了
selectList.add(i);
pointList.get(i).setSelected(true);
path.moveTo(pointList.get(i).getX(), pointList.get(i).getY());
startX = pointList.get(i).getX();
startY = pointList.get(i).getY();
}
首先把之前的都清除掉用setAllunSelected()把所有点的状态置成白色的状态,再把path重置一下,int i = getPoint(x, y); 来计算一下当前按下的这个点是不是在某个九宫格中的某个圆里面,这时候把当前的点放进selectList集合里面并且把这个点的状态置成被选中的状态,这就成了我们可以记录的画出密码的线路,也就是我们的密码。因为是第一次按下去,所以把当前的startX,startY换成当前选中的点,做为我们的出发点。
后面紧跟onMove代码:
endX = x;
endY = y;
int j = getPoint(x, y);
if (DEFAULT_NUM != j) {
if(selectList.contains(j)){
invalidate();
break;
}
int missNum = getPointNum(j);
if(missNum!=DEFAULT_NUM && !selectList.contains(j) ){
selectList.add(missNum);
pointList.get(missNum).setSelected(true);
}
selectList.add(j);
pointList.get(j).setSelected(true);
path.lineTo(pointList.get(j).getX(), pointList.get(j).getY());
startX = pointList.get(j).getX();
startY = pointList.get(j).getY();
}
invalidate();
这里我们做的就是移动到当前的点我们就改成endX,endY,还是通过getPoint这个方法来判断是不是按在某个点里面:
private int getPoint(float x, float y) {
for (int i = 0; i < pointList.size(); i++) {
int j = getPosition(x, y, i);
if (DEFAULT_NUM != j) {
return j;
}
}
return DEFAULT_NUM;
}
private int getPosition(float x, float y, int position) {
Point point = pointList.get(position);
if (Math.hypot(Math.abs(x - point.x), Math.abs(y - point.y)) < radius) {
return position;
}
return DEFAULT_NUM;
}
三.连线要考虑的问题
getPoint()方法就是记录就是计算当前点距离是不是在某个我们画的圆里面,分别到9个圆心的距离就可以判断是不是在当前这个圆里面,然后可以确定是点击的第几个圆。后面我们进行了一次判断假如我们selectList中已经有了某个点,当我们再滑动到他的时候不进行任何操作。后面加了一个方法getMissNum这个方法是后面思考的时候加上的主要的实现就是看图1跟图2的区别:
图1.png
图2.png
实际区别就是能不能主动吸附到中间这个点的问题代码:
private int getPointNum(int position){
if(selectList.size()<1)
{
return DEFAULT_NUM;
}
int size =selectList.size();
int i = selectList.get(size-1);
Point p = pointList.get(position);
Point point = pointList.get(i);
//判断是不是由上往下,从左往右
boolean b = false ;
if(position>i){
b = true;
}
if(Math.abs(p.getX()-point.getX())!=4*radius ||Math.abs(p.getX()-point.getX())!=4*radius){
//说明不是相邻的两个点
if(Math.abs(p.getX()-point.getX()) == 0 ||Math.abs(p.getY()-point.getY())==0 ){
//说明一排或者一列中间有个没点到的
if(Math.abs(p.getY()-point.getY()) == 0) {
if(b){
return i+1;
}else{
return i-1;
}
}else {
if(b){
return i+3;
}else{
return i-3;
}
}
}else if(Math.abs(p.getX()-point.getX())==8*radius && Math.abs(p.getY()-point.getY())==8*radius){
//这里是对角线
return CENTER_NUM;
}
}
return DEFAULT_NUM;
}
本来想通过Path转换来看看某个点在不在这个Path区域中,后来实践之后发现并不能做的出来,也把代码贴出来:
private boolean pointInPath(Path path, Point point) {
RectF bounds = new RectF();
path.computeBounds(bounds, true);
Region region = new Region();
region.setPath(path, new Region((int) bounds.left, (int) bounds.top,
(int) bounds.right, (int) bounds.bottom));
return region.contains((int)point.x, (int)point.y);
}
最后就是将绘制好的密码扔出去,可以的话再添加个回掉把密码组给扔出去。
完整代码:
public class LockView extends View {
final int DEFAULT_NUM = -1;
final int CENTER_NUM = 4;
String TAG = "LockView";
int measureWidth;
private int innerPadding;
private int radius;
List pointList;
Paint paint;
Paint linePaint;
float startX, startY;
float endX, endY;
Path path;
ArrayList selectList;
boolean dismiss = false;
boolean isMove = false;
public LockView(Context context) {
this(context, null);
}
public LockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
pointList = new ArrayList<>();
init();
}
private void init() {
paint = new Paint();
paint.setStrokeWidth(2);
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
linePaint = new Paint();
linePaint.setStrokeWidth(20);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setAntiAlias(true);
linePaint.setColor(Color.WHITE);
linePaint.setStrokeJoin(Paint.Join.ROUND);
//线条结束处绘制一个半圆
linePaint.setStrokeCap(Paint.Cap.ROUND);
selectList = new ArrayList<>();
path = new Path();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int size = Math.min(width, height);
setMeasuredDimension(size, size);
measureWidth = getMeasuredWidth();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.d(TAG, "onSizeChanged: " + measureWidth);
radius = (measureWidth - 100) / 5;
innerPadding = 50;
radius = radius / 2;
int centerX = innerPadding + radius, centerY = innerPadding + radius;
for (int i = 0; i < 9; i++) {
pointList.add(new Point(centerX, centerY));
centerX += 4 * radius;
if (centerX > (measureWidth - 100)) {
centerX = innerPadding + radius;
centerY += 4 * radius;
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(dismiss){
setAllunSelected();
path.reset();
}
for (Point mPoint : pointList) {
if (mPoint.isSelected) {
paint.setColor(Color.BLUE);
} else {
paint.setColor(Color.WHITE);
}
canvas.drawCircle(mPoint.x, mPoint.y, radius, paint);
}
canvas.drawPath(path, linePaint);
canvas.drawLine(startX, startY, endX, endY, linePaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setAllunSelected();
dismiss = false;
path.reset();
int i = getPoint(x, y);
if (DEFAULT_NUM == i) {
invalidate();
return false;
} else {
//说明点在点子上了
selectList.add(i);
pointList.get(i).setSelected(true);
path.moveTo(pointList.get(i).getX(), pointList.get(i).getY());
startX = pointList.get(i).getX();
startY = pointList.get(i).getY();
}
break;
case MotionEvent.ACTION_MOVE:
endX = x;
endY = y;
int j = getPoint(x, y);
if (DEFAULT_NUM != j) {
if(selectList.contains(j)){
invalidate();
break;
}
int missNum = getPointNum(j);
if(missNum!=DEFAULT_NUM && !selectList.contains(j) ){
selectList.add(missNum);
pointList.get(missNum).setSelected(true);
}
selectList.add(j);
pointList.get(j).setSelected(true);
path.lineTo(pointList.get(j).getX(), pointList.get(j).getY());
startX = pointList.get(j).getX();
startY = pointList.get(j).getY();
}
invalidate();
break;
case MotionEvent.ACTION_UP:
endX = startX;
endY = startY;
dismiss = true ;
invalidate();
break;
default:
break;
}
return true;
}
private int getPoint(float x, float y) {
for (int i = 0; i < pointList.size(); i++) {
int j = getPosition(x, y, i);
if (DEFAULT_NUM != j) {
return j;
}
}
return DEFAULT_NUM;
}
private int getPosition(float x, float y, int position) {
Point point = pointList.get(position);
if (Math.hypot(Math.abs(x - point.x), Math.abs(y - point.y)) < radius) {
return position;
}
return DEFAULT_NUM;
}
class Point {
float x;
float y;
boolean isSelected;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
}
private void setAllunSelected() {
for (int i = 0; i < pointList.size(); i++) {
pointList.get(i).setSelected(false);
}
selectList.clear();
}
private int getPointNum(int position){
if(selectList.size()<1)
{
return DEFAULT_NUM;
}
int size =selectList.size();
int i = selectList.get(size-1);
Point p = pointList.get(position);
Point point = pointList.get(i);
//判断是不是由上往下,从左往右
boolean b = false ;
if(position>i){
b = true;
}
if(Math.abs(p.getX()-point.getX())!=4*radius ||Math.abs(p.getX()-point.getX())!=4*radius){
//说明不是相邻的两个点
if(Math.abs(p.getX()-point.getX()) == 0 ||Math.abs(p.getY()-point.getY())==0 ){
//说明一排或者一列中间有个没点到的
if(Math.abs(p.getY()-point.getY()) == 0) {
if(b){
return i+1;
}else{
return i-1;
}
}else {
if(b){
return i+3;
}else{
return i-3;
}
}
}else if(Math.abs(p.getX()-point.getX())==8*radius && Math.abs(p.getY()-point.getY())==8*radius){
//这里是对角线
return CENTER_NUM;
}
}
return DEFAULT_NUM;
}
private boolean pointInPath(Path path, Point point) {
RectF bounds = new RectF();
path.computeBounds(bounds, true);
Region region = new Region();
region.setPath(path, new Region((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom));
return region.contains((int)point.x, (int)point.y);
}
}
最后关于一些想使用的自定义属性自己可以直接定义一下,包括圆的样式什么的都是可以自己自定义搞一下的。这里就不写了。只是实现了一下这个简单的功能,对onDraw()方法中画图有一定认识。主要有画circle,画Path ,画Line的实战。
android九宫格布局解锁,Android onDraw()--九宫格解锁相关推荐
- android 长方形九宫格布局,房子九宫格布局,长方形房屋九宫格怎么画
1,长方形房屋九宫格怎么画 也可直接点"搜索资料"搜索整个问题. 长方形 九宫格 房屋 搜索资料 本地图片 图片链接 提交回答 匿名 回答自动保存中为你推荐:特别推荐... 2,什 ...
- android渐变布局,关于Android的径向渐变高级编程的实现
在最近的一系列文章,对midipad APP,有一个关于一个radialgradiant渲染每个padview利用的探讨,对审美的原因,这是一个软件层,而不是一个硬件层.在这个简短的系列中,我们首先看 ...
- android 线性布局位置,android – 如何在线性布局中更改视图的位置.
所以我有一个已经填充了子项的线性布局.有没有办法改变其中一个孩子的位置? 如果有任何帮助的话,我正试图交换他们之间的观点. final LinearLayout parrent = (LinearLa ...
- android 获取布局textview,android – 获取TextView中文本的位置
看看几个Paint方法: getTextBounds()和 measureText.我们可以使用它们来确定TextView中文本的偏移量.确定TextView中的偏移后,我们可以将其添加到TextVi ...
- android局部布局替换,Android 局部布局替换的实现方式
最近再搞远程视频的功能,其中要实现加载视频.加载视频失败.加载成功的局部布局替换,查阅相关资料,找到一种投机取巧的方式. 首先分别写这三种效果的子布局,分别为 top_remotetreate.xml ...
- android圆角布局阴影,Android 布局阴影实现
最近项目要求,ui有很多有关于阴影的设计要求,网上找了些实现方式,但都不是很理想.现在闲下来了,就寻思着自己写个阴影布局耍耍,以备后用.先说道说道我找到的几种阴影实现方式: 系统阴影 Andorid ...
- android ui布局适配,Android适配全面总结(一)----屏幕适配
前言 Android适配是一个老生常谈的问题,很多程序员觉得很恶心,不愿意做适配,但是又不得不做.然后老板说,这位兄弟,做好了,今天晚饭给你加个鸡腿,然后程序员开始找各种资料,忙活起来了,最终在苦逼的 ...
- android滚动条布局横向,Android自定义ViewGroup实现可滚动的横向布局(2)
这里直接代码: package com.example.libingyuan.horizontallistview.ScrollViewGroup; import android.content.Co ...
- android相对布局代码,Android基础_3 Activity相对布局(示例代码)
相对布局要比前面讲的线性布局和表格布局要灵活一些,所以平常用得也是比较多的.相对布局控件的位置是与其周围控件的位置相关的,从名字可以看出来,这些位置都是相对的,确定出了其中一个控件的位置就可以确定另一 ...
- android 首页布局变换,Android XML布局与View之间的转换
Android的布局方式有两种,一种是通过xml布局,一种是通过java代码布局,两种布局方式各有各的好处,当然也可以相互混合使用.很多人都习惯用xml布局,那xml布局是如何转换成view的呢?本文 ...
最新文章
- HADOOP都升级到2.5啦~~~
- java 8进制 前是 零,从零学java笔录-第6篇 进制之间转换
- 多线程编程(2): 线程的创建、启动、挂起和退出
- 消息通知调度服务器,mPaaS 服务端核心组件:消息推送 MPS 架构及流程设计
- maxon电机中文名_MAXON电机完全手册(全系列).pdf
- excel数据正在计算机,excel数据太多表格太卡-急!Excel数据量大,电脑卡死?
- MSCI 明晟D.J. Orr博士:中国量化投资不缺人才缺工具
- 全国省份、城市关联表 mysql(含城市名拼音)
- 突发!意外!华芯通公司将于4月30日关闭
- Fiddler抓包配置和使用(全网最详细教程)
- WPF ContextMenu的使用
- P4924 [1007]魔法少女小Scarlet
- 教育知识与能力简答题知识点
- 360N4救砖刷机 全程完整操作
- [美文欣赏] 春风沉醉的晚上
- http协议:http协议理解
- 人性26——让我的蠢猫通人性吧
- 无人驾驶-GPS地图采集使用说明
- React18报错警告:You are calling ReactDOMClient.createRoot() on a container that has already been passed
- 一些问题的解决,分析
热门文章
- 17个C语言字符串处理函数
- winfrom快递追踪小程序 实现实时推送快递状态到手机
- 虹软人脸识别初试(windows SDK,idea,USB摄像头)
- LabVIEW编程基础:事件结构框架编程
- DataV在浏览器放大缩小以及全屏前后不能自适应问题
- idea的maven项目中报错:前言中不允许有内容
- Seq2Seq模型PyTorch版本
- 【Axure高保真原型】音频上传和播放
- Module not found: Error: Can‘t resolve ‘./@/assets/image/folder.png‘
- [Codevs] 3287 货车运输