效果图:
入口:
设置图案密码:
测试图案密码是否正确
自定义控件:GestureLock
package com.example.zhh.shoushimima4;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义图案解锁控件
*/
public class GestureLock extends View {
// 存放点对象的数组
private Point[][] points = new Point[3][3];
// 判断是否初始化,onDraw方法会被多次调用,我只要初始化一次
private boolean inited = false;
// 三种状态下点对应的Bitmap;用于绘制图片
private Bitmap bitmapPointError;
private Bitmap bitmapPointNormal;
private Bitmap bitmapPointPress;
// 手指点的x,y
float mouseX, mouseY;
// 圆圈的半径
private float bitmapR;
// 画笔,用来绘制图片
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 画笔,绘制正确的线
Paint pressPaint = new Paint();
// 画笔,绘制错误的线
Paint errorPaint = new Paint();
// 标记当前是否在绘制状态
private boolean isDraw = false;
// 选中点的集合
private ArrayList<Point> pointList = new ArrayList<Point>();
// 存储点的位置,是0到8的整数值
private ArrayList<Integer> passList = new ArrayList<Integer>();
// 绘制完成的监听对象(用来接口传参用的)
private OnDrawFinishedListener listener;
/**
* 构造方法
*
* @param context
*/
public GestureLock(Context context) {
super(context);
}
public GestureLock(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GestureLock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 绘制
*
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 初始化(只能在这里初始化)
if (!inited) {
init();
}
// 绘制点
drawPoints(canvas);
// 绘制线
// 判断有点时,绘制
if (pointList.size() > 0) {
Point a = pointList.get(0);
for (int i = 1; i < pointList.size(); i++) {
Point b = pointList.get(i);
// 绘制a到b线
drawLine(canvas, a, b);
a = b;
}
// 最后一个点,和手指按下点的连线
if (isDraw) {
drawLine(canvas, a, new Point(mouseX, mouseY));
}
}
}
/**
* 初始化方法
*/
private void init() {
pressPaint.setColor(Color.YELLOW);
pressPaint.setStrokeWidth(5);
errorPaint.setColor(Color.RED);
errorPaint.setStrokeWidth(5);
// 初始化三种状态对应的位图
bitmapPointError = BitmapFactory.decodeResource(getResources(), R.drawable.error);
bitmapPointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
bitmapPointPress = BitmapFactory.decodeResource(getResources(), R.drawable.press);
// 计算出图片的半径
bitmapR = bitmapPointError.getHeight() / 2;
// 手机屏幕的宽
int width = getWidth();
// 手机屏幕的高度
int height = getHeight();
// 整个九宫格的偏移量,保证横竖配屏都在中间,求得是宽高差的绝对值,这是个公式
int offset = Math.abs(width - height) / 2;
// 定义横纵坐标的偏移量
int offsetX, offsetY;
// 小方格的边长
int space;
if (width > height) {
// 横屏状态
space = height / 4;
offsetX = offset;
offsetY = 0;
} else {
// 竖屏状态
space = width / 4;
offsetX = 0;
offsetY = offset;
}
// 实例化点对象,并设置点位置,并存入数组
points[0][0] = new Point(offsetX + space, offsetY + space);
points[0][1] = new Point(offsetX + space * 2, offsetY + space);
points[0][2] = new Point(offsetX + space * 3, offsetY + space);
points[1][0] = new Point(offsetX + space, offsetY + space * 2);
points[1][1] = new Point(offsetX + space * 2, offsetY + space * 2);
points[1][2] = new Point(offsetX + space * 3, offsetY + space * 2);
points[2][0] = new Point(offsetX + space, offsetY + space * 3);
points[2][1] = new Point(offsetX + space * 2, offsetY + space * 3);
points[2][2] = new Point(offsetX + space * 3, offsetY + space * 3);
// 防止重复初始化
inited = true;
}
/**
* 绘制点
*
* @param canvas
*/
private void drawPoints(Canvas canvas) {
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
if (points[i][j].state == Point.STATE_NORMAL) {
//Normal 正常状态 绘制图片
canvas.drawBitmap(bitmapPointNormal, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
} else if (points[i][j].state == Point.STATE_PRESS) {
//Press 按下状态 绘制图片
canvas.drawBitmap(bitmapPointPress, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
} else {
//ERROR 错误状态 绘制图片
canvas.drawBitmap(bitmapPointError, points[i][j].x - bitmapR, points[i][j].y - bitmapR, paint);
}
}
}
}
/**
* 判断选择的点
* 就是手指点下的地方属于哪个点
*
* @return
*/
private int[] getSelectedPoint() {
Point pMouse = new Point(mouseX, mouseY);
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
// 根据按的位置,判断是否属于这个点,
// 按下的位置的距离小于半径,认为按得就是这个点
// 按下位置的距离距圆心,小于半径,认为点的就是这个点
if (points[i][j].distance(pMouse) < bitmapR) {
// 选中点在数组中的位置
int[] result = new int[2];
result[0] = i;
result[1] = j;
return result;
}
}
}
return null;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mouseX = event.getX();
mouseY = event.getY();
int[] ij;
int i, j;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 将所有得点,设置为初始状态
resetPoints();
ij = getSelectedPoint();
// 不是空,说明有选中的点
if (ij != null) {
// 标记为绘制状态
isDraw = true;
i = ij[0];
j = ij[1];
// 选中点标记为按下状态
points[i][j].state = Point.STATE_PRESS;
// 把选中的点放在集合中,就是起点
pointList.add(points[i][j]);
// 二维数组,转化一维数组的公式
passList.add(i * 3 + j);
}
break;
case MotionEvent.ACTION_MOVE:
// 判断处于绘制状态
if (isDraw) {
ij = getSelectedPoint();
if (ij != null) {
// 不是空,说明手指又碰到了一个点
i = ij[0];
j = ij[1];
// 判断选中的点是否在已经在集合中,如果没有就添加
// 每个点之后能使用一次
if (!pointList.contains(points[i][j])) {
points[i][j].state = Point.STATE_PRESS;
pointList.add(points[i][j]);
// 二位数组,转化成一维数组的公式
passList.add(i * 3 + j);
}
}
}
break;
case MotionEvent.ACTION_UP:
// 是否绘制正确
boolean valid = false;
// 判断是否在绘制状态
if (listener != null && isDraw) {
valid = listener.OnDrawFinished(passList);
}
// 绘制错误
if (!valid) {
for (Point p : pointList) {
p.state = Point.STATE_ERROR;
}
}
// 不在绘制
isDraw = false;
break;
}
this.postInvalidate();
return true;
}
/**
* 绘制连线
*
* @param canvas
* @param a
* @param b
*/
private void drawLine(Canvas canvas, Point a, Point b) {
// 判断第一个手否为按下状态
if (a.state == Point.STATE_PRESS) {
canvas.drawLine(a.x, a.y, b.x, b.y, pressPaint);
} else if (a.state == Point.STATE_ERROR) {
canvas.drawLine(a.x, a.y, b.x, b.y, errorPaint);
}
}
/**
* 设置点的初始状态
* 手滑动一次后,重新绘制
* 重新绘制点
*/
public void resetPoints() {
// 清空集合
passList.clear();
// 清空选中点的集合
pointList.clear();
for (int i = 0; i < points.length; i++) {
for (int j = 0; j < points[i].length; j++) {
// 把所有的点的状态设置为,未点击之前的状态
points[i][j].state = Point.STATE_NORMAL;
}
}
// 重绘
this.postInvalidate();
}
/**
* 绘制完成的方法
* @param listener
*/
public void setOnDrawFinishedListener(OnDrawFinishedListener listener) {
this.listener = listener;
}
/**
* 绘制完成的监听
*/
public interface OnDrawFinishedListener {
// 绘制图案是否正确
// true 正确
// false 错误
boolean OnDrawFinished(List<Integer> passList);
}
}
实体类Point
package com.example.zhh.shoushimima4;
/**
* Created by 泽群 on 2015/6/20.
* 保存9个点
*/
public class Point {
// 正常状态
public static int STATE_NORMAL = 0;
// 按下状态
public static int STATE_PRESS = 1;
// 错误状态
public static int STATE_ERROR = 2;
// 当前点的x值
float x;
// 当前点的y值
float y;
// 保存当前点的状态
int state = STATE_NORMAL;
// 构造函数,构造点
public Point(float x, float y) {
this.x = x;
this.y = y;
}
/**
* 计算距离的方法
* @param a
* @return
*/
public float distance(Point a) {
// 计算距离的公式
float distance = (float) Math.sqrt((x - a.x) * (x - a.x) + (y - a.y) * (y - a.y));
return distance;
}
}
入口 MainActivity
package com.example.zhh.shoushimima4;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
private Context context;
// 设置按钮
private Button btnSheZhi;
// 测试按钮
private Button btnCeShi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initClick();
}
/**
* 初始化控件
*/
private void initView() {
context = MainActivity.this;
btnSheZhi = (Button) findViewById(R.id.btnSheZhi);
btnCeShi = (Button) findViewById(R.id.btnCeShi);
}
/**
* 点击事件
*/
private void initClick(){
btnSheZhi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, SettingActivity.class);
startActivity(intent);
}
});
btnCeShi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, LockActivity.class);
startActivity(intent);
}
});
}
}
布局 activity_main
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
android:gravity="center"
>
<LinearLayout
android:layout_width="200dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center_vertical|center_horizontal"
>
<Button
android:id="@+id/btnSheZhi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="设置图案密码"
>
</Button>
<Button
android:id="@+id/btnCeShi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="测试图案密码"
android:layout_marginTop="50dp"
/>
</LinearLayout>
<!--<com.example.zhh.shoushimima4.GestureLock-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content">-->
<!--</com.example.zhh.shoushimima4.GestureLock>-->
</LinearLayout>
设置图案密码: SettingActivity
package com.example.zhh.shoushimima4;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.util.List;
/**
* 设置页面
*/
public class SettingActivity extends Activity {
private Context context;
// 绘制点的集合
private List<Integer> passList;
// 图案控件
private GestureLock gestureLock;
// 重置
private Button btnReset;
// 保存
private Button btnSave;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setting);
initView();
initClick();
}
/**
* 初始化控件
*/
private void initView() {
context= SettingActivity.this;
gestureLock = (GestureLock) findViewById(R.id.gestureLock);
btnReset = (Button) findViewById(R.id.btnReset);
btnSave = (Button) findViewById(R.id.btnSave);
}
/**
* 控件点击事件
*/
private void initClick() {
// 绘制是否结束
gestureLock.setOnDrawFinishedListener(new GestureLock.OnDrawFinishedListener() {
@Override
public boolean OnDrawFinished(List<Integer> passList) {
// 绘制结束时,判断绘制的点小于3,则提示不能少于3个点
// 大于3 则说明绘制成功,把绘制的点的list,提成全局变量
if (passList.size() < 3) {
Toast.makeText(SettingActivity.this, "密码不能少于3个点", Toast.LENGTH_SHORT).show();
return false;
} else {
SettingActivity.this.passList = passList;
return true;
}
}
});
// 重置
btnReset.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 重新设置点
gestureLock.resetPoints();
}
});
// 保存
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// passList 集合转成字符串,并保存
if (passList != null) {
StringBuilder sb = new StringBuilder();
for (Integer i : passList) {
sb.append(i);
}
SharedPreferences sp = SettingActivity.this.getSharedPreferences("password", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("password", sb.toString());
editor.commit();
Toast.makeText(SettingActivity.this, "保存完成", Toast.LENGTH_SHORT).show();
finish();
}
}
});
}
}
布局文件:activity_setting
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.zhh.shoushimima4.SettingActivity">
<com.example.zhh.shoushimima4.GestureLock
android:id="@+id/gestureLock"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:orientation="horizontal">
<Button
android:id="@+id/btnReset"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
android:text="重置密码" />
<Button
android:id="@+id/btnSave"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_weight="1"
android:text="保存密码" />
</LinearLayout>
</RelativeLayout>
测试图案密码:LockActivity
package com.example.zhh.shoushimima4;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;
import java.util.List;
/**
* 测试页面
*/
public class LockActivity extends Activity {
// 图案控件
private GestureLock gestureLock;
// 拿到保存的密码
private String password;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lock);
initView();
initOnclick();
}
/**
* 初始化控件
*/
private void initView() {
gestureLock = (GestureLock) findViewById(R.id.gestureLock);
// 拿到保存的密码
SharedPreferences sp = getSharedPreferences("password", Context.MODE_PRIVATE);
password = sp.getString("password", "");
}
/**
* 事件
*/
private void initOnclick() {
// 绘制结束的事件
gestureLock.setOnDrawFinishedListener(new GestureLock.OnDrawFinishedListener() {
@Override
public boolean OnDrawFinished(List<Integer> passList) {
// 拿到绘制的点的list,并和保存的对比,如果一致则,正确,否则错误
StringBuilder sb = new StringBuilder();
for (Integer i : passList) {
sb.append(i);
}
if (sb.toString().equals(password)) {
Toast.makeText(LockActivity.this, "正确", Toast.LENGTH_SHORT).show();
fuwei();
return true;
} else {
Toast.makeText(LockActivity.this, "错误", Toast.LENGTH_SHORT).show();
fuwei();
return false;
}
}
});
}
/**
* 开线程,停留1秒,复位图案
*/
private void fuwei(){
// 开线程,停留1秒,然后发消息给handler,复位图案
new Thread(){
@Override
public void run(){
try {
Thread.sleep(1000);
handler.sendEmptyMessage(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
/**
* 接收复位消息
*/
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
// 复位
gestureLock.resetPoints();
}
};
}
布局文件activity_lock
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.zhh.shoushimima4.LockActivity">
<com.example.zhh.shoushimima4.GestureLock
android:id="@+id/gestureLock"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
源码下载:
https://download.csdn.net/download/zhaihaohao1/10667418
参考视频:
https://www.jikexueyuan.com/course/1592.html