<ignore_js_op>                                                 

下面是最重要的那个LocusPassWordView:

/**
*
* 九宫格解锁
*
* @author way
*
*/
public class LocusPassWordView extends View {
private float w = 0;
private float h = 0; //
private boolean isCache = false;
//
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //
private Point[][] mPoints = new Point[3][3];
// 圆的半径
private float r = 0;
// 选中的点
private List<Point> sPoints = new ArrayList<Point>();
private boolean checking = false;
private Bitmap locus_round_original;// 圆点初始状态时的图片
private Bitmap locus_round_click;// 圆点点击时的图片
private Bitmap locus_round_click_error;// 出错时圆点的图片
private Bitmap locus_line;// 正常状态下线的图片
private Bitmap locus_line_semicircle;
private Bitmap locus_line_semicircle_error;
private Bitmap locus_arrow;// 线的移动方向
private Bitmap locus_line_error;// 错误状态下的线的图片
private long CLEAR_TIME = 0;// 清除痕迹的时间
private int passwordMinLength = 5;// 密码最小长度
private boolean isTouch = true; // 是否可操作
private Matrix mMatrix = new Matrix();
private int lineAlpha = 50;// 连线的透明度 public LocusPassWordView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
} public LocusPassWordView(Context context, AttributeSet attrs) {
super(context, attrs);
} public LocusPassWordView(Context context) {
super(context);
} @Override
public void onDraw(Canvas canvas) {
if (!isCache) {
initCache();
}
drawToCanvas(canvas);
} private void drawToCanvas(Canvas canvas) { // mPaint.setColor(Color.RED);
// Point p1 = mPoints[1][1];
// Rect r1 = new Rect(p1.x - r,p1.y - r,p1.x +
// locus_round_click.getWidth() - r,p1.y+locus_round_click.getHeight()-
// r);
// canvas.drawRect(r1, mPaint);
// 画所有点
for (int i = 0; i < mPoints.length; i++) {
for (int j = 0; j < mPoints[i].length; j++) {
Point p = mPoints[i][j];
if (p.state == Point.STATE_CHECK) {
canvas.drawBitmap(locus_round_click, p.x - r, p.y - r,
mPaint);
} else if (p.state == Point.STATE_CHECK_ERROR) {
canvas.drawBitmap(locus_round_click_error, p.x - r,
p.y - r, mPaint);
} else {
canvas.drawBitmap(locus_round_original, p.x - r, p.y - r,
mPaint);
}
}
}
// mPaint.setColor(Color.BLUE);
// canvas.drawLine(r1.left+r1.width()/2, r1.top, r1.left+r1.width()/2,
// r1.bottom, mPaint);
// canvas.drawLine(r1.left, r1.top+r1.height()/2, r1.right,
// r1.bottom-r1.height()/2, mPaint); // 画连线
if (sPoints.size() > 0) {
int tmpAlpha = mPaint.getAlpha();
mPaint.setAlpha(lineAlpha);
Point tp = sPoints.get(0);
for (int i = 1; i < sPoints.size(); i++) {
Point p = sPoints.get(i);
drawLine(canvas, tp, p);
tp = p;
}
if (this.movingNoPoint) {
drawLine(canvas, tp, new Point((int) moveingX, (int) moveingY));
}
mPaint.setAlpha(tmpAlpha);
lineAlpha = mPaint.getAlpha();
} } /**
* 初始化Cache信息
*
* @param canvas
*/
private void initCache() { w = this.getWidth();
h = this.getHeight();
float x = 0;
float y = 0; // 以最小的为准
// 纵屏
if (w > h) {
x = (w - h) / 2;
w = h;
}
// 横屏
else {
y = (h - w) / 2;
h = w;
} locus_round_original = BitmapFactory.decodeResource(
this.getResources(), R.drawable.locus_round_original);
locus_round_click = BitmapFactory.decodeResource(this.getResources(),
R.drawable.locus_round_click);
locus_round_click_error = BitmapFactory.decodeResource(
this.getResources(), R.drawable.locus_round_click_error); locus_line = BitmapFactory.decodeResource(this.getResources(),
R.drawable.locus_line);
locus_line_semicircle = BitmapFactory.decodeResource(
this.getResources(), R.drawable.locus_line_semicircle); locus_line_error = BitmapFactory.decodeResource(this.getResources(),
R.drawable.locus_line_error);
locus_line_semicircle_error = BitmapFactory.decodeResource(
this.getResources(), R.drawable.locus_line_semicircle_error); locus_arrow = BitmapFactory.decodeResource(this.getResources(),
R.drawable.locus_arrow);
// Log.d("Canvas w h :", "w:" + w + " h:" + h); // 计算圆圈图片的大小
float canvasMinW = w;
if (w > h) {
canvasMinW = h;
}
float roundMinW = canvasMinW / 8.0f * 2;
float roundW = roundMinW / 2.f;
//
float deviation = canvasMinW % (8 * 2) / 2;
x += deviation;
x += deviation; if (locus_round_original.getWidth() > roundMinW) {
float sf = roundMinW * 1.0f / locus_round_original.getWidth(); // 取得缩放比例,将所有的图片进行缩放
locus_round_original = BitmapUtil.zoom(locus_round_original, sf);
locus_round_click = BitmapUtil.zoom(locus_round_click, sf);
locus_round_click_error = BitmapUtil.zoom(locus_round_click_error,
sf); locus_line = BitmapUtil.zoom(locus_line, sf);
locus_line_semicircle = BitmapUtil.zoom(locus_line_semicircle, sf); locus_line_error = BitmapUtil.zoom(locus_line_error, sf);
locus_line_semicircle_error = BitmapUtil.zoom(
locus_line_semicircle_error, sf);
locus_arrow = BitmapUtil.zoom(locus_arrow, sf);
roundW = locus_round_original.getWidth() / 2;
} mPoints[0][0] = new Point(x + 0 + roundW, y + 0 + roundW);
mPoints[0][1] = new Point(x + w / 2, y + 0 + roundW);
mPoints[0][2] = new Point(x + w - roundW, y + 0 + roundW);
mPoints[1][0] = new Point(x + 0 + roundW, y + h / 2);
mPoints[1][1] = new Point(x + w / 2, y + h / 2);
mPoints[1][2] = new Point(x + w - roundW, y + h / 2);
mPoints[2][0] = new Point(x + 0 + roundW, y + h - roundW);
mPoints[2][1] = new Point(x + w / 2, y + h - roundW);
mPoints[2][2] = new Point(x + w - roundW, y + h - roundW);
int k = 0;
for (Point[] ps : mPoints) {
for (Point p : ps) {
p.index = k;
k++;
}
}
r = locus_round_original.getHeight() / 2;// roundW;
isCache = true;
} /**
* 画两点的连接
*
* @param canvas
* @param a
* @param b
*/
private void drawLine(Canvas canvas, Point a, Point b) {
float ah = (float) MathUtil.distance(a.x, a.y, b.x, b.y);
float degrees = getDegrees(a, b);
// Log.d("=============x===========", "rotate:" + degrees);
canvas.rotate(degrees, a.x, a.y); if (a.state == Point.STATE_CHECK_ERROR) {
mMatrix.setScale((ah - locus_line_semicircle_error.getWidth())
/ locus_line_error.getWidth(), 1);
mMatrix.postTranslate(a.x, a.y - locus_line_error.getHeight()
/ 2.0f);
canvas.drawBitmap(locus_line_error, mMatrix, mPaint);
canvas.drawBitmap(locus_line_semicircle_error, a.x
+ locus_line_error.getWidth(),
a.y - locus_line_error.getHeight() / 2.0f, mPaint);
} else {
mMatrix.setScale((ah - locus_line_semicircle.getWidth())
/ locus_line.getWidth(), 1);
mMatrix.postTranslate(a.x, a.y - locus_line.getHeight() / 2.0f);
canvas.drawBitmap(locus_line, mMatrix, mPaint);
canvas.drawBitmap(locus_line_semicircle, a.x + ah
- locus_line_semicircle.getWidth(),
a.y - locus_line.getHeight() / 2.0f, mPaint);
} canvas.drawBitmap(locus_arrow, a.x, a.y - locus_arrow.getHeight()
/ 2.0f, mPaint); canvas.rotate(-degrees, a.x, a.y); } public float getDegrees(Point a, Point b) {
float ax = a.x;// a.index % 3;
float ay = a.y;// a.index / 3;
float bx = b.x;// b.index % 3;
float by = b.y;// b.index / 3;
float degrees = 0;
if (bx == ax) // y轴相等 90度或270
{
if (by > ay) // 在y轴的下边 90
{
degrees = 90;
} else if (by < ay) // 在y轴的上边 270
{
degrees = 270;
}
} else if (by == ay) // y轴相等 0度或180
{
if (bx > ax) // 在y轴的下边 90
{
degrees = 0;
} else if (bx < ax) // 在y轴的上边 270
{
degrees = 180;
}
} else {
if (bx > ax) // 在y轴的右边 270~90
{
if (by > ay) // 在y轴的下边 0 - 90
{
degrees = 0;
degrees = degrees
+ switchDegrees(Math.abs(by - ay),
Math.abs(bx - ax));
} else if (by < ay) // 在y轴的上边 270~0
{
degrees = 360;
degrees = degrees
- switchDegrees(Math.abs(by - ay),
Math.abs(bx - ax));
} } else if (bx < ax) // 在y轴的左边 90~270
{
if (by > ay) // 在y轴的下边 180 ~ 270
{
degrees = 90;
degrees = degrees
+ switchDegrees(Math.abs(bx - ax),
Math.abs(by - ay));
} else if (by < ay) // 在y轴的上边 90 ~ 180
{
degrees = 270;
degrees = degrees
- switchDegrees(Math.abs(bx - ax),
Math.abs(by - ay));
} } }
return degrees;
} /**
* 1=30度 2=45度 4=60度
*
* @param tan
* @return
*/
private float switchDegrees(float x, float y) {
return (float) MathUtil.pointTotoDegrees(x, y);
} /**
* 取得数组下标
*
* @param index
* @return
*/
public int[] getArrayIndex(int index) {
int[] ai = new int[2];
ai[0] = index / 3;
ai[1] = index % 3;
return ai;
} /**
*
* 检查
*
* @param x
* @param y
* @return
*/
private Point checkSelectPoint(float x, float y) {
for (int i = 0; i < mPoints.length; i++) {
for (int j = 0; j < mPoints[i].length; j++) {
Point p = mPoints[i][j];
if (RoundUtil.checkInRound(p.x, p.y, r, (int) x, (int) y)) {
return p;
}
}
}
return null;
} /**
* 重置
*/
private void reset() {
for (Point p : sPoints) {
p.state = Point.STATE_NORMAL;
}
sPoints.clear();
this.enableTouch();
} /**
* 判断点是否有交叉 返回 0,新点 ,1 与上一点重叠 2,与非最后一点重叠
*
* @param p
* @return
*/
private int crossPoint(Point p) {
// 重叠的不最后一个则 reset
if (sPoints.contains(p)) {
if (sPoints.size() > 2) {
// 与非最后一点重叠
if (sPoints.get(sPoints.size() - 1).index != p.index) {
return 2;
}
}
return 1; // 与最后一点重叠
} else {
return 0; // 新点
}
} /**
* 添加一个点
*
* @param point
*/
private void addPoint(Point point) {
this.sPoints.add(point);
} /**
* 转换为String
*
* @param points
* @return
*/
private String toPointString() {
if (sPoints.size() > passwordMinLength) {
StringBuffer sf = new StringBuffer();
for (Point p : sPoints) {
sf.append(",");
sf.append(p.index);
}
return sf.deleteCharAt(0).toString();
} else {
return "";
}
} boolean movingNoPoint = false;
float moveingX, moveingY; @Override
public boolean onTouchEvent(MotionEvent event) {
// 不可操作
if (!isTouch) {
return false;
} movingNoPoint = false; float ex = event.getX();
float ey = event.getY();
boolean isFinish = false;
boolean redraw = false;
Point p = null;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 点下
// 如果正在清除密码,则取消
if (task != null) {
task.cancel();
task = null;
Log.d("task", "touch cancel()");
}
// 删除之前的点
reset();
p = checkSelectPoint(ex, ey);
if (p != null) {
checking = true;
}
break;
case MotionEvent.ACTION_MOVE: // 移动
if (checking) {
p = checkSelectPoint(ex, ey);
if (p == null) {
movingNoPoint = true;
moveingX = ex;
moveingY = ey;
}
}
break;
case MotionEvent.ACTION_UP: // 提起
p = checkSelectPoint(ex, ey);
checking = false;
isFinish = true;
break;
}
if (!isFinish && checking && p != null) { int rk = crossPoint(p);
if (rk == 2) // 与非最后一重叠
{
// reset();
// checking = false; movingNoPoint = true;
moveingX = ex;
moveingY = ey; redraw = true;
} else if (rk == 0) // 一个新点
{
p.state = Point.STATE_CHECK;
addPoint(p);
redraw = true;
}
// rk == 1 不处理 } // 是否重画
if (redraw) { }
if (isFinish) {
if (this.sPoints.size() == 1) {
this.reset();
} else if (this.sPoints.size() < passwordMinLength
&& this.sPoints.size() > 0) {
// mCompleteListener.onPasswordTooMin(sPoints.size());
error();
clearPassword();
Toast.makeText(this.getContext(), "密码太短,请重新输入!",
Toast.LENGTH_SHORT).show();
} else if (mCompleteListener != null) {
if (this.sPoints.size() >= passwordMinLength) {
this.disableTouch();
mCompleteListener.onComplete(toPointString());
} }
}
this.postInvalidate();
return true;
} /**
* 设置已经选中的为错误
*/
private void error() {
for (Point p : sPoints) {
p.state = Point.STATE_CHECK_ERROR;
}
} /**
* 设置为输入错误
*/
public void markError() {
markError(CLEAR_TIME);
} /**
* 设置为输入错误
*/
public void markError(final long time) {
for (Point p : sPoints) {
p.state = Point.STATE_CHECK_ERROR;
}
this.clearPassword(time);
} /**
* 设置为可操作
*/
public void enableTouch() {
isTouch = true;
} /**
* 设置为不可操作
*/
public void disableTouch() {
isTouch = false;
} private Timer timer = new Timer();
private TimerTask task = null; /**
* 清除密码
*/
public void clearPassword() {
clearPassword(CLEAR_TIME);
} /**
* 清除密码
*/
public void clearPassword(final long time) {
if (time > 1) {
if (task != null) {
task.cancel();
Log.d("task", "clearPassword cancel()");
}
lineAlpha = 130;
postInvalidate();
task = new TimerTask() {
public void run() {
reset();
postInvalidate();
}
};
Log.d("task", "clearPassword schedule(" + time + ")");
timer.schedule(task, time);
} else {
reset();
postInvalidate();
} } //
private OnCompleteListener mCompleteListener; /**
* @param mCompleteListener
*/
public void setOnCompleteListener(OnCompleteListener mCompleteListener) {
this.mCompleteListener = mCompleteListener;
} /**
* 取得密码
*
* @return
*/
private String getPassword() {
SharedPreferences settings = this.getContext().getSharedPreferences(
this.getClass().getName(), 0);
return settings.getString("password", ""); // , "0,1,2,3,4,5,6,7,8"
} /**
* 密码是否为空
*
* @return
*/
public boolean isPasswordEmpty() {
return StringUtil.isEmpty(getPassword());
} public boolean verifyPassword(String password) {
boolean verify = false;
if (com.way.util.StringUtil.isNotEmpty(password)) {
// 或者是超级密码
if (password.equals(getPassword())) {
verify = true;
}
}
return verify;
} /**
* 设置密码
*
* @param password
*/
public void resetPassWord(String password) {
SharedPreferences settings = this.getContext().getSharedPreferences(
this.getClass().getName(), 0);
Editor editor = settings.edit();
editor.putString("password", password);
editor.commit();
} public int getPasswordMinLength() {
return passwordMinLength;
} public void setPasswordMinLength(int passwordMinLength) {
this.passwordMinLength = passwordMinLength;
} /**
* 轨迹球画完成事件
*
* @author way
*/
public interface OnCompleteListener {
/**
* 画完了
*
* @param str
*/
public void onComplete(String password);
}
}

代码下载

Android之九宫格解锁的实现的更多相关文章

  1. appium 九宫格解锁招商银行手机客户端app

    之前研究了一段时间的appium for native app 相应的总结如下:                                            appium测试环境搭建 :ht ...

  2. App自动化(2)--Python&Appium实现安卓手机九宫格解锁

    九宫格作为常见的手势密码,我们在使用的时候,是从起点开始,按住不放,然后滑动手指,直到最后一个点松开手指,如果与设置的手势密码匹配,则解锁成功. 现在大多数九宫格作为一个元素存在,很难定位到每一个点. ...

  3. uiautomator2 实现App九宫格解锁

    App九宫格解锁 之前在testerhome社区看见codeskyblue大佬写过一种方法,但是这种办法存在一个弊端,那就是多个点的坐标是写死的,也就是说要是换了部手机,九宫格解锁就行不通了,于是就想 ...

  4. Python+Appium自动化测试(10)-TouchAction类与MultiAction类(控件元素的滑动、拖动,九宫格解锁,手势操作等)

    滑动屏幕方法swipe一般用于对页面进行上下左右滑动操作,但自动化过程中还会遇到其他情况,如对控件元素进行滑动.拖拽操作,九宫格解锁,手势操作,地图的放大与缩小等.这些需要针对控件元素的滑动操作,或者 ...

  5. Android 修改屏幕解锁方式

    Android 修改屏幕解锁方式 问题 在手机第一次开机的时候,运行手机激活的APP 在激活APP允许过程中,当用户按电源键的时候,屏幕黑掉,进入锁屏状态 手机默认的锁屏是滑动解锁 用户这个时候再一次 ...

  6. Android微信九宫格图片展示控件

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/214 Android微信九宫格图片展示控件 半年前,公司产 ...

  7. Android手势解锁, 九宫格解锁

    给大家介绍一个很好用的手势解锁控件ShapleLocker, 废话不多先上效果图: 这是一个第三方库, 可自己根据UI需求替换图标: 圆圈, 小箭头等等.. github地址: http://pane ...

  8. Android自定义九宫格图案解锁

    转自: http://blog.csdn.net/shineflowers/article/details/50408350

  9. IOS仿Android九宫格解锁效果[转]

    原理很简单,监听view中touch的一系列事件,当判定手指位置在某个按钮附近的时候则判断此按钮选中,并画出线. 效果图如下: 你可以在NineGridUnlockView.m文件中方法 touche ...

随机推荐

  1. Two Cakes

    It's New Year's Eve soon, so Ivan decided it's high time he started setting the table. Ivan has boug ...

  2. LeetCode Distribute Candies

    原题链接在这里:https://leetcode.com/problems/distribute-candies/#/description 题目: Given an integer array wi ...

  3. webpack学习(一)—— 入门

    ,我们通常采用的是组件化开发方式,这样就会对应有很多个js文件,而打包工具的出现则是为了正确处理这些js文件的依赖关系,并生成一个最终的文件,这样,我们最后只需要加载打包以后的文件就可以了,而无须加载 ...

  4. 在winform下实现左右布局多窗口界面的方法(二)

    这篇文章主要介绍了在winform下实现左右布局多窗口界面的方法之续篇 的相关资料,需要的朋友可以参考下 在上篇文章在winform下实现左右布局多窗口界面的方法(一)已经实现了左右布局多窗口界面,今 ...

  5. poj 2262 Goldbach's Conjecture——筛质数(水!)

    题目:http://poj.org/problem?id=2262 大水题的筛质数. #include<iostream> #include<cstdio> #include& ...

  6. jenkins 参数化构建,获取git分支

    def heads= ("git ls-remote -h git@gitlab.com:*.git").execute()def headlist=heads.text.read ...

  7. PHP类(二)-类的构造方法和析构方法

    构造方法 构造方法是对象创建完成后第一个被对象自动调用的方法,用来完成对象的初始化 在每个类中都会有一个构造方法,如果没有声明的话,类中会存在一个没有参数列表并且内容为空的构造方法.如果声明的话,默认 ...

  8. C语言-数组

    数组是具有同一属性的若干个数据组织成一个整体,互相关联 数组是有序数据的集合.数组中的每一个元素都属于同一个数据类型,用一个统一的数组名和下标来唯一地确定数组中的元素 一维数组 一维数组的定义 在定义 ...

  9. CentOS 配置XWIN/VNC

    Xwin服务器 CentOS上运维Xwin,在这之前需要理清一些关系: 一,  X window 包括xserver 和x client.linux下的xserver 主要有xorg.xfree86, ...

  10. java 多线程系列基础篇(十一)之生产消费者问题

    1. 生产/消费者模型 生产/消费者问题是个非常典型的多线程问题,涉及到的对象包括“生产者”.“消费者”.“仓库”和“产品”.他们之间的关系如下:(01) 生产者仅仅在仓储未满时候生产,仓满则停止生产 ...