Android中图案解锁的设计原理和实现过程

Android中图案解锁

首先要理解图案的实现原理,上一张图:

由上图,可以看出,图案中手势的记录是1-9或0-8的,保存的顺序就是密码,当然有些是可以重复的,为了安全,肯定不能直接存原顺序,一定是要加密处理的,如MD5或Hash散列。

(作者:
a day a better)

图案解锁中自定义View,是其中最为关键的一部分。下面是自定义LockView的实现,里面的注释写的很详细了。

<span style="font-size:14px;">package com.example.patternlock;

import java.util.ArrayList;
import java.util.List;

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;

public class LockView extends View {
	private boolean inited = false;
	Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);// 抗锯齿
	Paint pressPaint = new Paint(); //按下时的画笔
	Paint errorPaint = new Paint(); //错误时的画笔
	private Bitmap bitmapPointError;
	private Bitmap bitmapPointPress;
	private Bitmap bitmapPointNormal;
	private float bitmapR; // 点的半径
	private boolean isDraw = false; //是否正在绘制
	float mouseX, mouseY;
	private Point [][] points = new Point[3][3];
	private ArrayList<Point> pointList = new ArrayList<Point>(); //保存经过的点
	private ArrayList<Integer> passList = new ArrayList<Integer>();
	private OnDrawFinishedListener listener;

	public LockView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		// TODO Auto-generated constructor stub
	}

	public LockView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public LockView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	@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;

		default:
			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;
	}

	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_NORMOL;
            }
        }
		this.postInvalidate();
	}

	@Override
	protected void onDraw(Canvas canvas) {

		super.onDraw(canvas);
		if(!inited){
			init();
		}
		drawPoints(canvas);

		if(pointList.size() > 0){ //画线 2->3,3->5 ...
			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_ERROR){
			canvas.drawLine(a.x, a.y, b.x, b.y, errorPaint);
		}
		else if(a.state == Point.STATE_PRESS){
			canvas.drawLine(a.x, a.y, b.x, b.y, pressPaint);
		}
	}

	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_NORMOL){
					// normol
					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(4);
		errorPaint.setColor(Color.RED);
		errorPaint.setStrokeWidth(4);

		bitmapPointError = BitmapFactory.decodeResource(getResources(), R.drawable.error);
		bitmapPointNormal = BitmapFactory.decodeResource(getResources(), R.drawable.normal);
		bitmapPointPress = BitmapFactory.decodeResource(getResources(), R.drawable.press);

		bitmapR = bitmapPointError.getWidth() / 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 + 2*space , offsetY + space);
		points[0][2] = new Point(offsetX + 3 * space , offsetY + space);
		points[1][0] = new Point(offsetX +space, offsetY + 2 * space);
		points[1][1] = new Point(offsetX + 2 * space , offsetY + 2 * space);
		points[1][2] = new Point(offsetX + 3 * space , offsetY + 2 * space);
		points[2][0] = new Point(offsetX +space, offsetY + 3 * space);
		points[2][1] = new Point(offsetX + 2 * space , offsetY + 3 * space);
		points[2][2] = new Point(offsetX + 3 * space , offsetY + 3 * space);

		inited = true; //初始化完成
	}

	public interface OnDrawFinishedListener{
		boolean OnDrawFinished(List<Integer> passList);
	}

	public void setOnDrawFinishedListener(OnDrawFinishedListener listener){
		this.listener = listener;
	}

}
</span>

设置密码:

<span style="font-size:14px;">package com.jikexueyuan.screenlock;

import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.util.List;

public class SettingActivity extends ActionBarActivity {

    List<Integer> passList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_setting);
        final GestureLock lock = (GestureLock)findViewById(R.id.LockView);
        Button btn_reset = (Button)findViewById(R.id.btn_reset);
        Button btn_save = (Button)findViewById(R.id.btn_save);

        lock.setOnDrawFinishedListener(new GestureLock.OnDrawFinishedListener() {
            @Override
            public boolean OnDrawFinished(List<Integer> passList) {
                if (passList.size() < 3)
                {
                    Toast.makeText(SettingActivity.this, "密码不能少于3个点", Toast.LENGTH_SHORT).show();
                    return false;
                }
                else {
                    SettingActivity.this.passList = passList;
                    return true;
                }
            }
        });

        btn_reset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                lock.resetPoints();
            }
        });

        btn_save.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (passList != null)
                {
                    StringBuilder sb = new StringBuilder();
                    for (Integer i : passList)
                    {
                        sb.append(i);
                    }
                    SharedPreferences sp = SettingActivity.this.getSharedPreferences("password", SettingActivity.this.MODE_PRIVATE);
                    SharedPreferences.Editor editor = sp.edit();
                    editor.putString("password", sb.toString());
                    editor.commit();

                    Toast.makeText(SettingActivity.this, "保存完成", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    @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_setting, 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);
    }
}
</span>

检验密码:

package com.example.patternlock;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Toast;

import java.util.List;

import com.example.patternlock.LockView.OnDrawFinishedListener;

public class LockActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lock);
        SharedPreferences sp = getSharedPreferences("password", this.MODE_PRIVATE);
        final String password = sp.getString("password", "");

        LockView lock = (LockView)findViewById(R.id.LockView);
        lock.setOnDrawFinishedListener(new OnDrawFinishedListener() {
            @Override
            public boolean OnDrawFinished(List<Integer> passList) {
                StringBuilder sb = new StringBuilder();
                for (Integer i : passList)
                {
                    sb.append(i);
                }
                if (sb.toString().equals(password)){
                    Toast.makeText(LockActivity.this, "正确", Toast.LENGTH_SHORT).show();
                    return true;
                }
                else
                {
                    Toast.makeText(LockActivity.this, "错误", Toast.LENGTH_SHORT).show();
                    return false;
                }
            }
        });
    }
}

详细代码和注释在下面的链接中,欢迎大家学习。

http://download.csdn.net/detail/adayabetter/8923241

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-17 17:29:19

Android中图案解锁的设计原理和实现过程的相关文章

Android中图案锁的实现

原文地址:http://blog.csdn.net/liusiqian0209/article/details/50372448 很多品牌的Android手机都实现了图案解锁屏幕的功能,有些应用程序出于保护的目的也使用了图案锁(比如支付宝),本文将介绍一种图案锁的实现方式,这种实现的一个优势在于方便扩展和自定义,我们先看一下效果图. 首先是连线阶段,整个连线为两部分:第一部分是点和点之间的固定线段,第二部分是最后一个点到鼠标移动位置的自由线段. 接下来是连线结束之后,需要判断图案是否正确,我这里

Android中热修复框架Robust原理解析+并将框架代码从&quot;闭源&quot;变成&quot;开源&quot;(下篇)

一.回顾框架原理 本篇继续来看热修复框架Robust原理,在之前的一篇文章中已经详细讲解了:Robust框架原理,因为这个框架不是开源的,所以通过官方给出的原理介绍,咋们自己模拟了案例和框架逻辑的简单实践.最后在通过反编译美团app进行验证咋们的逻辑实现是否大致不差.最终确定实践的逻辑大同小异.但是在上一篇文章末尾多次强调了,这个框架吸引我研究的不是他热修复技术,而是他有一个技术点,就是如何在编译期给每个类每个方法都加上修复功能代码,对于上层开发代码是透明的.因为从之前案例可以看到,如果方法没有

Android中Input型输入设备驱动原理分析(一)

转自:http://blog.csdn.net/eilianlau/article/details/6969361 话说Android中Event输入设备驱动原理分析还不如说Linux输入子系统呢,反正这个是没变的,在android的底层开发中对于Linux的基本驱动程序设计还是没变的,当然Android底层机制也增加几个属于android自己的机制.典型的IPC Android中的input设备驱动主要包括:游戏杆(joystick).鼠标(mouse)和事件设备(Event). 1.Inpu

Android中HandlerThread的使用及原理解析

关于Hanlder的基本使用可以参见博文<Android中Handler的使用>,如果想了解Handler.Looper.Thread等的相互关系以及内部实现原理可以参见博文<深入源码解析Android中的Handler,Message,MessageQueue,Looper>. Android中的API中对HandlerThread的描述是: Handy class for starting a new thread that has a looper. The looper c

Android中Intent对象与Intent Filter过滤匹配过程详解

如果对Intent不是特别了解,可以参见博文<Android中Intent概述及使用>,该文对本文要使用的action.category以及data都进行了详细介绍. 本文内容有点长,希望大家可以耐心读完. 本文在描述组件在manifest中注册的Intent Filter过滤器时,统一用intent-filter表示. 概述 我们知道,Intent是分两种的:显式Intent和隐式Intent.如果一个Intent明确指定了要启动的组件的完整类名,那么这个Intent就是显式Intent,否

在Android中实现来自Material设计的Ripple效果:RippleEffect

网络示例图: RippleView.java 1 /* 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2014 Robin Chutaux 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software

Android中java文件与XML文件的协作过程

android是使用XML布局文件来定义界面,不是用Java代码来定义界面,所以说所有组件都提供了两种方式来控制组件的行为. 1. 在XML布局文件中通过XML属性进行时控制 2. 在Java程序代码中通过调用方法进行时控制. 实际上不管使用哪种方式,他们控制android用户界面行为的本质是完全一样的.大部分时候,控制UI组件的XML属性还有对应的方法. 对于View类而言,他是所有UI组件的基类,因此他包含的XML属性和方法时所有组件可以使用的.XML文件布局相对简单,常用的几种属性练熟就基

使用appium进行图案解锁

对于应用中图案解锁的功能,使用appium可以很好的完成这个功能. 这里以锁屏时图案解锁为例: 图案设置为: 在该锁屏界面解锁: 获取图案中9个点的坐标时,可以使用sdk tools目录下的hierarchyviewer工具获取. 下面贴上图案解锁的代码: package com.xuxu.autotest; import java.net.URL; import io.appium.java_client.AppiumDriver; import io.appium.java_client.T

Android中BroadcastReceiver的两种注册方式(静态和动态)详解

今天我们一起来探讨下安卓中BroadcastReceiver组件以及详细分析下它的两种注册方式. BroadcastReceiver也就是"广播接收者"的意思,顾名思义,它就是用来接收来自系统和应用中的广播.在Android系统中,广播体现在方方面面,例如当开机完成后系统会产生一条广播,接收到这条广播就能实现开机启动服务的功能:当网络状态改变时系统会产生一条广播,接收到这条广播就能及时地做出提示和保存数据等操作:当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户