自定義九宮格解鎖控件

前言

九宮格手勢解鎖已經(jīng)是非常常見的手機解鎖方式,支付寶等一些軟件也都曾經(jīng)使用過,感覺還是很高大上的。在github已經(jīng)有很好的開源控件,大家可以去自己搜索,我自己寫了一個自定義的九宮格控件,作為練習(xí)作業(yè)。

效果圖##

我不會做動圖,就湊合看吧......


這里寫圖片描述

先貼自定義屬性代碼<declare-styleable name="DrawPointLineUnlockView"> <attr name="columns" format="integer"/> <attr name="rows" format="integer"/> <attr name="lineWidth" format="dimension"/> <attr name="pointRadius" format="dimension"/> <attr name="pointColor" format="color" /> </declare-styleable>

我把自定義屬性的連線寬度,作為了九宮格的實心圓的半徑和外面的空心圓的邊框?qū)挾取?/p>

貼代碼

package lzp.com.interestlibrary.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;

import lzp.com.interestlibrary.R;
import lzp.com.interestlibrary.view.bean.DrawPoint;

/**
 * Created by li.zhipeng on 16/11/17.
 * <p>
 * 九宮格解鎖View
 * <p>
 * 可以定制顏色和九宮格的數(shù)量
 */

public class DrawPointLineUnlockView extends View {

    /**
     * 手勢按下時的x坐標(biāo)
     */
    private float xDown = -1;

    /**
     * 手勢按下時的y坐標(biāo)
     */
    private float yDown = -1;
    /**
     * 手勢y移動時的x坐標(biāo)
     */
    private float xMove = -1;

    /**
     * 手勢移動時的y坐標(biāo)
     */
    private float yMove = -1;

    /**
     * 畫筆的寬度
     */
    private int lineWidth = 10;

    /**
     * 每一個九宮格的大小
     */
    private int pointRadius = 30;

    /**
     * 九宮格的橫向個數(shù)
     */
    private int columeCount = 3;

    /**
     * 九宮格的行數(shù)
     */
    private int rowCount = 3;

    /**
     * 九宮格的顏色
     */
    private int pointColor = Color.parseColor("#000000");

    /**
     * 畫筆
     */
    private Paint paint;

    /**
     * 保存所有的九宮格的點的數(shù)組
     */
    private ArrayList<DrawPoint> pointsList;

    /**
     * 已經(jīng)繪制的九宮格的點
     */
    private ArrayList<DrawPoint> drawPoints;

    /**
     * 進行判斷的點
     * <p>
     * 只創(chuàng)建一個,不返回的創(chuàng)建對象,節(jié)省內(nèi)存
     */
    private DrawPoint tempPoint;

    /**
     * 是否要進行繪制
     */
    private boolean needDraw;

    /**
     * 密碼
     * */
    private String passward;

    /**
     * 解鎖解鎖回調(diào)
     * */
    private OnDrawPointUnlockResultListener listener;

    public void setPassward(String passward){
        this.passward = passward;
    }

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

    public DrawPointLineUnlockView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 獲取自定義屬性的值
        TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.DrawPointLineUnlockView);
        // 畫筆的寬度
        lineWidth = typedArray.getDimensionPixelSize(R.styleable.DrawPointLineUnlockView_lineWidth, 10);
        // 每一個九宮格的大小
        pointRadius = typedArray.getDimensionPixelSize(R.styleable.DrawPointLineUnlockView_pointRadius, 30);
        // 九宮格的橫向個數(shù)
        columeCount = typedArray.getInt(R.styleable.DrawPointLineUnlockView_columns, 3);
        // 九宮格的列數(shù)
        rowCount = typedArray.getInt(R.styleable.DrawPointLineUnlockView_rows, 3);
        // 九宮格的行數(shù)
        pointColor = typedArray.getColor(R.styleable.DrawPointLineUnlockView_pointColor, Color.parseColor("#000000"));
        typedArray.recycle();

        // 防止View不進行繪制,執(zhí)行onDraw()方法
        setWillNotDraw(false);

        pointsList = new ArrayList<>();
        drawPoints = new ArrayList<>();
        tempPoint = new DrawPoint(-1, pointRadius, lineWidth);
        // 初始化畫筆
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(pointColor);
        paint.setStrokeWidth(lineWidth);
        paint.setPathEffect(new CornerPathEffect(5));
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                xDown = event.getX();
                yDown = event.getY();
                // 判斷是否這個點是九宮格的某一個點,否則不進行繪制
                tempPoint.x = xDown;
                tempPoint.y = yDown;
                int index = pointsList.indexOf(tempPoint);
                if (index != -1) {
                    needDraw = true;
                    DrawPoint point = pointsList.get(index);
                    drawPoints.add(point);
                    xDown = point.x;
                    yDown = point.y;
                } else {
                    needDraw = false;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (needDraw) {
                    xMove = event.getX();
                    yMove = event.getY();
                    // 判斷是否這個點是九宮格的某一個點,如果是的話,連接這個點
                    tempPoint.x = xMove;
                    tempPoint.y = yMove;
                    int moveIndex = pointsList.indexOf(tempPoint);
                    // 判斷這個點是否是九宮格的點  且 這個點并沒有被連接過
                    if (moveIndex != -1 && !drawPoints.contains(tempPoint)) {
                        DrawPoint point = pointsList.get(moveIndex);
                        drawPoints.add(point);
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                if (needDraw) {
                    xDown = -1;
                    yDown = -1;
                    xMove = -1;
                    yMove = -1;
                    // 監(jiān)聽回調(diào)
                    if (listener != null){
                        String unlockResult = returnPwd();
                        listener.onUnlock(unlockResult, unlockResult.equals(passward));
                    }
                    // 清除所有的連接點
                    drawPoints.clear();
                    // 重繪
                    invalidate();
                }
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 畫出所有的九宮格的點
        for (DrawPoint point : pointsList) {
            point.draw(canvas, paint);
        }
        // 如果正在解鎖且已經(jīng)劃過了某些九宮格,畫出他們之間的連線
        int size = drawPoints.size() - 1;
        if (needDraw && size >= 0) {
            // 只有一個起點,畫出第一個點和手指位置的連線
            if (size == 0) {
                DrawPoint point = drawPoints.get(0);
                canvas.drawLine(point.x, point.y, xMove, yMove, paint);
            }
            // 多個點,畫出點和點之間的連線
            else {
                for (int i = 0; i < size; i++) {
                    DrawPoint point = drawPoints.get(i);
                    DrawPoint nextPoint = drawPoints.get(i + 1);
                    canvas.drawLine(point.x, point.y, nextPoint.x, nextPoint.y, paint);
                }
                // 畫出最后一個點和當(dāng)前手指位置之間的連線
                DrawPoint point = drawPoints.get(size);
                canvas.drawLine(point.x, point.y, xMove, yMove, paint);
            }
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        // 測量當(dāng)前的寬高 來繪制指定個數(shù)的九宮格
        float width = getWidth() - pointRadius * 2;
        float height = getHeight() - pointRadius * 2;

        // 獲取padding值
        float paddingLeft = getPaddingLeft();
        float paddingRight = getPaddingRight();
        float paddingTop = getPaddingTop();
        float paddingBottom = getPaddingBottom();


        // 求出每個點的起始位置
        float xDistance = (width - paddingTop - paddingBottom) / (columeCount - 1);
        float yDistance = (height - paddingLeft - paddingRight) / (rowCount - 1);

        // 計算每一個九宮格的圓心
        for (int i = 0; i < rowCount; i++) {
            for (int j = 0; j < columeCount; j++) {
                DrawPoint pointF = new DrawPoint(j + rowCount * i, pointRadius, lineWidth);
                pointF.y = yDistance * i + pointRadius + paddingTop;
                pointF.x = xDistance * j + pointRadius + paddingLeft;
                pointsList.add(pointF);
            }
        }
    }

    /**
     * 返回密碼
     * */
    private String returnPwd(){
        StringBuilder builder = new StringBuilder();
        for (DrawPoint point : drawPoints){
            builder.append(point.getValue());
        }
        return builder.toString();
    }

    /**
     * 解鎖回調(diào)
     * */
    public interface OnDrawPointUnlockResultListener{
        /**
         *  @param passward 解鎖的結(jié)果密碼
         *  @param success 與設(shè)置的密碼進行匹配的結(jié)果
         * */
        void onUnlock(String passward, boolean success);
    }

    public void setOnDrawPointUnlockResultListener(OnDrawPointUnlockResultListener listener){
        this.listener = listener;
    }
}

注釋寫的都是很清楚,不用做過多的解釋,關(guān)于計算每一個九宮格的中心點,就畫圖說明一下


這里寫圖片描述

圖太難畫了 ,我們首先減去了九宮格的直徑,這樣均分了之后,我們就可以得到每一個九宮格的最左邊的點,然后用最左邊的點加上了半徑就得到了x方向的圓心。在y方向上同理。

貼出DrawPoint的代碼:

package lzp.com.interestlibrary.view.bean;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;

/**
 * Created by li.zhipeng on 16/11/18.
 * <p>
 * 九宮格的點
 */

public class DrawPoint extends PointF {
    /**
    * 外部的空心圓的半徑
    */
    private int pointRadius;

   /**
    * 內(nèi)部的小實心圓的半徑
    */
    private int centerPointRadius;

    /**
     * 這個點的值,用于解鎖密碼的匹配
     */
    private int value;

    public int getValue(){
        return value;
    }

    public DrawPoint(int value, int pointRadius, int centerPointRadius) {
        this.value = value;
        this.pointRadius = pointRadius;
        this.centerPointRadius = centerPointRadius;
    }

    public void draw(Canvas canvas, Paint paint) {
        // 畫中心的小實心圓
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(x, y, centerPointRadius, paint);
        // 畫外面的空心圓
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(x, y, pointRadius, paint);
    }

    /**
     * 重寫此方法,判斷是否包含這個點
     */
    @Override
    public boolean equals(Object o) {
        if (o instanceof DrawPoint) {
            // 匹配的誤差是 大圓的半徑
            DrawPoint point = (DrawPoint) o;
            if (Math.abs(this.x - point.x) < pointRadius
                    && Math.abs(this.y - point.y) < pointRadius) {
                return true;
            }
        }
        return false;
    }
}

MainActivity:

DrawPointLineUnlockView drawPointLineUnlockView = (DrawPointLineUnlockView) findViewById(R.id.lock);
        drawPointLineUnlockView.setOnDrawPointUnlockResultListener(new DrawPointLineUnlockView.OnDrawPointUnlockResultListener() {
            @Override
            public void onUnlock(String passward, boolean success) {
                Toast.makeText(MainActivity.this, passward, Toast.LENGTH_SHORT).show();
            }
        });

結(jié)尾##

如果有什么問題和好的建議歡迎大家留言批評指正,尤其是妹紙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,256評論 4 61
  • 師兄除了因為比我高一屆之外,沒有任何一點具備師兄的氣質(zhì)。師兄是一個往處女座演變的金牛座,襯衫T恤一定要分開掛...
    羅哥哥閱讀 549評論 6 5
  • 尊敬的黃總監(jiān),各位領(lǐng)導(dǎo)、朋友: 感謝你們在百忙之中關(guān)注青秀業(yè)主子女初中學(xué)位問題。經(jīng)摸底,青秀今年有小升初入學(xué)需求的...
    財魚閱讀 1,403評論 1 2

友情鏈接更多精彩內(nèi)容