Android九宮格解鎖

當年感覺九宮格解鎖很是高大上,一臉懵逼,今天正好要做解鎖這一塊業(yè)務,回頭來看九宮格,這特么簡單啊

首先理清一下邏輯,我們要做NxN的九宮格 下圖是3x3的簡單圖例
// -(--)-(--)-(--)-
// -(--)-(--)-(--)-
// -(--)-(--)-(--)-

Image 2.png

我們就把九宮格分解成
外圓 、內圓、連線三部分
外圓半徑Radius,內圓半徑dp(5)
建立一個集合來放置 外圓的圓心( 內圓的圓心也一樣)

    private ArrayList<Point> mListCircle;//外圓的圓心

 for (int i = 0; i < mCount; i++) {
            for (int j = 0; j < mCount; j++) {
                Point point = new Point((3 * i + 2) * (int) mRadius, (3 * j + 2) * (int) mRadius);
                mListCircle.add(point);
            }
        }

這樣我們就初始化好了內外圓的位置point集合
我們draw一下看一下效果

void drawAll_Cicle(Canvas canvas) {
        for (int i = 0; i < mListCircle.size(); i++) {
            Point point = mListCircle.get(i);
                canvas.drawCircle(point.x, point.y, mRadius, mPaint);
                canvas.drawCircle(point.x, point.y, mMinRadius, miniPaint);
        }

效果圖就是上圖了 (哈哈一樣的)

主結構已經畫完了,接下來就是如何繪制點與點之間的連線了,有人會覺得沒思路,其實很簡單了,以3X3 為例子哈

我們可以給這九個棋子編號1--9號,把他存入LinkedHashSet中,著重介紹這個LinkedHashSet有順序不重復這個真的在合適不過了。
這樣就不會有重復的事情了。這個和解鎖時候的密碼也很契合。所以選對了存儲方式會事半功倍。

還有一個問題,就是點擊邊界問題,這個好解決,我們把每一個棋子都花矩形,通過圓來控制邊界,(其實也可以通過矩形來控制邊界,這個也很簡單,原理是差不多的,有興趣的同學可以下去試試)
同樣也是用集合。和上邊的圓是一樣一樣的。

ArrayList<RectF> mListRectFs;
for (int i = 0; i < mCount; i++) {
            for (int j = 0; j < mCount; j++) {
                RectF rectF = new RectF((3 * i + 1) * mRadius, (3 * j + 1) * mRadius, (3 * i + 3) * mRadius, (3 * j + 3) * mRadius);
                mListRectFs.add(rectF);
            }
        }

好邊界也有了,我們來計算邊界返回編號

/**
     * 點和圓形碰撞檢測
     *
     * @param x1     手指接觸點
     * @param y1     手指接觸點
     * @param x2     外圓
     * @param y2     外圓
     * @param radius 半徑
     * @return
     */
    private boolean isCollision(float x1, float y1, float x2, float y2, float radius) {
        if (Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) <= radius) {
            // 如果點和圓心距離小于或等于半徑則認為發(fā)生碰撞
            return true;
        }
        return false;
    }


/**
     * 判斷觸摸點在哪個item上
     *
     * @param x
     * @param y
     * @return
     */
    private int touchIndex(float x, float y) {
        for (int i = 0; i < mListCircle.size(); i++) {
            Point p = mListCircle.get(i);
            if (isCollision(x, y, p.x, p.y, mRadius)) {
                return i;
            }
        }

        return -1;
    }

幾個關鍵點都寫到了接下來就是具體的細節(jié)了。我把代碼都貼上來,注釋的很詳細。當然加入了一個手指觸控點,更加好看一些。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;

/**
 * Created by ld on 2017/7/25.
 */

public class NineGridLockView extends View {
    private Context context;
    int index_point = 0;
    private float mDensity;
    private int mCount = 3;
    private ArrayList<RectF> mListRectFs;//圓的外形矩形
    private ArrayList<Point> mListCircle;//外圓的圓心
    private LinkedHashSet<Integer> mSetPoints;//記錄需要連線的外圓圓心點在mListCircle中的索引值,LinkedHashSet線性不可重復 集合,F(xiàn)IFO
    private Paint mPaint, miniPaint;//畫筆
    private float mRadius;//外圓半徑
    private float mMinRadius;//內圓半徑
    private float mStrokeWidth = 10; //繪制時的畫筆寬度
    private Point mMovePoint; //記錄手指移動時的點

    public NineGridLockView(Context context) {
        this(context, null);
    }

    public NineGridLockView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        mDensity = getContext().getResources().getDisplayMetrics().density;
        mListRectFs = new ArrayList<>();
        mListCircle = new ArrayList<>();
        mPaint = new Paint();
        mPaint.setStrokeWidth(5);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.BLUE);

        miniPaint = new Paint();
        miniPaint.setColor(Color.BLACK);
        miniPaint.setAntiAlias(true);
        miniPaint.setStyle(Paint.Style.STROKE);
        miniPaint.setStrokeWidth(5);
        mMinRadius = dp(5);
        mSetPoints = new LinkedHashSet<>();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawAll_Cicle(canvas);
        draw_line(canvas);
    }

    private int dp(int dp) {
        return (int) (dp * mDensity + 0.5f);
    }

    //繪制 連接線
    void draw_line(Canvas canvas) {
        Point p1 = null;
        Point p2 = null;
        //從結合中獲取 move過的點
        for (int index : mSetPoints) {
            //當?shù)谝粋€點為null的時候 給第一個點賦值
            if (p1 == null) {
                p1 = mListCircle.get(index);
                //然后 給第二個點賦值  兩點一條線
            } else if (p2 == null) {
                p2 = mListCircle.get(index);
                canvas.drawLine(p1.x, p1.y, p2.x, p2.y, mPaint);
                //p1 挪到p2
                p1 = p2;
                //接下來就是第三個點了 乃至更多點
            } else {
                //p2重新賦值  兩點一條線
                p2 = mListCircle.get(index);
                canvas.drawLine(p1.x, p1.y, p2.x, p2.y, mPaint);
                //p1 挪到p2
                p1 = p2;
            }

        }

        //繪制實時連線
        if (mMovePoint != null && p1 != null) {
            canvas.drawLine(p1.x, p1.y, mMovePoint.x, mMovePoint.y, mPaint);
        }
    }

    //繪制 外圓 內圓
    void drawAll_Cicle(Canvas canvas) {
        for (int i = 0; i < mListRectFs.size(); i++) {
            Point point = mListCircle.get(i);
            //如果move過的 就換顏色
            if (mSetPoints.contains(i)) {
                canvas.drawCircle(point.x, point.y, mRadius, miniPaint);
                canvas.drawCircle(point.x, point.y, mMinRadius, mPaint);

            } else {

                canvas.drawCircle(point.x, point.y, mRadius, mPaint);
                canvas.drawCircle(point.x, point.y, mMinRadius, miniPaint);
            }

        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        float w = Math.min(getMeasuredWidth(), getMeasuredHeight());
        //      -(--)-(--)-(--)-
        //      -(--)-(--)-(--)-
        //      -(--)-(--)-(--)-
        mRadius = getMeasuredWidth() * 1.00f / (mCount * 3 + 1);

        float rectWH = mRadius * 2;
        mListRectFs.clear();
        mListCircle.clear();
        for (int i = 0; i < mCount; i++) {
            for (int j = 0; j < mCount; j++) {
                RectF rectF = new RectF((3 * i + 1) * mRadius, (3 * j + 1) * mRadius, (3 * i + 3) * mRadius, (3 * j + 3) * mRadius);
                mListRectFs.add(rectF);
                Point point = new Point((3 * i + 2) * (int) mRadius, (3 * j + 2) * (int) mRadius);


                mListCircle.add(point);
            }
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float point_x = event.getX();
        float point_y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:


                break;
            case MotionEvent.ACTION_UP:
                mMovePoint = null;
                mSetPoints.clear();
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:

                int index = touchIndex(point_x, point_y);
                if (mMovePoint == null) {
                    mMovePoint = new Point((int) point_x, (int) point_y);
                } else {
                    mMovePoint.set((int) point_x, (int) point_y);
                }

                if (index != -1) {
                    mSetPoints.add(index);
                    if (index_point != mSetPoints.size()) {
                        index_point = mSetPoints.size();
                        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
                    }
                }
                invalidate();
                break;
        }

        return true;
    }


    /**
     * 判斷觸摸點在哪個item上
     *
     * @param x
     * @param y
     * @return
     */
    private int touchIndex(float x, float y) {
        for (int i = 0; i < mListCircle.size(); i++) {
            Point p = mListCircle.get(i);
            if (isCollision(x, y, p.x, p.y, mRadius)) {
                return i;
            }
        }

        return -1;
    }

    /**
     * 點和圓形碰撞檢測
     *
     * @param x1     手指接觸點
     * @param y1     手指接觸點
     * @param x2     外圓
     * @param y2     外圓
     * @param radius 半徑
     * @return
     */
    private boolean isCollision(float x1, float y1, float x2, float y2, float radius) {
        if (Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) <= radius) {
            // 如果點和圓心距離小于或等于半徑則認為發(fā)生碰撞
            return true;
        }
        return false;
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 公司新項目中需要用到九宮格解鎖這個功能。都說不會偷懶的程序猿不是好程序猿,第一時間肯定是先去github上查找是否...
    饅頭先生閱讀 4,766評論 16 32
  • 基上篇TouchID 指紋解鎖 的技術文, 然后目前又練習一種解鎖方式: 九宮格手勢解鎖. 在一些涉及個人隱私的場...
    smile麗語閱讀 5,412評論 21 43
  • 源碼下載 項目中需求用到圖案解鎖的功能,就自己寫了類似的功能:說下思路: 1.實現(xiàn)一個子類繼承View 2.覆蓋o...
    IT楓閱讀 3,102評論 0 5
  • 前言 九宮格手勢解鎖已經是非常常見的手機解鎖方式,支付寶等一些軟件也都曾經使用過,感覺還是很高大上的。在githu...
    珠穆朗瑪小王子閱讀 552評論 0 0
  • 想買一樣東西,錢富裕就買,沒錢就努力掙多多錢去買。不要退而求其次,那個“次”的東西就算買回家了,還是會看不順眼,還...
    書宸閱讀 609評論 0 0

友情鏈接更多精彩內容