序言 第一次写Android技术博客,不知道该如何下手。
背景 现在人们越来越重视自己的隐私,对于一些涉及用户隐私的应用,用户可能会希望在应用启动时必须先输入密码。传统的数字式密码记忆繁琐、容易破解,而图案解锁则可以解决这个问题。因此,在应用中添加图案解锁功能,可以提高应用的可靠性,获得用户的信任。
今天,我要介绍的是Android 的屏幕9宫格图形解锁,如果有什么不对的地方,望各位批评指正。直接上代码
1. 首先自定义图形密码控件,继承自View,创建并绘制9个点,并重写onTouchEvent方法。
public class GestureLock extends View {
private Point[][] points = new Point[3][3];
private boolean inited = false;
private boolean isDraw = false;
private ArrayList<Point> pointList = new ArrayList<Point>();
private ArrayList<Integer> passList = new ArrayList<Integer>();
private Bitmap bitmapPointError;
private Bitmap bitmapPointNormal;
private Bitmap bitmapPointPress;
private OnDrawFinishedListener listener;
float mouseX, mouseY;
private float bitmapR;
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint pressPaint = new Paint();
Paint errorPaint = new Paint();
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);
}
@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;
}
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
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);
drawLine(canvas, a, b);
a = b;
}
if (isDraw)
{
drawLine(canvas, a, new Point(mouseX, mouseY));
}
}
}
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);
}
}
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);
}
}
}
}
private void init()
{
pressPaint.setColor(Color.YELLOW);
pressPaint.setStrokeWidth(5);
errorPaint.setColor(Color.RED);
errorPaint.setStrokeWidth(5);
bitmapPointError = BitmapFactory.decodeResource(getResources(), R.mipmap.error);
bitmapPointNormal = BitmapFactory.decodeResource(getResources(), R.mipmap.normal);
bitmapPointPress = BitmapFactory.decodeResource(getResources(), R.mipmap.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;
}
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();
}
public interface OnDrawFinishedListener
{
boolean OnDrawFinished(List<Integer> passList);
}
public void setOnDrawFinishedListener(OnDrawFinishedListener listener)
{
this.listener = listener;
}
}
辅助类 Point.java
public class Point {
public static int STATE_NORMAL = 0;
public static int STATE_PRESS = 1;
public static int STATE_ERROR = 2;
float x;
float y;
int state = STATE_NORMAL;
public Point(float x, float y)
{
this.x = x;
this.y = y;
}
public float distance(Point a)
{
float distance = (float)Math.sqrt((x - a.x) * (x - a.x) + (y - a.y) * (y - a.y));
return distance;
}
}
2.MainActivity.java 设置和检验密码锁。
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn_setting = (Button)findViewById(R.id.button);
Button btn_lock = (Button)findViewById(R.id.button2);
btn_setting.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SettingActivity.class);
startActivity(intent);
}
});
btn_lock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, LockActivity.class);
startActivity(intent);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
大体核心代码就是这些,后面有demo链接,可根据需要自行下载。