Android自定义控件实现九宫格解锁功能

最终Android九宫格解锁效果如下

1.进行定义实体point点

public class Point {
 private float x;
 private float y;
 //正常模式
 public static final int NORMAL_MODE = 1;
 //按下模式
 public static final int PRESSED_MODE = 2;
 //错误模式
 public static final int ERROR_MODE = 3;

 private int state = NORMAL_MODE;
 private String mark;

 public Point(float x, float y, String mark) {
  this.x = x;
  this.y = y;
  this.mark = mark;
 }

 public float getX() {
  return x;
 }

 public void setX(float x) {
  this.x = x;
 }

 public float getY() {
  return y;
 }

 public void setY(float y) {
  this.y = y;
 }

 public int getState() {
  return state;
 }

 public void setState(int state) {
  this.state = state;
 }

 public String getMark() {
  return mark;
 }

 public void setMark(String mark) {
  this.mark = mark;
 }
}

2.自定义ScreenLockView

public class ScreenLockView extends View {
 private static final String TAG = "ScreenLockView";

 // 错误格子的图片
 private Bitmap errorBitmap;
 // 正常格子的图片
 private Bitmap normalBitmap;
 // 手指按下时格子的图片
 private Bitmap pressedBitmap;
 // 错误时连线的图片
 private Bitmap lineErrorBitmap;
 // 手指按住时连线的图片
 private Bitmap linePressedBitmap;
 // 偏移量,使九宫格在屏幕中央
 private int offset;
 // 九宫格的九个格子是否已经初始化
 private boolean init;
 // 格子的半径
 private int radius;
 // 密码
 private String password = "123456";
 // 九个格子
 private Point[][] points = new Point[3][3];
 private int width;
 private int height;
 private Matrix matrix = new Matrix();
 private float moveX = -1;
 private float moveY = -1;
 // 是否手指在移动
 private boolean isMove;
 // 是否可以触摸,当用户抬起手指,划出九宫格的密码不正确时为不可触摸
 private boolean isTouch = true;
 // 用来存储记录被按下的点
 private List<Point> pressedPoint = new ArrayList<>();
 // 屏幕解锁监听器
 private OnScreenLockListener listener;

 public ScreenLockView(Context context) {
  super(context);
  init();
 }

 public ScreenLockView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public ScreenLockView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }

 private void init() {
  errorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_error);
  normalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_normal);
  pressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bitmap_pressed);
  lineErrorBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.line_error);
  linePressedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.line_pressed);
  radius = normalBitmap.getWidth() / 2;
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  if (widthSize > heightSize) {
   offset = (widthSize - heightSize) / 2;
  } else {
   offset = (heightSize - widthSize) / 2;
  }
  setMeasuredDimension(widthSize, heightSize);
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  if (!init) {
   width = getWidth();
   height = getHeight();
   initPoint();
   init = true;
  }
  drawPoint(canvas);
  if (moveX != -1 && moveY != -1) {
   drawLine(canvas);
  }
 }

 // 画直线
 private void drawLine(Canvas canvas) {

  // 将pressedPoint中的所有格子依次遍历,互相连线
  for (int i = 0; i < pressedPoint.size() - 1; i++) {
   // 得到当前格子
   Point point = pressedPoint.get(i);
   // 得到下一个格子
   Point nextPoint = pressedPoint.get(i + 1);
   // 旋转画布
   canvas.rotate(RotateDegrees.getDegrees(point, nextPoint), point.getX(), point.getY());

   matrix.reset();
   // 根据距离设置拉伸的长度
   matrix.setScale(getDistance(point, nextPoint) / linePressedBitmap.getWidth(), 1f);
   // 进行平移
   matrix.postTranslate(point.getX(), point.getY() - linePressedBitmap.getWidth() / 2);

   if (point.getState() == Point.PRESSED_MODE) {
    canvas.drawBitmap(linePressedBitmap, matrix, null);
   } else {
    canvas.drawBitmap(lineErrorBitmap, matrix, null);
   }
   // 把画布旋转回来
   canvas.rotate(-RotateDegrees.getDegrees(point, nextPoint), point.getX(), point.getY());
  }

  // 如果是手指在移动的情况
  if (isMove) {
   Point lastPoint = pressedPoint.get(pressedPoint.size() - 1);
   canvas.rotate(RotateDegrees.getDegrees(lastPoint, moveX, moveY), lastPoint.getX(), lastPoint.getY());

   matrix.reset();
   Log.i(TAG, "the distance : " + getDistance(lastPoint, moveX, moveY) / linePressedBitmap.getWidth());
   matrix.setScale(getDistance(lastPoint, moveX, moveY) / linePressedBitmap.getWidth(), 1f);
   matrix.postTranslate(lastPoint.getX(), lastPoint.getY() - linePressedBitmap.getWidth() / 2);
   canvas.drawBitmap(linePressedBitmap, matrix, null);

   canvas.rotate(-RotateDegrees.getDegrees(lastPoint, moveX, moveY), lastPoint.getX(), lastPoint.getY());
  }
 }

 // 根据point和坐标点计算出之间的距离
 private float getDistance(Point point, float moveX, float moveY) {
  Point b = new Point(moveX,moveY,null);
  return getDistance(point,b);
 }

 // 根据两个point计算出之间的距离
 private float getDistance(Point point, Point nextPoint) {
  return (float) Math.sqrt(Math.pow(nextPoint.getX() - point.getX(), 2f) + Math.pow(nextPoint.getY() - point.getY(), 2f));
 }
 private void drawPoint(Canvas canvas) {
  for (int i = 0; i < points.length; i++) {
   for (int j = 0; j < points[i].length; j++) {
    int state = points[i][j].getState();
    if (state == Point.NORMAL_MODE) {
     canvas.drawBitmap(normalBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
    } else if (state == Point.PRESSED_MODE) {
     canvas.drawBitmap(pressedBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
    } else {
     canvas.drawBitmap(errorBitmap, points[i][j].getX() - radius, points[i][j].getY() - radius, null);
    }
   }
  }
 }

 //初始化九宫格的点
 private void initPoint() {
  points[0][0] = new Point(width / 4, offset + width / 4, "0");
  points[0][1] = new Point(width / 2, offset + width / 4, "1");
  points[0][2] = new Point(width * 3 / 4, offset + width / 4, "2");

  points[1][0] = new Point(width / 4, offset + width / 2, "3");
  points[1][1] = new Point(width / 2, offset + width / 2, "4");
  points[1][2] = new Point(width * 3 / 4, offset + width / 2, "5");

  points[2][0] = new Point(width / 4, offset + width * 3 / 4, "6");
  points[2][1] = new Point(width / 2, offset + width * 3 / 4, "7");
  points[2][2] = new Point(width * 3 / 4, offset + width * 3 / 4, "8");

 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  if (isTouch) {
   float x = event.getX();
   float y = event.getY();
   Point point;
   switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
     // 判断用户触摸的点是否在九宫格的任意一个格子之内
     point = isPoint(x, y);
     if (point != null) {
      point.setState(Point.PRESSED_MODE); // 切换为按下模式
      pressedPoint.add(point);
     }
     break;
    case MotionEvent.ACTION_MOVE:
     if (pressedPoint.size() > 0) {
      point = isPoint(x, y);
      if (point != null) {
       if (!crossPoint(point)) {
        point.setState(Point.PRESSED_MODE);
        pressedPoint.add(point);
       }
      }
      moveX = x;
      moveY = y;
      isMove = true;
     }
     break;
    case MotionEvent.ACTION_UP:
     isMove = false;
     String tempPwd = "";
     for (Point p : pressedPoint) {
      tempPwd += p.getMark();
     }
     if (listener != null) {
      listener.getStringPassword(tempPwd);
     }

     if (tempPwd.equals(password)) {
      if (listener != null) {
       listener.isPassword(true);
       this.postDelayed(runnable, 1000);
      }

     } else {
      for (Point p : pressedPoint) {
       p.setState(Point.ERROR_MODE);
      }
      isTouch = false;
      this.postDelayed(runnable, 1000);
      if (listener != null) {
       listener.isPassword(false);
      }
     }
     break;
   }
   invalidate();
  }
  return true;
 }

 private boolean crossPoint(Point point) {
  if (pressedPoint.contains(point)) {
   return true;
  }
  return false;
 }

 public interface OnScreenLockListener {
  public void getStringPassword(String password);

  public void isPassword(boolean flag);
 }

 public void setOnScreenLockListener(OnScreenLockListener listener) {
  this.listener = listener;
 }

 private Point isPoint(float x, float y) {
  Point point;
  for(int i = 0; i<points.length;i++){
   for (int j = 0; j < points[i].length; j++) {
    point = points[i][j];
    if (isContain(point, x, y)) {
     return point;
    }
   }
  }
  return null;
 }

 private boolean isContain(Point point, float x, float y) {
  return Math.sqrt(Math.pow(x - point.getX(), 2f) + Math.pow(y - point.getY(), 2f)) <= radius;
 }
 private Runnable runnable = new Runnable() {
  @Override
  public void run() {
   isTouch = true;
   reset();
   invalidate();
  }
 };

 // 重置格子
 private void reset(){
  for (int i = 0; i < points.length; i++) {
   for (int j = 0; j < points[i].length; j++) {
    points[i][j].setState(Point.NORMAL_MODE);
   }
  }
  pressedPoint.clear();
 }
}

3.RotateDegress类

public class RotateDegrees {
 public static float getDegrees(Point a, Point b){
  float degrees = 0 ;
  float aX = a.getX();
  float aY = a.getY();
  float bX = b.getX();
  float bY = b.getY();
  if(aX == bX){
   if(aY<bY){
    degrees = 90;
   }else{
    degrees = 270;
   }
  }else if(bY == aY){
   if(aX<bX){
    degrees = 0 ;
   }else{
    degrees = 180;
   }

  }else{
   if(aX>bX){
    if(aY>bY){
     degrees = 180 + (float)(Math.atan2(aY-bY,aX-bX)*180/Math.PI);
    }else{
     degrees = 180 - (float)(Math.atan2(bY -aY,aX - bX)*180/Math.PI);
    }

   }else{
    if(aY>bY){
     degrees = 360 -(float)(Math.atan2(aY - bY,bX-aX)*180/Math.PI);
    }else{
     degrees = (float)(Math.atan2(bY - aY,bX - aX)*180/Math.PI);
    }
   }
  }
  return degrees;
 }

 public static float getDegrees(Point a, float bX,float bY){
  Point b = new Point(bX,bY,null);
  return getDegrees(a,b);
 }
}

用到的图片资源

4.MainActivity中使用

public class MainActivity extends AppCompatActivity {

 private ScreenLockView screenLockView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  screenLockView = (ScreenLockView) findViewById(R.id.slv);
  screenLockView.setOnScreenLockListener(new ScreenLockView.OnScreenLockListener() {
   @Override
   public void getStringPassword(String password) {

   }

   @Override
   public void isPassword(boolean flag) {
    String content;
    if (flag) {
     content = "密码正确";

    } else {
     content = "密码错误";
    }
    Toast.makeText(MainActivity.this, content, Toast.LENGTH_SHORT).show();

   }
  });
 }
}

5.布局文件

<?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:id="@+id/activity_main"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:paddingBottom="@dimen/activity_vertical_margin"
 android:paddingLeft="@dimen/activity_horizontal_margin"
 android:paddingRight="@dimen/activity_horizontal_margin"
 android:paddingTop="@dimen/activity_vertical_margin"
 tools:context="com.example.admin.ninegridunlock.MainActivity">

 <com.example.admin.ninegridunlock.ScreenLockView
  android:id="@+id/slv"
  android:layout_width="match_parent"
  android:layout_height="match_parent" />
</RelativeLayout>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 轻松实现安卓(Android)九宫格解锁

    效果图 思路 首先我们来分析一下实现九宫格解锁的思路:当用户的手指触摸到某一个点时,先判断该点是否在九宫格的某一格范围之内,若在范围内,则该格变成选中的状态:之后用户手指滑动的时候,以该格的圆心为中心,用户手指为终点,两点连线.最后当用户手指抬起时,判断划过的九宫格密码是否和原先的密码匹配. 大致的思路流程就是上面这样的了,下面我们可以来实践一下. Point 类 我们先来创建一个 Point 类,用来表示九宫格锁的九个格子.除了坐标 x ,y 之外,还有三种模式:正常模式.按下模式和错误模式.

  • Android实现九宫格解锁的方法

    相信大家都有使用九宫格解锁,比如在设置手机安全项目中,可以使用九宫格解锁,提高安全性,以及在使用支付功能的时候,为了提高安全使用九宫锁,今天就为大家介绍Android实现九宫格的方法,分享给大家供大家参考.具体如下: 运行效果截图如下: 具体代码如下: 布局文件如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas

  • Android 仿小米锁屏实现九宫格解锁功能(无需图片资源)

    最近公司要求做个九宫格解锁,本人用的是小米手机,看着他那个设置锁屏九宫格很好看,就做了该组件,不使用图片资源,纯代码实现. 尊重每个辛苦的博主,在http://blog.csdn.net/mu399/article/details/38734449的基础上进行修改 效果图: 关键代码类: MathUtil.Java /** * @author SoBan * @create 2016/12/5 15:52. */ public class MathUtil { public static dou

  • 使用Android自定义控件实现滑动解锁九宫格

    本文概述:  滑动解锁九宫格的分析: 1.需要自定义控件: 2.需要重写事件onTouchEvent(); 3.需要给九个点设置序号和坐标,这里用Map类就行: 4.需要判断是否到滑到过九点之一,并存储滑到过的点的序号,而且需要一个方法可以返回它们,这里用List类就行: 滑动解锁当前还是比较流行的,今天写了个简单的滑动解锁九宫格的例程,分享出来让初学者看看. 我的是这样的: Demo 首先,自定义一个View /** * 九宫格 */ public class NineGridView ext

  • android 九宫格滑动解锁开机实例源码学习

    效果图由于网站占时不能上传,以后补上. NinePointLineView.java 复制代码 代码如下: package org.demo.custon_view; import org.demo.utils.MLog; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; imp

  • 轻松实现Android自定义九宫格图案解锁

    Android实现九宫格图案解锁,自带将图案转化成数字密码的功能,代码如下: LockPatternView.java package com.jackie.lockpattern; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.text.TextUtils; i

  • Android实现九宫格解锁的实例代码

    当年感觉九宫格解锁很是高大上,一脸懵逼,今天正好要做解锁这一块业务,回头来看九宫格,这特么简单啊 首先理清一下逻辑,我们要做NxN的九宫格 下图是3x3的简单图例 // -(--)-(--)-(--)- // -(--)-(--)-(--)- // -(--)-(--)-(--)- 我们就把九宫格分解成 外圆 .内圆.连线三部分 外圆半径Radius,内圆半径dp(5) 建立一个集合来放置 外圆的圆心( 内圆的圆心也一样) private ArrayList<Point> mListCircl

  • Android自定义控件实现九宫格解锁功能

    最终Android九宫格解锁效果如下 1.进行定义实体point点 public class Point { private float x; private float y; //正常模式 public static final int NORMAL_MODE = 1; //按下模式 public static final int PRESSED_MODE = 2; //错误模式 public static final int ERROR_MODE = 3; private int state

  • Android自定义控件实现九宫格解锁

    关于九宫格解锁,我看了不少博客,但是都感觉很复杂,可能我的功夫还不到,所以很多东西我不了解,但是我还是打算写一个自己的九宫格.我相信我的九宫格大家都能很快的理解,当然如果需要实现更复杂的功能,需要大家自己接着往深了挖掘. 代码文件​​​​​​ NineGroupView:为九宫格空间组 ToggleView:九宫格中的子View,也就是我们看到的圆形按钮,我自己定义的ToggleView可能不好看,当然大家可以自己定义更加好看的ToggleView. MarkBean:记录ToggleView的

  • Android手机屏幕敲击解锁功能代码

    1.前言 现在市面上有不少Android手机支持敲击屏幕解锁,敲击屏幕解锁是一项很实用的功能,但一来只支持敲击屏幕,二来只能用于解锁或锁屏,再者我们应用层的开发者切不进去,完全无法玩起来.开发者,开发者,我们既然身为开发者何不搞点大新闻,那么这次我来教教各位如何用代码来实现手机的敲击识别,听起来是不是很有趣,有些跃跃欲试呢.事实上在ios上已经有实现这个功能的应用:Knock,一款敲击来解锁Mac电脑的应用,售价4.99美元,约为33人民币.有时候真想去做ios开发,可以开心的为自己的应用定价,

  • Android自定义控件EditText使用详解

    本文实例为大家分享了Android自定义控件EditText的具体代码,供大家参考,具体内容如下 自定义控件分三种: 1. 自绘控件 2. 组合控件 3. 继承控件 代码已上传到 github 以后的自定义控件就都放这个仓库 需求 这里由于项目的需要实现一个自定义EditText,主要实现的为两点,一个是工具图标toolIcon,例如点击清除EditText内容.一个为EditText左边的提示图标hintIcon, 例如输入账号密码时前面的图标. 为了让这个控件的拓展性更高,设置了两个点击事件

  • Android自定义控件打造绚丽平行空间引导页

    本文实例为大家分享了Android自定义控件打造平行空间引导页的具体代码,供大家参考,具体内容如下 先上图,动图太大传不上来,在项目中有动图 点击查看动图 首先解释下工程的主要部分. 首先谷歌的百分比布局做了部分修改,因为我设置的宽高都是相对于屏幕的宽度,而谷歌的百分比布局不能实现,只需要修改一部分代码就可以实现.下面贴出修改的部分代码 public static class PercentLayoutInfo { private enum BASEMODE { BASE_WIDTH, BASE

  • 实例讲解Android自定义控件

    小编在此之前给大家介绍过关于Android自定义控件的用法等,需要的可以参考下: Android开发之自定义控件用法详解 详解Android自定义控件属性 可以看到QQ上的ToolBar其实就是一个自定义的view,可以看到不同的界面就是简单地修改了文字而已,在第二张与第三张尤其的明显,我们就仿QQ的这个Toolbar设置一个自定义控件 在开始之前,首先了解一下官方是如何实现一个控件的,比如说一个Linearlayout 它不是有layout_width和layout_height这两个属性吗?

随机推荐