您的位置:首页 > 移动开发 > Android开发

Android手势锁屏界面

2016-01-06 16:34 537 查看
最近在学习Android的自定义控件的知识,因为经常见App中有手势锁屏的功能,所以用自定义控件的方法制作了一个简单的手势锁屏的App,(逻辑简单,代码很容易理解)并且添加了一些实际的小功能进行了测试。本来想要制作成一个gif图像在此演示,因为时间的问题就不做了,以下为主要的代码:

启动APP时进入的界面



设置密码时的界面:



密码错误时 :



密码不符合规则时:



大体就是这样的,因为图片资源找的不是很好,所以最终呈现的效果不是很好,但完成了基本的功能。程序总体架构



首先是锁频界面的设计(自定义控件 由 LockPatternView 继承View类)

package com.example.lock;

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.Matrix;
import android.graphics.Paint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class LockPatternView  extends  View{

private static final String TAG = null;
//创建全局对象
judgeflag   signal ;
//创建画笔
Paint  paint  = new Paint(Paint.ANTI_ALIAS_FLAG);
OnpatternchangeListener  listener ;
//9个点
private    Point   points [][]= new Point[3][3];
private   boolean    isinit  , isselected , isover;                                              //判断是否初始化
private   float    Width, Height, MoveX , MoveY;                                  //获取屏幕的宽,高
private float   offsetsX  ;                                                 //偏移量 X
private   float   offsetsY;                                                    //偏移量 Y
private  float   BitmapRadious;                                   //图片的半径
private  List<Point>  listpoint  = new ArrayList<Point>();          //用来存放按下的点
private   Bitmap    normalpoint, errorpoint, pressedpoint , linepoint ,lineerropoint;
private   boolean  MovingNopoint;

private   Matrix  matrix = new Matrix();     //矩阵实现图片的缩放,旋转

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

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

public LockPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
//类名前加static,调用不用实例化
public    static   class   Point {
//三种状态
public   static  int    STATE_NORMAL = 0;
public   static  int    STATE_SELECED = 1;
public  static  int    STATE_ERROR = 2;

//点的横坐标,纵坐标
public     float    x;
public    float    y;
public      int   index = 0, state = 0;
public     Point(){

}

public   Point(float  X,  float Y){
this.x = X;
this.y = Y;
}

//判断重合的方法,两个点之间的距离在一定的范围内则认为这两个点是一起的,大致认为
public  static   Boolean  with(float x , float y , float R ,float MoveX, float MoveY){
return  Math.sqrt(    (x - MoveX) * (x - MoveX)  +  ( y - MoveY)  * (y - MoveY)   ) < R;
}

//两点之间的距离,用于求缩放比例
public   static   double   distance(Point a ,Point b){
return  Math.sqrt(    Math.abs( a.x - b.x) * Math.abs(a.x - b.x)  + Math.abs(a.y - b.y)  * Math.abs(a.y - b.y) ) ;
}

}
/**
* 图案监听器,在OnTouchEvent事件上调用
* @author lenovo
*
*/

//在OnDraw中画出点与线
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
//需要初始化点,然后进行绘制
if(!isinit){
initPoints();                         //初始化点的函数
}
point2Canvas(canvas);     //把点画出来

if(listpoint .size() > 0){       //把线画出来
Point a = listpoint.get(0);     //把a点取出

for(int i = 0  ; i < listpoint.size() ; i++){
Point  b = listpoint.get(i);
//绘制九宫格中的点
linetoCanvas(canvas, a, b);
a = b;
}

//绘制九宫格之外的坐标点
if(MovingNopoint){
linetoCanvas(canvas, a, new Point(MoveX,MoveY));
}
}
else{

}
}

/**
* 将点绘制在画布上
* @param canvas  画布
*/

private void point2Canvas(Canvas canvas) {
// TODO Auto-generated method stub
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(normalpoint, point.x - BitmapRadious, point.y - BitmapRadious, paint );
}
else if(point.state == Point.STATE_ERROR){
canvas.drawBitmap(errorpoint, point.x  - BitmapRadious, point.y - BitmapRadious, paint);

}
else{
canvas.drawBitmap(pressedpoint, point.x  - BitmapRadious, point.y  - BitmapRadious, paint);
}

}
}
}
/**
*
* @param canvas  画布
* @param a   第一个点
* @param b  第二个点
*/
public  void  linetoCanvas(Canvas  canvas  , Point a , Point b){

double   linelength = Point.distance(a, b);
float   degress = getDegress(a,b);
canvas.rotate(degress, a.x,a.y);                        //让画布旋转,rotate这个方法旋转的中心是绕着当前点旋转
if(a.state == Point.STATE_SELECED){
//matrix中的两种方法
matrix.setScale((float)linelength  / linepoint.getWidth(),  1) ;               //x轴,Y轴的缩放  X轴的缩放比例,Y轴不需要缩放则是1,在canvas上能够显示
matrix.postTranslate(a.x - linepoint.getWidth() /2,a.y - linepoint.getHeight() /2);    //平移,从当前位置开始画,也就是点击的位置为画的起点
canvas.drawBitmap(linepoint, matrix, paint);                                         //canvas相当于一个透明图层,每次draw时是画在图层上,相当于PS总的图层,然后最终合成在一起
}
else{
matrix.setScale((float)linelength  / lineerropoint.getWidth(),  1) ;               //x轴,Y轴的缩放  X轴的缩放比例,Y轴不需要缩放则是1
//   matrix.postTranslate(a.x - lineerropoint.getWidth() /5,a.y - lineerropoint.getHeight() /5);
matrix.postTranslate(a.x - linepoint.getWidth() /2,a.y - linepoint.getHeight() /2);
//	    matrix.postTranslate(a.x -7, a.y - 7);
canvas.drawBitmap(lineerropoint, matrix, paint);
}

canvas.rotate( - degress,a.x,a.y);       //把画布在旋转回来
}

/**
* 初始化点的函数
*/
private void initPoints() {
// TODO Auto-generated method stub
Width = getWidth();                          //获取屏幕宽,高
Height = getHeight();

//判断横屏还是竖屏

if(Width > Height){
//横屏
offsetsX = (Width  - Height )/2;                    //偏移到画圆圈的地方
Width  = Height;                                                 //图形的宽高一样
}
else{
//竖屏
offsetsY = (Height - Width )/2;
Height  = Width;
}

//图片资源
normalpoint = BitmapFactory.decodeResource(getResources(), R.drawable.green);
pressedpoint =  BitmapFactory.decodeResource(getResources(), R.drawable.yellow);
errorpoint =  BitmapFactory.decodeResource(getResources(), R.drawable.red);
linepoint = BitmapFactory.decodeResource(getResources(), R.drawable.line);
lineerropoint = BitmapFactory.decodeResource(getResources(), R.drawable.lineerror);
//计算点的坐标
points [0][0] = new Point(offsetsX  + Width / 4 ,offsetsY+ Width /4);
points [0][1] = new Point(offsetsX + Width /2,offsetsY + Width/4);
points [0][2] = new Point(offsetsX + Width - Width /4 ,offsetsY + Width/4);

points [1][0] = new Point(offsetsX  + Width / 4,offsetsY+ Width /2);
points [1][1] = new Point(offsetsX + Width /2,offsetsY+ Width /2);
points [1][2] = new Point(offsetsX + Width - Width /4,offsetsY+ Width /2);

points [2][0] = new Point(offsetsX  + Width / 4,offsetsY+ Width - Width /4);
points [2][1] = new Point(offsetsX + Width /2,offsetsY+ Width - Width /4);
points [2][2] = new Point(offsetsX + Width - Width /4,offsetsY+ Width - Width /4);
//图片资源半径,获得当前图片的长度并且除以2
BitmapRadious = normalpoint.getHeight()/2;
//设置密码
int  index = 1;
for(Point[] points : this.points){
for(Point point : points){
point.index = index;
index ++ ;
}
}
//初始化完成
isinit = true;

}

//覆盖View中的Touch事件
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
isover = false;
MovingNopoint   =  false;
MoveX  = event.getX();      //获得当前按下的点的X坐标
MoveY   = event.getY();     //获得当前按下的点的Y坐标
Log.i(TAG, MoveX+"");
Log.i(TAG, MoveY+"");

Point   point    = null ;

switch (event.getAction()) {
//按下
case MotionEvent.ACTION_DOWN   :
reset();
if(listener != null){
listener.onpatterstart(true);
}
point = checkpoint();
if(point != null){
isselected = true;
}
break;
//移动
case  MotionEvent.ACTION_MOVE:
if(isselected){
point  = checkpoint();
if(point  == null){
MovingNopoint = true;
}
}

break;

//抬起
case MotionEvent.ACTION_UP:
isover = true;
isselected   = false;
break;

}
//选中重复检查
if(  !isover  && isselected && point !=null){
//交叉点
if(crossPoint(point )){
MovingNopoint  = true;
}
//新点
else{
point.state = Point.STATE_SELECED;
listpoint.add(point);

}
}
//绘制结束
if(isover){
//绘制不成立
if(listpoint.size() == 1){
reset();
}
//绘制错误
else if(listpoint.size() < 5 &&  listpoint.size()  > 0){
errorPoint();
if(listener != null){
listener.onpatterchanged(null);
// listener.onpatterchanged("nimei");

}

}
//绘制成功,触发监听事件
else{
if(listener != null){
String  passstr = "";
for(int i = 0 ; i < listpoint.size(); i++){
passstr = passstr + listpoint.get(i).index;
}
if(!TextUtils.isEmpty(passstr))
listener.onpatterchanged(passstr);       //绘制成功把绘制成功的密码返回出去
//  listener.onpatterchanged("nimei");
}
}
}
postInvalidate();       //每次ontouch 事件都必须让View重新绘制一下,刷新View,会重新调用OnDraw的方法
return  true;
}

//交叉点的检查
private boolean crossPoint( Point   point){
if(listpoint.contains(point)){
return true;
}
else{

return false;
}

}
//获取角度
public  float  getDegress (Point a  ,Point b){
float  ax = a.x;
float  ay = a.y;
float   bx = b.x;
float  by = b.y;
float  degress = 0;
if(bx == ax){           //y轴相等 90度或270度
if(by > ay){       //在Y轴下边90
degress = 90;
}else if(by < ay){     //在Y轴上边270
degress = 270;
}
}else if(by  == ay){     //y轴相等 0 或 180
if(bx > ax){
degress = 0;
}else if(bx < ax){
degress = 180;
}
}
else{
degress =   (float) Math.toDegrees((float) Math.atan2(b.y - a.y, b.x - a.x));
}

return   degress;

}

//绘制不成立的方法
public  void   reset(){
for(int i = 0 ; i < listpoint.size() ; i ++){
Point point = listpoint.get(i);
point.state = Point.STATE_NORMAL;
}

listpoint.clear();
}

//绘制错误的方法
public   void    errorPoint(){
for(Point  point :   listpoint){
point.state = Point.STATE_ERROR;
}
}
/**
* 判断当前点击的地方的坐标和点的坐标是否相重合(可以有一定范围)
* @return
*/
public   Point checkpoint (){

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, BitmapRadious, MoveX, MoveY)){
return   point;
}
}
}

return  null;
}
//判断重合的方法
public  static   Boolean  with(float x , float y , float R ,float MoveX, float MoveY){
return  Math.sqrt(    (x - MoveX) * (x - MoveX)  +  ( y - MoveY)  * (y - MoveY)   ) < R;
}

public   static  interface OnpatternchangeListener{
//图案改变
void  onpatterchanged(String passwordstr);       //返回密码字符串
//图案是否重新绘制
void  onpatterstart(boolean   isstart);          //当不画的时候就显示请绘制的字符串

}

public   void   setOnpatternLListener(OnpatternchangeListener hhh){
if(hhh != null)
this.listener = hhh;
}

}
代码上都有注释,LockPatternView类就实现了锁屏界面的基本功能,看的不是很清楚的可以给我留言,主要的核心代码就是LockPatternView类

然后是欢迎界面WelcomeActivity.java 启动App时,调到锁屏界面,判断如果设置过密码,则这时手势锁屏认为是解锁,如果没有设置过密码,手势锁屏为设置密码(注 :界面之间的传值使用全局对象来完成)

package com.example.lock;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;

public class WelcomeActivity  extends Activity{
/**
* 欢迎界面
*/
private  judgeflag  collect;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
//欢迎界面延迟启动
new  Handler().postDelayed(new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
//检测先前有没有设置过密码,用sharePreerenced
SharedPreferences sp = getSharedPreferences("sp", Context.MODE_PRIVATE);
String password = sp.getString("mima", "");    //从sharepreference中找keypassword所对应的值

if(TextUtils.isEmpty(password)){
//设定密码
Intent  intent  = new Intent(WelcomeActivity.this,MainActivity.class);
intent.putExtra("judge", "1");   //重新设定密码
startActivity(intent);
finish();
}
//设置过密码,则是密码解锁
else{
collect = (judgeflag)getApplication();
collect.setPassword(password);
startActivity(new Intent(WelcomeActivity.this,MainActivity.class).putExtra("judge", "2"));  //解锁密码
finish();
}
}
},1000);

}

}
MainActivity.java用来为锁屏界面添加监听器,监听用户的动作(设置了监听接口,并且使用了Android自带的轻量级存储方法SharedPreference来对密码进行增删改查)

package com.example.lock;

import com.example.lock.LockPatternView.OnpatternchangeListener;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends Activity implements OnpatternchangeListener{

protected static final String TAG = null;
private    TextView     xianshi;
private    LockPatternView    hhh ;
private    judgeflag    judge;
private    String      chuandimima;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
judge = (judgeflag)getApplication();   //采用全局对象进行界面之间的传值

hhh = (LockPatternView)findViewById(R.id.huatu);    //初始化画布
chuandimima = getIntent().getStringExtra("judge");
xianshi = (TextView)findViewById(R.id.name);
hhh.setOnpatternLListener(this);      //给图案锁设置监听器
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.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();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}

@Override
public void onpatterchanged(String passwordstr) {
// TODO Auto-generated method stub
if(!TextUtils.isEmpty(passwordstr)){
// xianshi.setText(passwordstr);
// judge.password = passwordstr;		     //设置密码,把密码写到sharedpreference
if(chuandimima.equals("1")){
SharedPreferences  myshare = getSharedPreferences("sp", 0);
SharedPreferences.Editor edit = myshare.edit();
edit.putString("mima", passwordstr);     //写入到sharedPreference
edit.commit();
//  Log.i(TAG, passwordstr);
xianshi.setText("密码设置成功");

}
else{
if(passwordstr.equals(judge.getPassword()))
{
//xianshi.setText("解锁成功");
Intent  intent  = new Intent(this,zhujiemian.class);
startActivity(intent);
finish();
}
else
xianshi.setText("手势错误");

}
}
else{
xianshi.setText("至少五个密码");
}
}

@Override
public void onpatterstart(boolean isstart) {
// TODO Auto-generated method stub
if(isstart){
xianshi.setText("请输入图案锁");
}
}
}
主要的核心代码都已经写出,作为一个Android新手,希望大家一起共同学习,大家也可以一起探讨,源码地址如下: http://download.csdn.net/detail/danielntz/9393180
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: