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

本文概述:

 滑动解锁九宫格的分析:

1、需要自定义控件;
2、需要重写事件onTouchEvent();
3、需要给九个点设置序号和坐标,这里用Map类就行;
4、需要判断是否到滑到过九点之一,并存储滑到过的点的序号,而且需要一个方法可以返回它们,这里用List类就行;

滑动解锁当前还是比较流行的,今天写了个简单的滑动解锁九宫格的例程,分享出来让初学者看看。

我的是这样的:

Demo

首先,自定义一个View

/**
 * 九宫格
 */
public class NineGridView extends View {
  private int width;//该控件的宽
  private int height;//该控件的高
  private Paint mPaintBigCircle;//用于画外圆
  private Paint mPaintSmallCircle;//用于画内圆
  private Paint mPaintLine;//用于画线
  private Paint mPaintText;//用于画文本
  private Path path;//手势划线时需要用到它
  private Map<Integer, Float[]> pointContainer;//存储九个点的坐标
  private List<Integer> pointerSlipped;//存储得到的九宫格密码
  public List<Integer> getPointerSlipped() {
    return pointerSlipped;
  }
  public void setPointerSlipped(List<Integer> pointerSlipped) {
    this.pointerSlipped = pointerSlipped;
  }
  public NineGridView(Context context) {
    super(context);
  }
  public NineGridView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mPaintBigCircle = new Paint();
    mPaintBigCircle.setColor(Color.BLUE);
    mPaintBigCircle.setStyle(Paint.Style.STROKE);//不充满
    mPaintBigCircle.setAntiAlias(true);//抗锯齿打开
    mPaintSmallCircle = new Paint();
    mPaintSmallCircle.setColor(Color.GREEN);
    mPaintSmallCircle.setStyle(Paint.Style.FILL);//充满,即画的几何体为实心
    mPaintSmallCircle.setAntiAlias(true);
    mPaintLine = new Paint();
    mPaintLine.setColor(Color.GREEN);
    mPaintLine.setStyle(Paint.Style.STROKE);
    mPaintLine.setStrokeWidth(20);
    mPaintLine.setAntiAlias(true);
    mPaintText = new Paint();
    mPaintText.setColor(Color.WHITE);
    mPaintText.setTextAlign(Paint.Align.CENTER);//向中央对齐
    mPaintText.setTextSize(50);
    mPaintText.setAntiAlias(true);
    path = new Path();
    pointContainer = new HashMap<>();
    pointerSlipped = new ArrayList<>();
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
  }
  private float pivotX;//触屏得到的x坐标
  private float pivotY;//触屏得到的y坐标
  private float selectedX;//当前选中的圆点的x坐标
  private float selectedY;//当前选中的圆点的y坐标
  private float selectedXOld;//从前选中的圆点的x坐标
  private float selectedYOld;//从前选中的圆点的y坐标
  private boolean isHasMoved = false;//用于判断path是否调用过moveTo()方法
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        pivotX = event.getX();
        pivotY = event.getY();
        //每次触屏时需要清空一下pointerSlipped,即重置密码
        pointerSlipped.clear();
        Log.d("pointTouched", pivotX + "," + pivotY);
        getSelectedPointIndex(pivotX, pivotY);
        invalidate();//重绘
        break;
      case MotionEvent.ACTION_MOVE:
        pivotX = event.getX();
        pivotY = event.getY();
        getSelectedPointIndex(pivotX, pivotY);
        invalidate();
        break;
      case MotionEvent.ACTION_UP:
        /**
         * 当手指离开屏幕时,重置path
         */
        path.reset();
        isHasMoved = false;
        String indexSequence = "";
        //打印出上一次手势密码的值
        for(int index:pointerSlipped){
          indexSequence += "/"+index;
        }
        Log.d("index",indexSequence);
        break;
    }
    invalidate();
    return true;
  }
  /**
   * 得到并存储经过的圆点的序号
   * @param pivotX
   * @param pivotY
   */
  private void getSelectedPointIndex(float pivotX, float pivotY) {
    int index = 0;
    if (pivotX > patternMargin && pivotX < patternMargin + bigCircleRadius * 2) {
      if (pivotY > height / 2 && pivotY < height / 2 + bigCircleRadius * 2) {
        selectedX = pointContainer.get(1)[0];
        selectedY = pointContainer.get(1)[1];
        index = 1;
        Log.d("selectedPoint", selectedX + "," + selectedY);
      } else if (pivotY > height / 2 + added && pivotY < height / 2 + added + bigCircleRadius * 2) {
        selectedX = pointContainer.get(4)[0];
        selectedY = pointContainer.get(4)[1];
        index = 4;
      } else if (pivotY > height / 2 + added * 2 && pivotY < height / 2 + added * 2 + bigCircleRadius * 2) {
        selectedX = pointContainer.get(7)[0];
        selectedY = pointContainer.get(7)[1];
        index = 7;
      }
    } else if (pivotX > patternMargin + added && pivotX < patternMargin + added + bigCircleRadius * 2) {
      if (pivotY > height / 2 && pivotY < height / 2 + bigCircleRadius * 2) {
        selectedX = pointContainer.get(2)[0];
        selectedY = pointContainer.get(2)[1];
        index = 2;
      } else if (pivotY > height / 2 + added && pivotY < height / 2 + added + bigCircleRadius * 2) {
        selectedX = pointContainer.get(5)[0];
        selectedY = pointContainer.get(5)[1];
        index = 5;
      } else if (pivotY > height / 2 + added * 2 && pivotY <height / 2 + added * 2 + bigCircleRadius * 2) {
        selectedX = pointContainer.get(8)[0];
        selectedY = pointContainer.get(8)[1];
        index = 8;
      }
    } else if (pivotX > patternMargin + added * 2 && pivotX < patternMargin + added * 2 + bigCircleRadius * 2) {
      if (pivotY > height / 2 && pivotY < height / 2 + bigCircleRadius * 2) {
        selectedX = pointContainer.get(3)[0];
        selectedY = pointContainer.get(3)[1];
        index = 3;
      } else if (pivotY > height / 2 + added && pivotY < height / 2 + added + bigCircleRadius * 2) {
        selectedX = pointContainer.get(6)[0];
        selectedY = pointContainer.get(6)[1];
        index = 6;
      } else if (pivotY > height / 2 + added * 2 && pivotY < height / 2 + added * 2 + bigCircleRadius * 2) {
        selectedX = pointContainer.get(9)[0];
        selectedY = pointContainer.get(9)[1];
        index = 9;
      }
    }
    if (selectedX!=selectedXOld||selectedY!=selectedYOld){
      //当这次的坐标与上次的坐标不同时存储这次点序号
      pointerSlipped.add(index);
      selectedXOld = selectedX;
      selectedYOld = selectedY;
      if (!isHasMoved){
        //当第一次触碰到九个点之一时,path调用moveTo;
        path.moveTo(selectedX,selectedY);
        isHasMoved = true;
      }else{
        //path移动至当前圆点坐标
        path.lineTo(selectedX,selectedY);
      }
    }
  }
  private String text = "请绘制解锁图案";
  private float x;//绘制的圆形的x坐标
  private float y;//绘制圆形的纵坐标
  private float added;//水平竖直方向每个圆点中心间距
  private float patternMargin = 100;//九宫格距离边界距离
  private float bigCircleRadius = 90;//外圆半径
  private float smallCircleRadius = 25;//内圆半径
  private int index;//圆点的序号
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    added = (width - patternMargin * 2) / 3;
    x = patternMargin + added / 2;
    y = added / 2 + height / 2;
    index = 1;
    canvas.drawColor(Color.BLACK);
    canvas.drawText(text, width / 2, height / 4, mPaintText);
    /**
     * 绘制九个圆点图案
     */
    for (int column = 0; column < 3; column++) {
      for (int row = 0; row < 3; row++) {
        canvas.drawCircle(x, y, bigCircleRadius, mPaintBigCircle);
        canvas.drawCircle(x, y, smallCircleRadius, mPaintSmallCircle);
        pointContainer.put(index, new Float[]{x, y});
        index++;
        x += added;
      }
      y += added;
      x = patternMargin + added / 2;
    }
    x = patternMargin + added / 2;
    y = added / 2 + height / 2;
    canvas.drawPath(path, mPaintLine);
  }
}

为什么要规避重复?

因为在触屏时,会调用很多次onTouchEvent()方法,这样存储的手势密码肯定会不准确,我在以上代码中作出了处理,已经避免了重复,看打印信息:

这里写图片描述

显然,密码没有相邻数重复,当然还有一种情况就是手指在两个点之间来回等问题,这种状况也需要避免,这里没有作处理。当然,我做得还不够。。。

自定义view中用到的dp和px互相转换的工具类:

public class SizeConvert {
  /**
   * 将dp转换为sp
   */
  public static int dip2px(Context context, float dipValue){
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int)(dipValue * scale + 0.5f);
  }
  /**
   * sp转dp
   */
  public static int px2dip(Context context, float pxValue){
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int)(pxValue / scale + 0.5f);
  }
}

主活动:

public class NineGridActivity extends BaseActivity{
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.view_nine_grid);
  }
}

layout中的布局文件view_nine_grid:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
  <com.test.shiweiwei.myproject.selfish_view.NineGridView
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
</LinearLayout>

总结

我写的只是最基本的九宫格滑动解密项目,实际用的九宫格解密比这个要复杂,有许多特效和其他更严谨的处理,事件的处理也不是这样草草了事,如果想写得漂亮,还得多花工夫。

(0)

相关推荐

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

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

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

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

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

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

  • 轻松实现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 九宫格滑动解锁开机实例源码学习

    效果图由于网站占时不能上传,以后补上. 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九宫格解锁效果如下 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 仿小米锁屏实现九宫格解锁功能(无需图片资源)

    最近公司要求做个九宫格解锁,本人用的是小米手机,看着他那个设置锁屏九宫格很好看,就做了该组件,不使用图片资源,纯代码实现. 尊重每个辛苦的博主,在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模拟实现滑动解锁界面

    本文实例为大家分享了Android模拟滑动解锁界面,供大家参考,具体内容如下 实现逻辑 自定义一个view继承view类,实现里面的方法 在构造方法中加载出图片资源.在onMeasure中获取背景的宽和高作为自定义控件的宽和高 在onDraw方法中绘制出滑块,在控件的布局文件中设置控件的背景图片 设置滑块的触摸事件,分别算出当手指按下屏幕.移动,离开屏幕时滑块的位置 在移动的过程中,对滑块的位置进行限定,使滑块的位置不能超过指定的区域 在手指离开屏幕的事件中判定手指的位置,如果滑块没有到达最右边

  • Android 自定义TextView实现滑动解锁高亮文字

    下面一段代码给大家分享Android 自定义TextView实现滑动解锁高亮文字效果,具体代码如下所示: public class HightLightTextView extends TextView { // 存储view的宽度 private int mTextViewWidth = 0; // 画笔 private Paint mPaint; // 线性渲染 private LinearGradient mLinearGradient; // 存储变换的matrix private Ma

  • android实现滑动解锁

    本文实例为大家分享了android实现滑动解锁的具体代码,供大家参考,具体内容如下 效果图 需要用到的画笔, 整体灰色的背景,  滑块, 滑动之后绿色背景, 字体 mSliPaint = new Paint(); mSliPaint.setColor(Color.parseColor("#4a4c5b")); mSliPaint.setAntiAlias(true); mBgPaint = new Paint(); mBgPaint.setColor(Color.parseColor(

  • Android自定义view实现滑动解锁效果

    本文实例为大家分享了Android自定义view实现滑动解锁的具体代码,供大家参考,具体内容如下 1. 需求如下: 近期需要做一个类似屏幕滑动解锁的功能,右划开始,左划暂停. 2. 需求效果图如下 3. 实现效果展示 4. 自定义view如下 /** * Desc 自定义滑动解锁View * Author ZY * Mail sunnyfor98@gmail.com * Date 2021/5/17 11:52 */ @SuppressLint("ClickableViewAccessibili

  • android滑动解锁震动效果的开启和取消

    如果我们需要根据设置中的触摸震动开关来开启和取消滑动解锁的震动效果,就需要做以下修改了. 在LockScreen.java类中的LockScreen方法中的 复制代码 代码如下: else if (mUnlockWidget instanceof MultiWaveView) {            MultiWaveView multiWaveView = (MultiWaveView) mUnlockWidget; multiWaveView.setVibrateEnabled(Setti

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

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

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

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

  • 实例讲解Android自定义控件

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

  • iOS实现手势滑动解锁功能简析

    题记 在平常的生活中,我们大概经常遇见手势滑动解锁---也就是九宫格啊,已经出现好久了,虽然随着Apple的指纹解锁的发展手势解锁虽然还有但是因为其不如指纹解锁方便也用的也少了,但是在大多数APP中这两种方式都是并存的,比如qq,微信,支付宝等等,最近项目里面也刚好有这个需求,趁着刚完成抽出时间来记录下来当时的一些思路,可能有的地方理解的不到位,还需多总结,闲言少叙了,看重点. 功能描述如图:大概说一下思路,这个功能用来做相当于密令,用于两端的匹配,教师端设置了路径生成密码,储存在本地,学生端用

随机推荐