Android手势锁屏界面

        最近在学习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





 

                                                                                       

     

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值