前言
九宫格手势解锁已经是非常常见的手机解锁方式,支付宝等一些软件也都曾经使用过,感觉还是很高大上的。在github已经有很好的开源控件,大家可以去自己搜索,我自己写了一个自定义的九宫格控件,作为练习作业。
效果图##
我不会做动图,就凑合看吧......
先贴自定义属性代码<declare-styleable name="DrawPointLineUnlockView"> <attr name="columns" format="integer"/> <attr name="rows" format="integer"/> <attr name="lineWidth" format="dimension"/> <attr name="pointRadius" format="dimension"/> <attr name="pointColor" format="color" /> </declare-styleable>
我把自定义属性的连线宽度,作为了九宫格的实心圆的半径和外面的空心圆的边框宽度。
贴代码
package lzp.com.interestlibrary.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import lzp.com.interestlibrary.R;
import lzp.com.interestlibrary.view.bean.DrawPoint;
/**
* Created by li.zhipeng on 16/11/17.
* <p>
* 九宫格解锁View
* <p>
* 可以定制颜色和九宫格的数量
*/
public class DrawPointLineUnlockView extends View {
/**
* 手势按下时的x坐标
*/
private float xDown = -1;
/**
* 手势按下时的y坐标
*/
private float yDown = -1;
/**
* 手势y移动时的x坐标
*/
private float xMove = -1;
/**
* 手势移动时的y坐标
*/
private float yMove = -1;
/**
* 画笔的宽度
*/
private int lineWidth = 10;
/**
* 每一个九宫格的大小
*/
private int pointRadius = 30;
/**
* 九宫格的横向个数
*/
private int columeCount = 3;
/**
* 九宫格的行数
*/
private int rowCount = 3;
/**
* 九宫格的颜色
*/
private int pointColor = Color.parseColor("#000000");
/**
* 画笔
*/
private Paint paint;
/**
* 保存所有的九宫格的点的数组
*/
private ArrayList<DrawPoint> pointsList;
/**
* 已经绘制的九宫格的点
*/
private ArrayList<DrawPoint> drawPoints;
/**
* 进行判断的点
* <p>
* 只创建一个,不返回的创建对象,节省内存
*/
private DrawPoint tempPoint;
/**
* 是否要进行绘制
*/
private boolean needDraw;
/**
* 密码
* */
private String passward;
/**
* 解锁解锁回调
* */
private OnDrawPointUnlockResultListener listener;
public void setPassward(String passward){
this.passward = passward;
}
public DrawPointLineUnlockView(Context context) {
this(context, null);
}
public DrawPointLineUnlockView(Context context, AttributeSet attrs) {
super(context, attrs);
// 获取自定义属性的值
TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.DrawPointLineUnlockView);
// 画笔的宽度
lineWidth = typedArray.getDimensionPixelSize(R.styleable.DrawPointLineUnlockView_lineWidth, 10);
// 每一个九宫格的大小
pointRadius = typedArray.getDimensionPixelSize(R.styleable.DrawPointLineUnlockView_pointRadius, 30);
// 九宫格的横向个数
columeCount = typedArray.getInt(R.styleable.DrawPointLineUnlockView_columns, 3);
// 九宫格的列数
rowCount = typedArray.getInt(R.styleable.DrawPointLineUnlockView_rows, 3);
// 九宫格的行数
pointColor = typedArray.getColor(R.styleable.DrawPointLineUnlockView_pointColor, Color.parseColor("#000000"));
typedArray.recycle();
// 防止View不进行绘制,执行onDraw()方法
setWillNotDraw(false);
pointsList = new ArrayList<>();
drawPoints = new ArrayList<>();
tempPoint = new DrawPoint(-1, pointRadius, lineWidth);
// 初始化画笔
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(pointColor);
paint.setStrokeWidth(lineWidth);
paint.setPathEffect(new CornerPathEffect(5));
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
xDown = event.getX();
yDown = event.getY();
// 判断是否这个点是九宫格的某一个点,否则不进行绘制
tempPoint.x = xDown;
tempPoint.y = yDown;
int index = pointsList.indexOf(tempPoint);
if (index != -1) {
needDraw = true;
DrawPoint point = pointsList.get(index);
drawPoints.add(point);
xDown = point.x;
yDown = point.y;
} else {
needDraw = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (needDraw) {
xMove = event.getX();
yMove = event.getY();
// 判断是否这个点是九宫格的某一个点,如果是的话,连接这个点
tempPoint.x = xMove;
tempPoint.y = yMove;
int moveIndex = pointsList.indexOf(tempPoint);
// 判断这个点是否是九宫格的点 且 这个点并没有被连接过
if (moveIndex != -1 && !drawPoints.contains(tempPoint)) {
DrawPoint point = pointsList.get(moveIndex);
drawPoints.add(point);
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (needDraw) {
xDown = -1;
yDown = -1;
xMove = -1;
yMove = -1;
// 监听回调
if (listener != null){
String unlockResult = returnPwd();
listener.onUnlock(unlockResult, unlockResult.equals(passward));
}
// 清除所有的连接点
drawPoints.clear();
// 重绘
invalidate();
}
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
// 画出所有的九宫格的点
for (DrawPoint point : pointsList) {
point.draw(canvas, paint);
}
// 如果正在解锁且已经划过了某些九宫格,画出他们之间的连线
int size = drawPoints.size() - 1;
if (needDraw && size >= 0) {
// 只有一个起点,画出第一个点和手指位置的连线
if (size == 0) {
DrawPoint point = drawPoints.get(0);
canvas.drawLine(point.x, point.y, xMove, yMove, paint);
}
// 多个点,画出点和点之间的连线
else {
for (int i = 0; i < size; i++) {
DrawPoint point = drawPoints.get(i);
DrawPoint nextPoint = drawPoints.get(i + 1);
canvas.drawLine(point.x, point.y, nextPoint.x, nextPoint.y, paint);
}
// 画出最后一个点和当前手指位置之间的连线
DrawPoint point = drawPoints.get(size);
canvas.drawLine(point.x, point.y, xMove, yMove, paint);
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 测量当前的宽高 来绘制指定个数的九宫格
float width = getWidth() - pointRadius * 2;
float height = getHeight() - pointRadius * 2;
// 获取padding值
float paddingLeft = getPaddingLeft();
float paddingRight = getPaddingRight();
float paddingTop = getPaddingTop();
float paddingBottom = getPaddingBottom();
// 求出每个点的起始位置
float xDistance = (width - paddingTop - paddingBottom) / (columeCount - 1);
float yDistance = (height - paddingLeft - paddingRight) / (rowCount - 1);
// 计算每一个九宫格的圆心
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < columeCount; j++) {
DrawPoint pointF = new DrawPoint(j + rowCount * i, pointRadius, lineWidth);
pointF.y = yDistance * i + pointRadius + paddingTop;
pointF.x = xDistance * j + pointRadius + paddingLeft;
pointsList.add(pointF);
}
}
}
/**
* 返回密码
* */
private String returnPwd(){
StringBuilder builder = new StringBuilder();
for (DrawPoint point : drawPoints){
builder.append(point.getValue());
}
return builder.toString();
}
/**
* 解锁回调
* */
public interface OnDrawPointUnlockResultListener{
/**
* @param passward 解锁的结果密码
* @param success 与设置的密码进行匹配的结果
* */
void onUnlock(String passward, boolean success);
}
public void setOnDrawPointUnlockResultListener(OnDrawPointUnlockResultListener listener){
this.listener = listener;
}
}
注释写的都是很清楚,不用做过多的解释,关于计算每一个九宫格的中心点,就画图说明一下
图太难画了 ,我们首先减去了九宫格的直径,这样均分了之后,我们就可以得到每一个九宫格的最左边的点,然后用最左边的点加上了半径就得到了x方向的圆心。在y方向上同理。
贴出DrawPoint的代码:
package lzp.com.interestlibrary.view.bean;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
/**
* Created by li.zhipeng on 16/11/18.
* <p>
* 九宫格的点
*/
public class DrawPoint extends PointF {
/**
* 外部的空心圆的半径
*/
private int pointRadius;
/**
* 内部的小实心圆的半径
*/
private int centerPointRadius;
/**
* 这个点的值,用于解锁密码的匹配
*/
private int value;
public int getValue(){
return value;
}
public DrawPoint(int value, int pointRadius, int centerPointRadius) {
this.value = value;
this.pointRadius = pointRadius;
this.centerPointRadius = centerPointRadius;
}
public void draw(Canvas canvas, Paint paint) {
// 画中心的小实心圆
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(x, y, centerPointRadius, paint);
// 画外面的空心圆
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(x, y, pointRadius, paint);
}
/**
* 重写此方法,判断是否包含这个点
*/
@Override
public boolean equals(Object o) {
if (o instanceof DrawPoint) {
// 匹配的误差是 大圆的半径
DrawPoint point = (DrawPoint) o;
if (Math.abs(this.x - point.x) < pointRadius
&& Math.abs(this.y - point.y) < pointRadius) {
return true;
}
}
return false;
}
}
MainActivity:
DrawPointLineUnlockView drawPointLineUnlockView = (DrawPointLineUnlockView) findViewById(R.id.lock);
drawPointLineUnlockView.setOnDrawPointUnlockResultListener(new DrawPointLineUnlockView.OnDrawPointUnlockResultListener() {
@Override
public void onUnlock(String passward, boolean success) {
Toast.makeText(MainActivity.this, passward, Toast.LENGTH_SHORT).show();
}
});
结尾##
如果有什么问题和好的建议欢迎大家留言批评指正,尤其是妹纸。