Android之九宫格解锁的实现

来源:互联网 发布:kali linux双系统安装 编辑:程序博客网 时间:2024/04/27 06:11

九宫格解锁在Android中应用的很广泛,也是Android特有的一种解锁方式,其实实现起来也并不是很复杂,下面我就根据系统源码LockPatternView,移植出来的一个更加简单小巧九宫格解锁的例子,和大家一起学习一下。图片资源来自"支付宝钱包",先看看效果图:

源码下载地址:http://download.csdn.net/detail/weidi1989/5374787

                                                   


下面是最重要的那个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);}@Overridepublic 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) {// 重叠的不最后一个则 resetif (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;@Overridepublic 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);}}

这里只贴出了最重要的那部分代码,有兴趣的朋友可以下载源码看看