android 自定义view实现九宫格手势解锁

刚才我使用银行APP时,看见了这个效果,趁着下班时间,就写了。
先看下效果图:
android 自定义view实现九宫格手势解锁_第1张图片
android 自定义view实现九宫格手势解锁_第2张图片
我先说下我的思路:
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实现九宫格手势解锁_第3张图片

具体的看下代码:

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 integerList = new ArrayList<>();//九个点的坐标
    private  List indexList = new ArrayList<>();//连接的点(不重复)

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

    private OnLessPaintListener onLessPaintListener;

    private boolean isUP = false;//判断是否是UP状态,是的话,就去除多余的连接 , 只连接点与点

    private int widthLine = 4;//线的宽度

    //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 / 3;
        companyHeight = height / 3;
        radius = wight / 16;
    }

    @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 = 0;i < 3;i++){
            for(int j = 0;j < 3 ;j++ ){
                RectF rectF = new RectF();
                rectF.left = j * companyWidth + companyWidth / 2 - radius;
                rectF.right = j * companyWidth + companyWidth / 2 + radius;
                rectF.top = i * companyHeight + companyHeight / 2 - radius;
                rectF.bottom = i * companyHeight + companyHeight / 2 + radius;
                integerList.add(new PaintBean(j * companyWidth + companyWidth / 2, i * companyHeight + companyHeight / 2));
                canvas.drawOval(rectF, paint);
            }
        }

        /**
         * 画连接的线
         */
        if(indexList.size() == 0){
            return;
        }
        paint.setColor(lineColor);//线的颜色
        paint.setStrokeWidth(widthLine);//线的宽度
        for(int i = 1;i < indexList.size();i++){
            canvas.drawLine(integerList.get(indexList.get(i - 1)).getPaintX(), integerList.get(indexList.get(i - 1)).getPaintY(),
                    integerList.get(indexList.get(i)).getPaintX(),integerList.get(indexList.get(i)).getPaintY(), paint);

        }
        if(!isUP){
            canvas.drawLine(integerList.get(indexList.get(indexList.size() - 1)).getPaintX(), integerList.get(indexList.get(indexList.size() - 1)).getPaintY(),
                    moveX, moveY, paint);
        }
        /**
         * 画选中的点
         */
        paint.setColor(selectColor);//选中圆的颜色
        for(int i = 0;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 = 3;
                handler.sendMessage(message);
                break;
            }
            case MotionEvent.ACTION_MOVE:{
                moveX = event.getX();
                moveY = event.getY();
                for(int i = 0;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 = 1;
                handler.sendMessage(message);
                break;
            }
            case MotionEvent.ACTION_UP:{
                Message message = new Message();
                message.what = 2;
                handler.sendMessage(message);
                break;
            }
        }
        return true;
    }
    private Handler handler  = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:{
                    isUP = false;
                    integerList.clear();
                    invalidate();
                    break;
                }
                case 2:{
                    isUP = true;
                    if(indexList.size() < 4){
                        /**
                         * TODO  连点数少于四个时,业务逻辑处理
                         */
                        Toast.makeText(getContext(),"连点数不能少于4个", Toast.LENGTH_SHORT).show();
                        if(onLessPaintListener != null){
                            onLessPaintListener.onLessPaintListener();//少于四个时,回调给view层
                        }
                        integerList.clear();
                        indexList.clear();
                    }else{
                        /**
                         * TODO  连点数大于四个时,业务逻辑处理
                         */
                    }
                    invalidate();
                    break;
                }
                case 3:{
                    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

你可能感兴趣的:(android开发,自定义view)