天天看點

android 自定義view實作九宮格手勢解鎖

剛才我使用銀行APP時,看見了這個效果,趁着下班時間,就寫了。

先看下效果圖:

android 自定義view實作九宮格手勢解鎖
android 自定義view實作九宮格手勢解鎖

我先說下我的思路:

1: 先計算每個點 XY坐标點,然後存儲在integerList中,然後畫九個點,此時我把它叫為:畫闆的原始狀态!

2:複寫onTouchEvent事件, 然後每次監聽ACTION_DOWN,ACTION_MOVE,ACTION_UP狀态;

  1. ACTION_DOWN:我們需要将畫闆至于原始狀态。
  2. ACTION_MOVE:我們需要計算周遊integerList的點,然後跟move點做比較,如果距離distance < radius , 就表明有選中了該點,此時我們把改點的下标放進indexList中;然後每一次都重新整理一次view。
  3. ACTION_UP:離開動作,就涉及到自己的業務邏輯了,在這我寫了一個回調,可以根據回調做相應的業務邏輯。對了,我寫的點不能小于4個,也就是indexList.size()>=4;

對了,integerList裡點的存儲的順序是:從左上角開始為第一個,然後,第二個……..第九個;

具體的看下圖:

android 自定義view實作九宮格手勢解鎖

具體的看下代碼:

package com.app.test.testlineproject;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by ${liumegnqiang} on 2017/6/28.
 */

public class LockLineView extends View {
    private float height;// view的高度
    private float wight;
    private float radius ;//圓的半徑
    private float companyWidth;//因為是三個點,是以将width分為三分 也就是 companyWidth = width / 3;
    private float companyHeight;

    private List<PaintBean> integerList = new ArrayList<>();//九個點的坐标
    private  List<Integer> indexList = new ArrayList<>();//連接配接的點(不重複)

    private float moveX;//每次移動的X坐标
    private float moveY;//每次移動的Y坐标

    private OnLessPaintListener onLessPaintListener;

    private boolean isUP = false;//判斷是否是UP狀态,是的話,就去除多餘的連接配接 , 隻連接配接點與點

    private int widthLine = ;//線的寬度

    //R.color.line 可以自定義color
    private int selectColor = ContextCompat.getColor(getContext(),R.color.line);//圓選中的顔色
    private int unSelectColor = Color.GRAY;//圓未選中的顔色
    private int lineColor = ContextCompat.getColor(getContext(),R.color.line);//線的顔色

    public LockLineView(Context context) {
        super(context);
    }

    public LockLineView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public LockLineView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        height = getMeasuredHeight();
        wight = getMeasuredWidth();
        /**
         * 這三個數值的計算比例,可根據自己的需求, 更改!!!
         */
        companyWidth = wight / ;
        companyHeight = height / ;
        radius = wight / ;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        /**
         * 畫九宮格
         */
        paint.setColor(unSelectColor);//原點的顔色
        for(int i = ;i < ;i++){
            for(int j = ;j <  ;j++ ){
                RectF rectF = new RectF();
                rectF.left = j * companyWidth + companyWidth /  - radius;
                rectF.right = j * companyWidth + companyWidth /  + radius;
                rectF.top = i * companyHeight + companyHeight /  - radius;
                rectF.bottom = i * companyHeight + companyHeight /  + radius;
                integerList.add(new PaintBean(j * companyWidth + companyWidth / , i * companyHeight + companyHeight / ));
                canvas.drawOval(rectF, paint);
            }
        }

        /**
         * 畫連接配接的線
         */
        if(indexList.size() == ){
            return;
        }
        paint.setColor(lineColor);//線的顔色
        paint.setStrokeWidth(widthLine);//線的寬度
        for(int i = ;i < indexList.size();i++){
            canvas.drawLine(integerList.get(indexList.get(i - )).getPaintX(), integerList.get(indexList.get(i - )).getPaintY(),
                    integerList.get(indexList.get(i)).getPaintX(),integerList.get(indexList.get(i)).getPaintY(), paint);

        }
        if(!isUP){
            canvas.drawLine(integerList.get(indexList.get(indexList.size() - )).getPaintX(), integerList.get(indexList.get(indexList.size() - )).getPaintY(),
                    moveX, moveY, paint);
        }
        /**
         * 畫選中的點
         */
        paint.setColor(selectColor);//選中圓的顔色
        for(int i = ;i < indexList.size();i++){
            PaintBean paintBean = integerList.get(indexList.get(i));
            RectF rectF = new RectF();
            rectF.left = paintBean.getPaintX() - radius;
            rectF.right = paintBean.getPaintX() + radius;
            rectF.top = paintBean.getPaintY() - radius;
            rectF.bottom = paintBean.getPaintY() + radius;
            canvas.drawOval(rectF, paint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:{
                Message message = new Message();
                message.what = ;
                handler.sendMessage(message);
                break;
            }
            case MotionEvent.ACTION_MOVE:{
                moveX = event.getX();
                moveY = event.getY();
                for(int i = ;i < integerList.size();i++){
                    PaintBean paintBean = integerList.get(i);
                    float distance = (float)Math.sqrt((paintBean.getPaintX()-moveX)  * (paintBean.getPaintX()-moveX)
                            + (paintBean.getPaintY() - moveY) * (paintBean.getPaintY() - moveY));
                    /**
                     * 有一個問題:
                     * 就是當你測試時,可以中間隔一個原點,
                     * 也就是該原點沒有被選中,這時你可以修改該下面的判斷,進行修改;
                     * 當然 你感覺沒問題的話 就不用改了。。。
                     */
                    if(distance < radius){
                        if(!indexList.contains(i)){
                            indexList.add(i);
                        }
                    }
                }
                Message message = new Message();
                message.what = ;
                handler.sendMessage(message);
                break;
            }
            case MotionEvent.ACTION_UP:{
                Message message = new Message();
                message.what = ;
                handler.sendMessage(message);
                break;
            }
        }
        return true;
    }
    private Handler handler  = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case :{
                    isUP = false;
                    integerList.clear();
                    invalidate();
                    break;
                }
                case :{
                    isUP = true;
                    if(indexList.size() < ){
                        /**
                         * TODO  連點數少于四個時,業務邏輯處理
                         */
                        Toast.makeText(getContext(),"連點數不能少于4個", Toast.LENGTH_SHORT).show();
                        if(onLessPaintListener != null){
                            onLessPaintListener.onLessPaintListener();//少于四個時,回調給view層
                        }
                        integerList.clear();
                        indexList.clear();
                    }else{
                        /**
                         * TODO  連點數大于四個時,業務邏輯處理
                         */
                    }
                    invalidate();
                    break;
                }
                case :{
                    isUP = false;
                    integerList.clear();
                    indexList.clear();
                    invalidate();
                    break;
                }
            }
        }
    };

    /**
     * 在view層設定參數之後(比如:setWidthLine(4)),可調用此方法,重新整理LockLineView;
     */
    public void setPaint(){
        postInvalidate();
    }

    /**
     * 少于四個點數時,回調
     */
    interface OnLessPaintListener{
        void onLessPaintListener();
    }

    public OnLessPaintListener getOnLessPaintListener() {
        return onLessPaintListener;
    }

    public void setOnLessPaintListener(OnLessPaintListener onLessPaintListener) {
        this.onLessPaintListener = onLessPaintListener;
    }

    public float getRadius() {
        return radius;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }

    public int getWidthLine() {
        return widthLine;
    }

    public void setWidthLine(int widthLine) {
        this.widthLine = widthLine;
    }

    public int getLineColor() {
        return lineColor;
    }

    public void setLineColor(int lineColor) {
        this.lineColor = lineColor;
    }

    public int getUnSelectColor() {
        return unSelectColor;
    }

    public void setUnSelectColor(int unSelectColor) {
        this.unSelectColor = unSelectColor;
    }

    public int getSelectColor() {
        return selectColor;
    }

    public void setSelectColor(int selectColor) {
        this.selectColor = selectColor;
    }
}
           

有的小夥伴可能會問:為什麼不用postInvalidate()呢?

其實我剛開始用的就是這個方法,但是我發現滑動的move和重新整理view不同步,是以就用了invalidate(),自己控制重新整理view。

至于顔色的,可以通過相應的get方法進行設定。對了 這個view的寬高,需要自己去調試!

最後附上源碼:

http://download.csdn.net/detail/lmq121210/9884371

繼續閱讀