Android自定义UI手势密码改进版

接着第一个Android UI手势密码设计的基础上继续改进,效果图如下

activity_main.xml

<LinearLayout 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"
 android:orientation="vertical"
 tools:context=".MainActivity" > 

 <TextView
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:gravity="center"
 android:text="请输入密码"
 android:id="@+id/text"
 /> 

 <com.example.lockpatterview.LockPatterView
 android:id="@+id/lock"
 android:layout_weight="1"
 android:layout_width="match_parent"
 android:layout_height="0dp" /> 

</LinearLayout>

MainActivity

package com.example.lockpatterview; 

import com.example.lockpatterview.LockPatterView.OnPatterChangeLister; 

import android.os.Bundle;
import android.text.TextUtils;
import android.widget.TextView;
import android.widget.Toast;
import android.app.Activity; 

public class MainActivity extends Activity implements OnPatterChangeLister { 

 LockPatterView lock;
 TextView text;
 String p = "14789"; 

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 text = (TextView) findViewById(R.id.text);
 lock = (LockPatterView) findViewById(R.id.lock);
 lock.SetOnPatterChangeLister(this);
 } 

 @Override
 public void onPatterChange(String passwordStr) {
 if (!TextUtils.isEmpty(passwordStr)) {
  if (passwordStr.equals(p)) {
  text.setText(passwordStr);
  } else {
  text.setText("密码错误");
  lock.errorPoint();
  }
 }else {
  Toast.makeText(MainActivity.this, "至少连接5点", 0).show();
 } 

 } 

 @Override
 public void onPatterStart(boolean isStart) {
 if (isStart) {
  text.setText("请绘制图案");
 }
 } 

}

LockPatterView

package com.example.lockpatterview; 

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.view.MotionEvent;
import android.view.View; 

public class LockPatterView extends View { 

 private static final int POINT_SIZE = 5; 

 private Point[][] points = new Point[3][3]; 

 private Matrix matrix = new Matrix(); 

 private float width, height, offstartY, moveX, moveY;; 

 private Bitmap bitmap_pressed, bitmap_normal, bitmap_error, bitmap_line,
  bitmap_line_error; 

 private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 

 private List<Point> pointList = new ArrayList<LockPatterView.Point>(); 

 private OnPatterChangeLister onPatterChangeLister; 

 /**
 * 构造函数
 */
 public LockPatterView(Context context, AttributeSet attrs, int defStyle) {
 super(context, attrs, defStyle);
 } 

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

 public LockPatterView(Context context) {
 super(context);
 } 

 /*********************************************************
 * 绘制9宫格
 * movePoint代表鼠标在移动,但是不是9宫格里面的点
 * isInit是否初始化过9个点
 * isSelect 点位是否被选中状态
 * isFinish 是否绘制完毕
 */
 private boolean isInit, isSelect, isFinish, movePoint; 

 @Override
 protected void onDraw(Canvas canvas) {
 // 第一次没有初始化就进行初始化,一旦初始化就不在初始化工作了,isInit的意思是---默认没有初始化过
 if (!isInit) {
  // 初始化9个点
  initPoints();
 }
 // 绘制9个点
 points2Canvas(canvas); 

 if (pointList.size() > 0) {
  Point a = pointList.get(0);
  // 绘制九宫格坐标点
  for (int i = 0; i < pointList.size(); i++) {
  Point b = pointList.get(i);
  line2Canvas(canvas, a, b);
  a = b;
  }
  // 绘制鼠标坐标点
  if (movePoint) {
  line2Canvas(canvas, a, new Point(moveX, moveY));
  }
 }
 } 

 /**
 * 初始化9个点位 获取点位的3种状态 线的2种状态 以及9点的坐标位置 以及初始化密码操作 isInit=
 * true设置状态--下次不必初始化话工作了
 */
 private void initPoints() { 

 // 获取布局宽高
 width = getWidth();
 height = getHeight(); 

 // 横屏和竖屏 

 offstartY = (height - width) / 2; 

 // 图片资源
 bitmap_normal = BitmapFactory.decodeResource(getResources(),
  R.drawable.btn_circle_normal);
 bitmap_pressed = BitmapFactory.decodeResource(getResources(),
  R.drawable.btn_circle_pressed);
 bitmap_error = BitmapFactory.decodeResource(getResources(),
  R.drawable.btn_circle_selected);
 bitmap_line = BitmapFactory.decodeResource(getResources(),
  R.drawable.ddd);
 bitmap_line_error = BitmapFactory.decodeResource(getResources(),
  R.drawable.qqq); 

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

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

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

 // 设置密码1--9
 int index = 1;
 for (Point[] points : this.points) {
  for (Point point : points) {
  point.index = index;
  index++;
  }
 }
 // 初始化完成
 isInit = true;
 } 

 /**
 * 将9个点绘制到画布 循环遍历9个点位, 根据3种不同的状态绘制3种不同的9个点位
 */
 private void points2Canvas(Canvas canvas) {
 // 循环遍历9个点位
 for (int i = 0; i < points.length; i++) {
  // 循环遍历每行的3个点位
  for (int j = 0; j < points[i].length; j++) {
  // 获取依次的某个点位
  Point point = points[i][j];
  if (point.state == Point.STATE_PRESSED) {
   // (Bitmap bitmap, float left, float top, Paint paint)
   canvas.drawBitmap(bitmap_pressed,
    point.x - bitmap_normal.getWidth() / 2, point.y
     - bitmap_normal.getHeight() / 2, paint);
  } else if (point.state == Point.STATE_ERROR) {
   canvas.drawBitmap(bitmap_error,
    point.x - bitmap_normal.getWidth() / 2, point.y
     - bitmap_normal.getHeight() / 2, paint);
  } else {
   canvas.drawBitmap(bitmap_normal,
    point.x - bitmap_normal.getWidth() / 2, point.y
     - bitmap_normal.getHeight() / 2, paint);
  }
  }
 }
 } 

 /**
 * 画线
 */
 public void line2Canvas(Canvas canvas, Point a, Point b) {
 // 线的长度--2点之间的距离
 float linelength = (float) Point.distance(a, b);
 // 获取2点之间的角度
 float degress = getDegrees(a, b);
 //根据a点进行旋转
 canvas.rotate(degress, a.x, a.y); 

 if (a.state == Point.STATE_PRESSED) {
  // xy方向上的缩放比例
  matrix.setScale(linelength / bitmap_line.getWidth(), 1);
  matrix.postTranslate(a.x - bitmap_line.getWidth() / 2, a.y
   - bitmap_line.getHeight() / 2);
  canvas.drawBitmap(bitmap_line, matrix, paint);
 } else {
  matrix.setScale(linelength / bitmap_line.getWidth(), 1);
  matrix.postTranslate(a.x - bitmap_line.getWidth() / 2, a.y
   - bitmap_line.getHeight() / 2);
  canvas.drawBitmap(bitmap_line_error, matrix, paint);
 }
 //画线完毕回归角度
 canvas.rotate(-degress, a.x, a.y);
 } 

 // 获取角度
 public float getDegrees(Point pointA, Point pointB) {
 return (float) Math.toDegrees(Math.atan2(pointB.y - pointA.y, pointB.x
  - pointA.x));
 } 

 /****************************************************************************
 * onTouch事件处理
 */ 

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 moveX = event.getX();
 moveY = event.getY(); 

 movePoint = false;
 isFinish = false; 

 Point point = null; 

 switch (event.getAction()) {
 //只要按下操作,就代表重新绘制界面
 case MotionEvent.ACTION_DOWN:
  if (onPatterChangeLister != null) {
  onPatterChangeLister.onPatterStart(true);
  }
  // 每次按下,都需要清空之前的集合
  resetPoint();
  // 检测是不是在九宫格内
  point = chechSelectPoint();
  if (point != null) {
  //如果按下的位置在9宫格内,就改成状态为true
  isSelect = true;
  }
  break;
 case MotionEvent.ACTION_MOVE:
  if (isSelect) {
  // 检测是不是在九宫格内
  point = chechSelectPoint();
  if (point == null) {
   movePoint = true;
  }
  }
  break;
 case MotionEvent.ACTION_UP:
  //绘制完毕,点位状态改为未选中
  isFinish = true;
  isSelect = false;
  break; 

 }
 // 如果没有绘制完毕,如果九宫格处于选中状态
 if (!isFinish && isSelect && point != null) {
  // 交叉点
  if (crossPoint(point)) {
  movePoint = true;
  } else {// 新点
  point.state = Point.STATE_PRESSED;
  pointList.add(point);
  }
 } 

 // 绘制结束
 if (isFinish) {
  // 绘制不成立
  if (pointList.size() == 1) {
  // resetPoint();
  errorPoint();
  } else if (pointList.size() < POINT_SIZE && pointList.size() > 0) {// 绘制错误
  errorPoint();
  if (onPatterChangeLister != null) {
   onPatterChangeLister.onPatterChange(null);
  }
  } else {
  if (onPatterChangeLister != null) {
   String pass = "";
   for (int i = 0; i < pointList.size(); i++) {
   pass = pass + pointList.get(i).index;
   }
   if (!TextUtils.isEmpty(pass)) {
   onPatterChangeLister.onPatterChange(pass);
   }
  }
  }
 } 

 postInvalidate();
 return true;
 } 

 /**
 * 重新绘制
 */
 public void resetPoint() {
 for (int i = 0; i < pointList.size(); i++) {
  Point point = pointList.get(i);
  point.state = Point.STATE_NORMAL;
 }
 pointList.clear();
 } 

 /**
 * 检查是否选中
 */
 private Point chechSelectPoint() {
 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, bitmap_normal.getWidth() / 2,
   moveX, moveY)) {
   return point;
  }
  }
 } 

 return null;
 } 

 /**
 * 交叉点
 */
 private boolean crossPoint(Point point) {
 if (pointList.contains(point)) {
  return true;
 } else {
  return false;
 }
 } 

 /**
 * 绘制错误
 */
 public void errorPoint() {
 for (Point point : pointList) {
  point.state = Point.STATE_ERROR;
 }
 } 

 /***********************************************************************
 * 自定义的点
 */
 public static class Point {
 // 正常
 public static int STATE_NORMAL = 0;
 // 选中
 public static int STATE_PRESSED = 1;
 // 错误
 public static int STATE_ERROR = 2;
 public float x, y;
 public int index = 0, state = 0; 

 public Point() {
 }; 

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

 /**
  * 两点之间的距离
  */
 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));
 } 

 /**
  */
 public static boolean with(float paintX, float pointY, float r,
  float moveX, float moveY) {
  return Math.sqrt((paintX - moveX) * (paintX - moveX)
   + (pointY - moveY) * (pointY - moveY)) < r;
 }
 } 

 /**
 * 图案监听器
 */
 public static interface OnPatterChangeLister {
 void onPatterChange(String passwordStr); 

 void onPatterStart(boolean isStart);
 } 

 /**
 * 设置图案监听器
 */
 public void SetOnPatterChangeLister(OnPatterChangeLister changeLister) {
 if (changeLister != null) {
  this.onPatterChangeLister = changeLister;
 }
 }
}

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

(0)

相关推荐

  • Android仿支付宝手势密码解锁功能

    Starting 创建手势密码可以查看 CreateGestureActivity.java 文件. 登陆验证手势密码可以看 GestureLoginActivity.java 文件. Features 使用了 JakeWharton/butterknife butterknife 使用了 ACache 来存储手势密码 /** * 保存手势密码 */ private void saveChosenPattern(List<LockPatternView.Cell> cells) { byte[

  • Android 简易手势密码开源库详解

    简介 本文介绍一个Android手势密码开源库的使用及实现的详细过程,该开源库主要实现以下几个功能: 支持手势密码的绘制,并支持密码保存功能,解锁时自动比对密码给出结果 封装了绘制密码的方法,比对两次密码是否一致,可以快捷地进行手势密码的设置 可以设置密码输入错误后的重试次数上限 可以自定义不同状态下手势密码图案的颜色 可以自定义手势密码的触摸点数量(n*n) 最近需要用到手势密码解锁功能,找了一些demo感觉用起来都有点麻烦,于是参考一些文章自己造了下轮子,封装了相关的一些方法,使用起来比较便

  • Android手势密码实现实例代码

    一.效果实现 二.实现思路: 1. 正上方的提示区域,用一个类(LockIndicator.java)来实现,自定义view来绘制9个提示图标: 2. 手势密码绘制区域,用一个类(GestureContentView.java)来实现,它继承自ViewGroup里面, 添加9个ImageView来表示图标, 在onLayout()方法中设置它们的位置: 3. 手势路径绘制, 用一个类(GestureDrawline.java)来实现,复写onTouchEvent()方法,在这个方法里面监听Tou

  • Android手势密码的实现

    一.大致界面介绍: 图1 图2 图3 图4 图1:手势密码绘制界面 [主要是绘制上方的9个提示图标和9个宫格密码图标] 图2:设置手势密码 [监听手势的输入,TouchEvent的事件处理,获取输入的手势密码,同时显示在上方的提示区域] 图3:再绘制一次,两次密码不一致提示界面 [这里在实现的时候,错误提示文字加了"左右晃动的动画",错误路径颜色标记为红色] 图4:校验手势密码,输入的密码错误,给予红色路径+错误文字提示 二.实现思路: 1. 正上方的提示区域,用一个类(LockInd

  • Android自定义UI手势密码简单版

    先看看效果图: ImageLockActivity package com.example.imagelock; import com.example.view.NinePointLineView; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; public class ImageLockActivity extends Acti

  • Android自定义UI手势密码终结版

    之前写过3篇手势密码的demo,不过没有集成到真实的企业项目中,这几天正好领到一个手势密码项目,昨天刚好弄完,今天抽空整理下,目前还没有完善,有一些地方需要更改,不过基本的流程都可以跑通了. 源码下载地址:http://xiazai.jb51.net/201610/yuanma/AndroidGestureLock(jb51.net).rar 先看主界面的入口把.里面有2个button(一个是设置手势密码.一个是校验手势密码) activity_main.xml <RelativeLayout

  • Android手势密码view学习笔记(二)

    我们还是接着我们上一篇博客中的内容往下讲哈,上一节 Android手势密码view笔记(一)我们已经实现了我们的IndicatorView指示器view了: 下面我们来实现下我们的手势密码view: 实现思路: 1.我们照样需要拿到用户需要显示的一些属性(行.列.选中的图片.未选中的图片.错误显示的图片.连接线的宽度跟颜色......). 2.我们需要根据手势的变换然后需要判断当前手指位置是不是在某个点中,在的话就把该点设置为选中状态,然后每移动到两个点(也就是一个线段)就记录该两个点. 3.最

  • Android手势密码view学习笔记(一)

    刚接触Android的时候看到别人写的手势密码view,然后当时就在想,我什么时候才能写出如此高端的东西?? 没关系,不要怕哈,说出这样话的人不是你技术不咋地而是你不愿意花时间去研究它,其实也没有那么难哦(世上无难事,只怕有心人!),下面我们就一步一步实现一个手势密码view. 想必都看过手势密码view,但我们还是看看我们今天要实现的效果吧: 上面是一个手势view的提示view,下面是一个手势view. 用法: <com.leo.library.view.GestureContentView

  • Android自定义控件实现手势密码

    Android手势解锁密码效果图 首先呢想写这个手势密码的想法呢,完全是凭空而来的,然后笔者就花了一天时间弄出来了.本以为这个东西很简单,实际上手的时候发现,还有很多逻辑需要处理,稍不注意就容易乱套.写个UI效果图大约只花了3个小时,但是处理逻辑就处理了2个小时!废话不多说,下面开始讲解.      楼主呢,自己比较自定义控件,什么东西都掌握在自己的手里感觉那是相当不错(对于赶工期的小伙瓣儿们还是别手贱了,非常容易掉坑),一有了这个目标,我就开始构思实现方式.      1.整个自定义控件是继承

  • Android自定义UI手势密码改进版源码下载

    在之前文章的铺垫下,再为大家分享一篇:Android手势密码,附源码下载,不要错过. 源码下载:http://xiazai.jb51.net/201610/yuanma/androidLock(jb51.net).rar 先看第一张图片的布局文件 activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://sc

随机推荐