Android手势锁屏界面
2016-01-06 16:34
537 查看
最近在学习Android的自定义控件的知识,因为经常见App中有手势锁屏的功能,所以用自定义控件的方法制作了一个简单的手势锁屏的App,(逻辑简单,代码很容易理解)并且添加了一些实际的小功能进行了测试。本来想要制作成一个gif图像在此演示,因为时间的问题就不做了,以下为主要的代码:
启动APP时进入的界面
设置密码时的界面:
密码错误时 :
密码不符合规则时:
大体就是这样的,因为图片资源找的不是很好,所以最终呈现的效果不是很好,但完成了基本的功能。程序总体架构
首先是锁频界面的设计(自定义控件 由 LockPatternView 继承View类)
然后是欢迎界面WelcomeActivity.java 启动App时,调到锁屏界面,判断如果设置过密码,则这时手势锁屏认为是解锁,如果没有设置过密码,手势锁屏为设置密码(注 :界面之间的传值使用全局对象来完成)
启动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
相关文章推荐
- Android应用开发基础篇(16)-----ScaleGestureDetector(缩放手势检测)
- Android初学习 - AsyncTask的一些介绍I
- android使用5.0的toolbar,自定义布局参考地址
- 利用反射实现对sqlite3数据库的crud(增删改查)操作的一个baseAndroidDao封装,安卓开发中
- Android屏幕适配全攻略(最权威的官方适配指导)
- android EventBus
- Android studio 注解插件安装与使用
- ViewPager的多种应用详解(一)
- 学习Android遇到的一些坑
- Android ProgressBar
- Android Studio Keymap
- 关于Android适配华为等带有底部虚拟按键的解决方案
- Android Init进程源码分析
- 支持C++ 11代码规范的 android NDK 编译
- Android消息机制
- Android编程实现为应用添加菜单的方法
- Android的消息机制
- Android Studio使用教程(二)
- android 之popupWindow 在指定位置上的显示
- Android卸载反馈