100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > android九宫格布局解锁 Android onDraw()--九宫格解锁

android九宫格布局解锁 Android onDraw()--九宫格解锁

时间:2019-05-06 08:12:35

相关推荐

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();

puteBounds(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();

puteBounds(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的实战。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。