九宮格解鎖在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);
- }
- @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);
- }
- }
這裏只貼出了最重要的那部分代碼,有興趣的朋友可以下載源碼看看