前言
我们都知道安卓系统有个自带的图案解锁,这些年也被很多前辈们自定义过了,之前我看过慕课网上有一堂课程,感觉他的实现有点麻烦,而且不太完善,最近项目用到 于是自己重新写了下,实现的方式有很多,感觉我的实现还是可以,简单易懂吧,也不敢说是最佳实践 哈哈 有不足的地方 欢迎大家指正。
效果
功能
参考了系统的实现需求 纯代码手工制作 ^_^
- 小于4个点 代表错误 会红色高亮提示 2秒后移除 也可以手动重输
- 连接过得点不能再次连接
- 途径中间的点会自动连接(横、竖、斜的方向)
- 设置密码 接口回调 就没写了 so easy
代码(很少 全贴上了)
/**
* Created by Administrator on 2016/11/14.
* author:Zcoder2013
* blog:http://blog.csdn.net/u011507982
*/
public class LockPatternView extends View{
private Paint pointPaint,linePaint; //画笔
private List<Point> allPointList= new ArrayList<>(); //所有点的集合
private List<Point> currentPointList = new ArrayList<>(); //选中点的集合
private float moveX,moveY; //手指移动坐标
private final int radius = 12; //半径
private final int bigRadius = 42; //外圆半径
private final int count = 3; //矩阵 3*3
private boolean isActionUp = false; //手指是否抬起
private int state; //状态
private static final int STATE_PRESS = 1; // 手指按下
private static final int STATE_ERROR = 2;// 选中的密码错误或密码太短,太长
public LockPatternView(Context context) {
super(context);
}
public LockPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init(){
// 初始化点的画笔
pointPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
pointPaint.setStrokeWidth(2f);
pointPaint.setStyle(Paint.Style.STROKE);
pointPaint.setAntiAlias(true); // 抗锯齿
// 初始化线的画笔
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setStrokeWidth(radius-2f);
linePaint.setColor(Color.parseColor("#A9A9A9"));
linePaint.setAntiAlias(true); // 抗锯齿
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//测量 设置当前view宽高一样->正方形
int specSize = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(specSize,specSize);
}
int size;
@Override
protected void onDraw(Canvas canvas) {
//获取当前view的宽度
size = getWidth();
//画笔颜色
pointPaint.setColor(Color.BLACK);
//初始化所有点的集合并画9个点
allPointList.clear();
for (int i = 1; i <= count; i++) {
for (int j = 1; j <= count; j++) {
allPointList.add(new Point(size * i / 4, size * j / 4));
canvas.drawCircle(size * i / 4, size * j / 4, radius, pointPaint);
}
}
// 绘制已有的连线
for (int i = 1; i< currentPointList.size(); i++) {
Point firstPoint = currentPointList.get(i - 1);
Point secondPoint = currentPointList.get(i);
canvas.drawLine(firstPoint.x, firstPoint.y, secondPoint.x, secondPoint.y, linePaint);
}
//绘制当前点和手指移动的连线
if (currentPointList.size() > 0 && !isActionUp) {
Point point = currentPointList.get(currentPointList.size() - 1);
canvas.drawLine(point.x,point.y,moveX,moveY, linePaint);
}
//绘制当前状态下的点
for (Point point:currentPointList) {
if(state==STATE_PRESS){
//实心画小圆
pointPaint.setColor(Color.parseColor("#006400"));
pointPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(point.x,point.y, radius+1, pointPaint);
//空心画大圆
pointPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(point.x,point.y, bigRadius, pointPaint);
}
if(state==STATE_ERROR){
pointPaint.setColor(Color.RED);
pointPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(point.x,point.y, radius+1, pointPaint);
pointPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(point.x,point.y, bigRadius, pointPaint);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//当手指按下重置所有的点
resetPoint();
isActionUp = false;
state = STATE_PRESS;
case MotionEvent.ACTION_MOVE:
moveX = event.getX(); // 这里要实时记录手指的坐标
moveY = event.getY();
int pointSize = currentPointList.size();
//获得当前选中的点
Point point = getCurrentPoint(moveX,moveY);
//判断点是选中的 并且 没有添加到选中的集合中
if(null!=point&&!isContainPoint(point)){
//判断是否已经有了选中的点
if(pointSize>0){
//获取集合里面最后一个选中的点
Point prePoint = currentPointList.get(pointSize-1);
//用当前点和之前的点判断出是否有中间点
Point middlePoint = getMiddlePoint(prePoint,point);
//如果有中间点 加入选中集合 (自动选中中间点的功能)
if(null!=middlePoint){
currentPointList.add(middlePoint);
}
}
//将选中点加入集合
currentPointList.add(point);
}
//重绘
invalidate();
break;
case MotionEvent.ACTION_UP:
isActionUp = true;
pointSize = currentPointList.size();
//这里判断选中点的数量小于4个 则表示错误
if(pointSize<4){
state = STATE_ERROR;
}
invalidate();
//两秒以后清除选中的点
clearPoint();
break;
}
return true;
}
Runnable runnable;
//两秒以后清除选中的点
private void clearPoint(){
runnable = new Runnable() {
@Override
public void run() {
currentPointList.clear();
invalidate();
}
};
postDelayed(runnable,2000);
}
//重置
private void resetPoint(){
if(null!=runnable) removeCallbacks(runnable);
currentPointList.clear();
}
//获取选中的点
private Point getCurrentPoint(float moveX, float moveY) {
for(Point point:allPointList){
//两点之间的距离<=外圆半径
if( Math.sqrt( Math.pow((moveX-point.x),2) + Math.pow((moveY-point.y),2) )<=bigRadius){
return point;
}
}
return null;
}
//判断选中的点是否在选中集合里
public boolean isContainPoint(Point currentPoint){
for(Point point:currentPointList){
if(point.x==currentPoint.x&&point.y==currentPoint.y){
return true;
}
}
return false;
}
//获取中间的点
private Point getMiddlePoint(Point prePoint, Point nextPoint) {
//先拿到距离
double distance = Math.sqrt( Math.pow((nextPoint.x - prePoint.x),2) + Math.pow((nextPoint.y - prePoint.y),2) );
//斜着的情况
if(Math.abs(nextPoint.x - prePoint.x)==size/2
&&Math.abs(nextPoint.y - prePoint.y)==size/2){
return new Point(size/2,size/2);
};
//横着的情况
if(Math.abs(nextPoint.x - prePoint.x)==size/2&&distance==size/2){
return new Point(size/2,nextPoint.y);
};
//竖着的情况
if(Math.abs(nextPoint.y - prePoint.y)==size/2&&distance==size/2){
return new Point(nextPoint.x,size/2);
};
return null;
}
class Point{
int x,y;
public Point(int x,int y){
this.x = x;
this.y = y;
}
}
}
GitHub
更多精彩 关注博客 分类下的合集 持续更新哟~