Android九宫格解锁控件
Daisuki_ch 人气:0前两天从网上学习了下如何自定义一个九宫格解锁的控件,于是自己根据逻辑写了一遍,自定义控件的代码如下:
public class LockedView extends View { private boolean isFirst = true;//设置第一次加载时为true,后面重新画图不再执行 private int width, height;//获取控件的宽度和高度 private int offsetX, offsetY;//获取点坐标时的X轴和Y轴的偏移量 private Point[][] pointList;//每个点的坐标存放的数组 private int r;//每个圆的半径 private Bitmap map1, map2, map3;//3种状态的bitmap private float eventX, eventY;//触摸控件时的X坐标和Y坐标 private boolean isPressed;//判断是否触摸着控件 private boolean moveOnPoint;//判断是否移动到一个点上了 private boolean isFinish;//判断手势是否结束 private List<Point> list = new ArrayList<>();//存放经过的点的集合 private Point checkedPoint; private Paint paint;//画笔 public static final int LOCKED_FIRST=0;//第一次加载acitivity时设置密码时的返回值 public static final int LOCKED_TRUE=1;//解锁成功时的返回值 public static final int LOCKED_FALSE=2;//解锁失败的返回值 private OnLockedChangedListener onLocked;//接口回调 public LockedView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //判断是否第一次加载 if (isFirst) { //初始化点坐标 initPoints(); //初始化画笔 initPaint(); isFirst = false; } //根据每个点的状态来画对应的bitmap drawPoints(canvas); //画手势滑动过程中的线 drawLines(canvas); } //初始化画笔 private void initPaint() { paint = new Paint(Paint.ANTI_ALIAS_FLAG); //设置画笔颜色为蓝色 paint.setColor(getResources().getColor(R.color.LockedColor)); //设置画笔的宽度为8 paint.setStrokeWidth(8); } //画线 private void drawLines(Canvas canvas) { //如果集合有值 if (list.size() > 0) { //获得起始点的实例 Point startPoint = list.get(0); for (int i = 1; i < list.size(); i++) { //获得停止点的实例 Point stopPoint = list.get(i); //根据起始点坐标跟停止点坐标来画线 canvas.drawLine(startPoint.getX(), startPoint.getY(), stopPoint.getX(), stopPoint.getY(), paint); //把停止点赋值给起始点,这样每次遍历的时候起始点都是上一个点 startPoint = stopPoint; } //如果没有移动到一个点上 if (moveOnPoint == false) { //则根据最后个点的坐标跟当前手势移动的坐标画线 canvas.drawLine(startPoint.getX(), startPoint.getY(), eventX, eventY, paint); } } } //设置触摸事件 @Override public boolean onTouchEvent(MotionEvent event) { //获得触摸的X坐标 eventX = event.getX(); //获得触摸的Y坐标 eventY = event.getY(); //每次触摸到离开屏幕之前都默认为没有完成 isFinish = false; //默认移动到点上了 moveOnPoint = true; //选中的点 checkedPoint = null; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //如果按下,重置下点 reset(); //根据触摸的X,Y坐标以及圆的半径来判断是否触摸到了一个圆上,如果有则返回实例,没有则返回空 checkedPoint = checkPoint(eventX, eventY, r); if (checkedPoint != null) { //如果实例不为空,则设置选中点的状态为选中 checkedPoint.setState(Point.POINT_XUANZHONG); //第一次按下按到点上时设置为true isPressed = true; } break; case MotionEvent.ACTION_MOVE: //如果按下在一个点上,就会执行移动动作的逻辑 if (isPressed) { //同上 checkedPoint = checkPoint(eventX, eventY, r); if (checkedPoint != null) { checkedPoint.setState(Point.POINT_XUANZHONG); //如果实例不为空,则设置移动到了点上 moveOnPoint = true; } else { //否则设置没有移动到点上 moveOnPoint = false; } } break; case MotionEvent.ACTION_UP: //抬起时,设置第一次按在点上的参数为false以及完成了触摸过程 isPressed = false; isFinish = true; break; case MotionEvent.ACTION_CANCEL: isPressed = false; isFinish = true; break; } //如果第一下按在了点上并且没有完成触摸并且选中的点的实例不为空 if (isPressed && !isFinish && checkedPoint != null) { //判断这个实例是否在list集合中 if (isInList(checkedPoint)) { //如果在,则设置没有移动在点上 moveOnPoint = false; } else { //否则就添加到集合里面 list.add(checkedPoint); } //如果完成了触摸 } else if (isFinish) { if (list.size() > 0) { //如果集合长度为1,则表示只是摸到了一个点,直接重置 if(list.size()==1){ reset(); //如果集合长度小于5,则表示密码太短了不符合要求,把选中的点设置为错误状态,并且通过接口回调返回数据 }else if(list.size()<5){ errorPoint(); if(onLocked!=null){ onLocked.onResult("密码太短"); } //如果集合长度满足要求,则通过接口的返回值,来判断不同的情况 }else if(list.size()>=5){ StringBuffer buffer=new StringBuffer(); for(int i=0;i<list.size();i++){ buffer.append(list.get(i).getIndex()); } if(onLocked!=null){ switch (onLocked.onPassword(buffer.toString())){ //第一次打开activity时,shared里面没有值,则把当前的密码存到shared里 case LOCKED_FIRST: onLocked.onResult("设置密码成功"); reset(); break; //如果shared里面有值,则根据值对比下当前的密码值,如果一样则解锁成功,不一样则失败 case LOCKED_TRUE: onLocked.onResult("解锁成功"); reset(); break; case LOCKED_FALSE: onLocked.onResult("解锁失败"); errorPoint(); break; } //重新调用onDraw方法 postInvalidate(); //此次触摸消费掉 return true; } } } } postInvalidate(); return true; } //设置错误的点的状态 private void errorPoint() { for(int i=0;i<list.size();i++){ list.get(i).setState(Point.POINT_XUANCUO); } } //判断点是否在集合里面 private boolean isInList(Point checkedPoint) { return list.contains(checkedPoint); } //根据触摸点的X,Y轴坐标以及圆半径判断是否触摸到了一个圆 private Point checkPoint(float eventX, float eventY, int r) { for (int i = 0; i < pointList.length; i++) { for (int j = 0; j < pointList[i].length; j++) { Point point = pointList[i][j]; double juli = getPointJuli(eventX, eventY, point.getX(), point.getY()); if (juli < r) { return point; } } } return null; } //重置点 private void reset() { for (int i = 0; i < list.size(); i++) { list.get(i).setState(Point.POINT_MOREN); } list.clear(); } //获取两点之间的距离 private double getPointJuli(float eventX, float eventY, int x, int y) { return Math.sqrt(Math.abs(eventX - x) * Math.abs(eventX - x) + Math.abs(eventY - y) * Math.abs(eventY - y)); } //根据点的状态来画点 private void drawPoints(Canvas canvas) { for (int i = 0; i < pointList.length; i++) { for (int j = 0; j < pointList[i].length; j++) { Point point = pointList[i][j]; switch (point.getState()) { case Point.POINT_MOREN: canvas.drawBitmap(map1, point.getX() - r, point.getY() - r, null); break; case Point.POINT_XUANZHONG: canvas.drawBitmap(map2, point.getX() - r, point.getY() - r, null); break; case Point.POINT_XUANCUO: canvas.drawBitmap(map3, point.getX() - r, point.getY() - r, null); break; } } } } //初始化点坐标和bitmap private void initPoints() { //获得控件的宽度 width = getWidth(); //获得控件的高度 height = getHeight(); //设置X的偏移量为0 offsetX = 0; //设置Y的偏移量为0 offsetY = 0; //如果是竖屏则 if (width < height) { offsetY = (height - width) / 2; height = width; } else { offsetX = (width - height) / 2; width = height; } //创建一个Point数组存放点的坐标 pointList = new Point[3][3]; //设置索引,好判断密码 int index=1; //遍历,用算法算出每个点的坐标 for (int i = 0; i < pointList.length; i++) { for (int j = 0; j < pointList[i].length; j++) { pointList[i][j] = new Point(offsetX + width / 4 * (i + 1), offsetY + height / 4 * (j + 1)); pointList[i][j].setIndex(index); index++; } } //设置3个bitmap,分别是默认状态的,选中状态的,错误状态的 map1 = BitmapFactory.decodeResource(getResources(), R.drawable.aa); map2 = BitmapFactory.decodeResource(getResources(), R.drawable.bb); map3 = BitmapFactory.decodeResource(getResources(), R.drawable.cc); //获得圆的半径 r = map1.getWidth() / 2; } public void setOnLockedChangedListener(OnLockedChangedListener onLocked){ this.onLocked=onLocked; } //设置回调接口 public interface OnLockedChangedListener{ public int onPassword(String password); public void onResult(String result); } }
activity代码:
public class LockedActivity extends Activity { private LockedView lockedView; private TextView textView; private SharedPreferences preferences; private String pass; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_locked); //初始化控件 lockedView= (LockedView) findViewById(R.id.lockedView); textView= (TextView) findViewById(R.id.textView); //获得shared preferences=getSharedPreferences("Locked",MODE_PRIVATE); //根据存储在shared里的键获得密码值 pass=preferences.getString("password",""); //如果没有代表第一次启动activity,则textview设置文本为情设置密码,如果不是第一次启动,则设置文本为请解锁 if(pass.equals("")){ textView.setText("请设置密码"); }else{ textView.setText("请解锁"); } lockedView.setOnLockedChangedListener(new LockedView.OnLockedChangedListener() { @Override public int onPassword(String password) { //从shared里取值 pass=preferences.getString("password",""); //如果值为空,则把接口返回的password存入shared里 if(pass.equals("")){ SharedPreferences.Editor editor=preferences.edit(); editor.putString("password",password); editor.commit(); textView.setText("请解锁"); return 0; }else if(pass.equals(password)){ //如果匹配了密码,则返回数字1代表解锁成功 return 1; } //如果不匹配密码返回2 return 2; } @Override public void onResult(String result) { //根据密码匹配情况再从接口获得要toast的数据 Toast.makeText(LockedActivity.this,result,Toast.LENGTH_SHORT).show(); //数据是解锁成功,则跳转activity if(result.equals("解锁成功")){ Intent intent=new Intent(LockedActivity.this,MainActivity.class); startActivity(intent); } } }); } }
布局代码如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.chaohengdai.test922.LockedView android:id="@+id/lockedView" android:layout_width="match_parent" android:layout_height="match_parent" /> <TextView android:id="@+id/textView" android:textSize="20sp" android:layout_centerHorizontal="true" android:layout_marginTop="50dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
加载全部内容