备注说明:通过之前的“Android图案解锁”学习,特意将实现的思路给理出来、记录下来,作为自己的学习总结。文中存在不对之处,请大家踊跃指出,转载请标明出处。
实现思路:
1、自定义LockPatterView控件,定义九宫格点的类型,实现绘制九宫格布局。
2、重写onTouchEvent()事件,完成选中点的绘制。
3、绘制两点之间的连接线。
4、图案密码的设置与验证。
效果展示:
一、自定义LockPatterView控件,定义九宫格点的类型,实现绘制九宫格布局。
1、定义九宫格中的点类型。
/**自定义九宫格点类型*/
public class Point{
public static final int STATE_NORMAL=0;//正常时
public static final int STATE_PRESS=1;//选中时
public static final int STATE_ERROR=2;//错误时
public float x,y;//点的坐标
public int state;//点的状态
public Point(float x,float y){
this.x=x;
this.y=y;
}
}
2、初始化九宫格的点坐标
/**初始化点的数据*/
private void initPoints() {
//1.得到控件的布局
float width=getWidth();
float height=getHeight();
//2.得到横竖屏时的偏移量
if(width>height){//横屏时,高度充满整个控件
offsetX=(width-height)/2;//宽度居于控件中央显示
offsetY=0;
width=height;
}else{//竖屏时,宽度充满整个控件的宽度
offsetX=0;
offsetY=(width-height)/2;//高度居于控件中央显示
height=width;
}
//图片资源
bitmap_normal = BitmapFactory.decodeResource(getResources(), R.drawable.ic_point_normal);
bitmap_pressed = BitmapFactory.decodeResource(getResources(), R.drawable.ic_point_press);
bitmap_error = BitmapFactory.decodeResource(getResources(), R.drawable.ic_point_error);
bitmap_line = BitmapFactory.decodeResource(getResources(), R.drawable.ic_line_normal);
bitmap_line_error = BitmapFactory.decodeResource(getResources(), R.drawable.ic_line_error);
//3.初始化九宫格点的坐标
for(int i=0;i<points.length;i++){
for(int j=0;j<points[i].length;j++){
points[i][j]=new Point(width/4+j*width/4+offsetX,height/4+height/4*i+offsetY);
}
}
isInit=true;
}
3、将九宫格点绘制到画布上
/**将点画在画布上*/
private void points2Canvas(Canvas canvas) {
// TODO Auto-generated method stub
//3.绘制九宫格点
for(int i=0;i<points.length;i++){
for(int j=0;j<points[i].length;j++){
Point point=points[i][j];
if(point.state==Point.STATE_NORMAL){
canvas.drawBitmap(bitmap_normal, point.x-bitmap_normal.getWidth()/2, point.y-bitmap_normal.getHeight()/2, paint);
}else if(point.state==Point.STATE_PRESS){
canvas.drawBitmap(bitmap_pressed, point.x-bitmap_pressed.getWidth()/2, point.y-bitmap_pressed.getHeight()/2, paint);
}else{
canvas.drawBitmap(bitmap_error, point.x-bitmap_error.getWidth()/2, point.y-bitmap_error.getHeight()/2, paint);
}
}
}
}
4、最终效果
二、重写onTouchEvent()事件,完成选中点的绘制。
1、判断移动的点是否属于九宫格的点,如果是,则该点属于选中的点。
/**判断两点是否重合*/
public static boolean with(float pointX,float pointY,float r,float moveX,float moveY){
return Math.sqrt((pointX-moveX)*(pointX-moveX)+(pointY-moveY)*(pointY-moveY))<r;
}
/**检查移动的点是否属于九宫格的点*/
private Point checkPoints(){
for(int i=0;i<points.length;i++){
for(int j=0;j<points[i].length;j++){
Point point=points[i][j];
if(Point.with(point.x, point.y, bitmap_normal.getWidth()/2, moveX, moveY)){
return point;
}
}
}
return null;
}
2、判断该点是否属于交叉点(之前已经添加过的点)。
/**检查是否是交叉点*/
private boolean crossPoint(Point point){
return pointList.contains(point);
}
3、完成选中点的添加。
private float moveX,moveY;//移动点的坐标
private boolean isSelect,isFinish;
private List<Point>pointList=new ArrayList<LockPatterView.Point>();
private static final int mMinPointCount=5;//最少绘制点数
@Override
public boolean onTouchEvent(MotionEvent event) {
moveX=event.getX();
moveY=event.getY();
isFinish=false;
Point point=null;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
resetPoints();
point=checkPoints();
if(point!=null){
isSelect=true;
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
isFinish=true;
isSelect=false;
break;
}
//添加点
if(!isFinish&&isSelect&&point!=null){
if(crossPoint(point)){//是交叉点
}else{//新点
point.state=Point.STATE_PRESS;
pointList.add(point);
}
}
//绘制结束
if(isFinish){
if(pointList.size()==1){//只绘制一个点
resetPoints();
}else if(pointList.size()<mMinPointCount){//小于5个点时
errorPoints();
}else{//正确的点,用于之后作为密码使用
}
}
postInvalidate();//通知onDraw()方法,重新绘制
return true;//触摸事件能够继续执行下去
}
4、最终效果。
三、绘制两点之间的连线。
1、设置连接线的缩放。
/**判断两点之间的距离*/
public static double distance(Point a,Point b){
return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
//得到线的长度
float linelength=(float) Point.distance(a, b);
if(a.state==Point.STATE_PRESS){
//设置线的缩放
matrix.setScale(linelength / bitmap_line.getWidth(), 1);
//设置线的起始位置
matrix.postTranslate(a.x - bitmap_line.getWidth() / 2 , a.y - bitmap_line.getHeight() /2);
canvas.drawBitmap(bitmap_line, matrix, paint);
}else{
matrix.setScale(linelength / bitmap_line.getWidth(), 1);
matrix.postTranslate(a.x - bitmap_line.getWidth() / 2 , a.y - bitmap_line.getHeight() /2);
canvas.drawBitmap(bitmap_line_error, matrix, paint);
}
2、得到线的旋转角度
// 获取角度
public float getDegrees(Point pointA, Point pointB) {
return (float) Math.toDegrees(Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x));
}
3、根据线的旋转角度以及线的长度,绘制出两点之间的连线。
/**画两点之间的连线*/
private void line2Canvas(Point a,Point b,Canvas canvas) {
// TODO Auto-generated method stub
//得到线的长度
float linelength=(float) Point.distance(a, b);
float degress = getDegrees(a,b);
canvas.rotate(degress, a.x, a.y);
if(a.state==Point.STATE_PRESS){
//设置线的缩放
matrix.setScale(linelength / bitmap_line.getWidth(), 1);
//设置线的起始位置
matrix.postTranslate(a.x - bitmap_line.getWidth() / 2 , a.y - bitmap_line.getHeight() /2);
canvas.drawBitmap(bitmap_line, matrix, paint);
}else{
matrix.setScale(linelength / bitmap_line.getWidth(), 1);
matrix.postTranslate(a.x - bitmap_line.getWidth() / 2 , a.y - bitmap_line.getHeight() /2);
canvas.drawBitmap(bitmap_line_error, matrix, paint);
}
canvas.rotate(-degress, a.x, a.y);
}
4、在交叉点或移动线的时候也需要绘制连接线。
//画线
if(pointList.size()>0){
Point a=pointList.get(0);
for(int i=1;i<pointList.size();i++){
Point b=pointList.get(i);
line2Canvas(a,b,canvas);
a=b;
}
if(isMoveNoPoint){//交叉点,连接线的时候
line2Canvas(a,new Point(moveX,moveY),canvas);
}
}
5、最终效果
四、图案密码的设置与验证。
1、初始化密码参数。
//初始化密码参数
int index = 1;
for(int i=0;i<points.length;i++){
for(int j=0;j<points[i].length;j++){
Point point=points[i][j];
point.index = index;
index++;
}
}
2、绘制图案后,设置密码。
String passwordStr="";
for(int i=0;i<pointList.size();i++){
passwordStr+=pointList.get(i).index;
}
3、设置密码回调监听
/**
* 图案监听器
*/
public static interface OnPatterChangeLister{
void onPatterChange(String passwordStr);
void onPatterStart(boolean isStart);
}
/**
* 设置图案监听器
* @param changeLister
*/
public void SetOnPatterChangeLister(OnPatterChangeLister changeLister){
if (changeLister != null) {
this.onPatterChangeLister = changeLister;
}
}
4、对绘制正确、绘制错误时设置图案监听返回。
//绘制结束
if(isFinis){
if(pointList.size()==1){
resetPoint();
}else if(pointList.size()<POINT_SIZE&&pointList.size()>0){
errorPoint();
onPatterChangeLister.onPatterChange(null);
}else{
if(onPatterChangeLister!=null){
String passwordStr="";
for(int i=0;i<pointList.size();i++){
passwordStr+=pointList.get(i).index;
}
onPatterChangeLister.onPatterChange(passwordStr);
}
}
}
5、根据图案密码回调进行相应处理。
mLockPatterView.SetOnPatterChangeLister(new LockPatterView.OnPatterChangeLister() {
@Override
public void onPatterChange(String passwordStr) {
if (passwordStr != null) {
if(pwdStr.equals("")){//设置密码
if(setPwd.equals("")){//第一次设置密码
setPwd=passwordStr;
pwdTv_.setText("请绘制确认密码");
mLockPatterView.resetPoint();
}else{
if(setPwd.equals(passwordStr)){
sharedPreferences.edit().putString("pwd",setPwd).commit();
Toast.makeText(MainActivity.this, "密码设置成功", Toast.LENGTH_LONG).show();
finish();
}else{
setPwd="";
pwdTv_.setText("两次密码绘制不一致,请重新绘制");
mLockPatterView.resetPoint();
}
}
}else{//验证密码
if(pwdStr.equals(passwordStr)){
Toast.makeText(MainActivity.this, "恭喜你,密码输入正确", Toast.LENGTH_LONG).show();
pwdStr="";
mLockPatterView.resetPoint();
pwdTv_.setText("请先绘制图案密码");
}else{
pwdTv_.setText("密码绘制错误,请重新绘制");
mLockPatterView.resetPoint();
}
}
} else {
pwdTv_.setText("至少5个图案");
}
}
@Override
public void onPatterStart(boolean isStart) {
}
});
6、最终效果
五、源码下载