Android自定义控件实现九宫格解锁

关于九宫格解锁,我看了不少博客,但是都感觉很复杂,可能我的功夫还不到,所以很多东西我不了解,但是我还是打算写一个自己的九宫格。我相信我的九宫格大家都能很快的理解,当然如果需要实现更复杂的功能,需要大家自己接着往深了挖掘。

代码文件​​​​​​

NineGroupView:为九宫格空间组

ToggleView:九宫格中的子View,也就是我们看到的圆形按钮,我自己定义的ToggleView可能不好看,当然大家可以自己定义更加好看的ToggleView。

MarkBean:记录ToggleView的索引(ChildIndex)以及是否选中的状态

PositionUtils:工具类,包含规划九个ToggleView的中心点位置,判断当前触摸点是否属于ToggleView中等方法。

NineActivity:测试页面。

布局规划图

public class PositionUtils {
    /**
     * 判断触摸的点是否属于View中的一点
     *
     * @param point    触摸的点
     * @param position 目标对象圆形坐标
     * @param outR     目标对象的外半径
     * @return
     */
    public static boolean IsIn(Point point, Point position, int outR) {
        int touchX = point.x;
        int touchY = point.y;
 
        int cx = position.x;
        int cy = position.y;
 
        int distance = (int) Math.sqrt(Math.pow((touchX - cx), 2) + Math.pow((touchY - cy), 2));
        if (distance <= outR) {
            return true;
        } else {
            return false;
        }
    }
 
    /**
     * 规划 child 的中心位置
     *
     * @param width
     * @param height
     * @return
     */
    public static List<Point> getNinePoints(int width, int height) {
        List<Point> points = new ArrayList<>();
        for (int i = 1; i <= 3; i++) {
            for (int j = 1; j <= 3; j++) {
                points.add(getPoint(width, height, 0.25f * j, 0.2f * i + 0.1f));
            }
        }
        return points;
    }
 
    /**
     * 获取
     *
     * @param width  父控件的宽
     * @param height 父控件的高
     * @param x      横轴方向比例
     * @param y      纵轴方向的比例
     * @return
     */
    private static Point getPoint(int width, int height, float x, float y) {
        Point point = new Point();
        point.x = (int) (width * x);
        point.y = (int) (height * y);
        return point;
    }
 
}
public class ToggleView extends View {
 
    private Paint inPaint;
    private Paint outPaint;
    private int outColor;
    private int inColor;
    private int outR;
    private int inR;
    private boolean isChecked;
 
    public int getOutColor() {
        return outColor;
    }
 
    public void setOutColor(int outColor) {
        this.outColor = outColor;
    }
 
    public int getInColor() {
        return inColor;
    }
 
    public void setInColor(int inColor) {
        this.inColor = inColor;
    }
 
    public int getOutR() {
        return outR;
    }
 
    public void setOutR(int outR) {
        this.outR = outR;
    }
 
    public int getInR() {
        return inR;
    }
 
    public void setInR(int inR) {
        this.inR = inR;
    }
 
    public boolean isChecked() {
        return isChecked;
    }
 
    public void setChecked(boolean checked) {
        isChecked = checked;
    }
 
    public ToggleView(Context context) {
        this(context, null);
    }
 
    public ToggleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
 
    public ToggleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }
 
    /**
     * 初始化
     */
    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ToggleView, defStyleAttr, 0);
        int indexCount = array.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.ToggleView_InCircleR_T:
                    inR = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(Dimension.DP, 10, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.ToggleView_OutCircleR_T:
                    outR = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(Dimension.DP, 50, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.ToggleView_InCircleColor_T:
                    inColor = array.getColor(attr, 0xff00ffff);
                    break;
                case R.styleable.ToggleView_OutCircleColor_T:
                    outColor = array.getColor(attr, 0xff888888);
                    break;
            }
        }
        inPaint = new Paint();
        inPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        inPaint.setColor(inColor);
        inPaint.setAntiAlias(true);
 
        outPaint = new Paint();
        outPaint.setAntiAlias(true);
        outPaint.setStrokeWidth(5);
        outPaint.setStyle(Paint.Style.STROKE);
    }
 
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int cx = getWidth() / 2;
        int cy = getHeight() / 2;
        outPaint.setStyle(Paint.Style.FILL);
        outPaint.setColor(Color.WHITE);
        canvas.drawCircle(cx, cy, outR, outPaint);
        outPaint.setStyle(Paint.Style.STROKE);
        outPaint.setColor(outColor);
        canvas.drawCircle(cx, cy, outR, outPaint);
        canvas.drawCircle(cx, cy, inR, inPaint);
    }
}
public class NineGroupView extends ViewGroup {
 
    private OnFinishListener mListener;
 
    public interface OnFinishListener {
 
        public void onFinish(List<Integer> positionSet);
    }
 
    public void setOnFinishListener(OnFinishListener listener) {
        this.mListener = listener;
    }
 
    private Paint paint;
    private Path path;
    private TreeMap<Integer, Boolean> checkedMap;
    private List<Integer> checkedINdexSet;   //用于记录被选中的序号排列。
    private List<Point> positionList;
 
    private List<Point> childSize = new ArrayList<>();
 
    public NineGroupView(Context context) {
        this(context, null);
    }
 
    public NineGroupView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
 
    public NineGroupView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
 
    public void reset() {
        for (int i = 0; i < 9; i++) {
            checkedMap.put(i, false);
        }
        checkedINdexSet.clear();
        IsDownIn = false;
        IsUp = false;
        path.reset();
        prePoint = new Point(-1, -1);
        currentPoint = new Point(-1, -1);
        invalidate();
    }
 
    private void init() {
        checkedMap = new TreeMap<>();
        for (int i = 0; i < 9; i++) {
            checkedMap.put(i, false);
        }
        checkedINdexSet = new ArrayList<>();
        positionList = new ArrayList<>();
        path = new Path();
        paint = new Paint();
        paint.setStrokeWidth(10);
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.STROKE);
        //如果该方法在此不调用的话,那么onDraw()方法将不被调用,那么就无法完成连接线的绘制
        setWillNotDraw(false);
    }
 
    @Override
    protected void onLayout(boolean b, int left, int top, int right, int bottom) {
        int height = getMeasuredHeight();
        int width = getMeasuredWidth();
        positionList = PositionUtils.getNinePoints(width, height);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            Point size = childSize.get(i);
            Point position = positionList.get(i);
            int cLeft = position.x - size.x;
            int cTop = position.y - size.y;
            int cRight = position.x + size.x;
            int cBottom = position.y + size.y;
            child.layout(cLeft, cTop, cRight, cBottom);
        }
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            Point point = new Point();
            point.x = child.getMeasuredWidth();
            point.y = child.getMeasuredHeight();
            childSize.add(point);
        }
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
    }
 
    private boolean IsDownIn = false;
    private boolean IsUp = false;
 
    private Point prePoint = new Point(-1, -1);
    private Point currentPoint = new Point(-1, -1);
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        int currentX = (int) event.getX();
        int currentY = (int) event.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                if (IsUp) {
                    return true;
                }
                MarkBean bean = isInToggle(new Point(currentX, currentY));
                if (bean != null) {
                    IsDownIn = true;
                    prePoint = positionList.get(bean.getIndex());
                    path.moveTo(prePoint.x, prePoint.y);
                    invalidate();
                }
            }
            break;
            case MotionEvent.ACTION_UP:
                IsUp = true;
                if (IsDownIn) {
                    currentPoint = prePoint;
                    IsDownIn = false;
                    invalidate();
                    if (mListener != null) {
                        mListener.onFinish(checkedINdexSet);
                        reset();
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE: {
                if (IsDownIn) {
                    if (!IsUp) {
                        MarkBean bean = isInToggle(new Point(currentX, currentY));
                        if (bean != null) {
                            int index = bean.getIndex();
                            currentPoint = positionList.get(index);
                            path.lineTo(currentPoint.x, currentPoint.y);
                            invalidate();
                            prePoint = currentPoint;
                        } else {
                            currentPoint = new Point(currentX, currentY);
                            invalidate();
                        }
                    }
                } else {
                    if (!IsUp) {
                        MarkBean bean = isInToggle(new Point(currentX, currentY));
                        if (bean != null) {
                            Point position = positionList.get(bean.getIndex());
                            prePoint = position;
                            path.moveTo(position.x, position.y);
                            IsDownIn = true;
                            invalidate();
                        }
                    }
                }
            }
            break;
            case MotionEvent.ACTION_CANCEL:
 
                break;
        }
        return true;
    }
 
    private MarkBean isInToggle(Point point) {
        MarkBean bean = new MarkBean();
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            Point position = positionList.get(i);
            ToggleView child = (ToggleView) getChildAt(i);
            if (PositionUtils.IsIn(point, position, child.getOutR())) {
                if (!checkedMap.get(i)) {
                    checkedMap.put(i, true);
                    checkedINdexSet.add(i);
                    bean.setIndex(i);
                    bean.setCheck(true);
                    return bean;
                }
            }
        }
        return null;
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawPath(path, paint);
        if (prePoint.x != -1 && prePoint.y != -1 && currentPoint.x != -1 && currentPoint.y != -1) {
            canvas.drawLine(prePoint.x, prePoint.y, currentPoint.x, currentPoint.y, paint);
        }
        super.onDraw(canvas);
    }
}

代码总是最直接的引导,我看博客最喜欢的是研究代码,当然如果代码中有一些讲解就更好了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android实现九宫格解锁的方法

    相信大家都有使用九宫格解锁,比如在设置手机安全项目中,可以使用九宫格解锁,提高安全性,以及在使用支付功能的时候,为了提高安全使用九宫锁,今天就为大家介绍Android实现九宫格的方法,分享给大家供大家参考.具体如下: 运行效果截图如下: 具体代码如下: 布局文件如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas

  • Android实现九宫格手势解锁

    本文为大家分享了Android九宫格手势解锁的具体代码,供大家参考,具体内容如下 这里是使用的开源库GestureLibray 里面有关于这个东西的介绍和接入方式,这里就不累赘了,我只是说下里面没有的. 关于这个库的使用: protected void initViews() { //设置模式 LockMode lockMode = (LockMode) getIntent().getSerializableExtra(Config.INTENT_SECONDACTIVITY_KEY); //是

  • Android自定义View九宫格手势密码解锁

    由于公司新的项目需要用到九宫格手势密码解锁的功能,于是觉得自己写一个.废话不多说,直接上效果图: 首选我们来分析下实现的思路: 1. 绘制出相对于这个View的居中的九个点,作为默认状态的点 2. 点击屏幕的时候是否点击在这九个点上 3. 在屏幕上滑动的时候,绘制两个点之间的线条,以及选中状态的点 4. 手指离开屏幕的时候判断手势密码是否正确,如若错误这把错误状态下的点和线绘制出来. 具体实现: 首先我们得绘制出默认正常状态下的九个点: /** * 点的bean * Created by Adm

  • android 九宫格滑动解锁开机实例源码学习

    效果图由于网站占时不能上传,以后补上. NinePointLineView.java 复制代码 代码如下: package org.demo.custon_view; import org.demo.utils.MLog; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; imp

  • 使用Android自定义控件实现滑动解锁九宫格

    本文概述:  滑动解锁九宫格的分析: 1.需要自定义控件: 2.需要重写事件onTouchEvent(); 3.需要给九个点设置序号和坐标,这里用Map类就行: 4.需要判断是否到滑到过九点之一,并存储滑到过的点的序号,而且需要一个方法可以返回它们,这里用List类就行: 滑动解锁当前还是比较流行的,今天写了个简单的滑动解锁九宫格的例程,分享出来让初学者看看. 我的是这样的: Demo 首先,自定义一个View /** * 九宫格 */ public class NineGridView ext

  • Android自定义控件实现九宫格解锁功能

    最终Android九宫格解锁效果如下 1.进行定义实体point点 public class Point { private float x; private float y; //正常模式 public static final int NORMAL_MODE = 1; //按下模式 public static final int PRESSED_MODE = 2; //错误模式 public static final int ERROR_MODE = 3; private int state

  • 轻松实现Android自定义九宫格图案解锁

    Android实现九宫格图案解锁,自带将图案转化成数字密码的功能,代码如下: LockPatternView.java package com.jackie.lockpattern; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Point; import android.text.TextUtils; i

  • Android实现九宫格解锁的实例代码

    当年感觉九宫格解锁很是高大上,一脸懵逼,今天正好要做解锁这一块业务,回头来看九宫格,这特么简单啊 首先理清一下逻辑,我们要做NxN的九宫格 下图是3x3的简单图例 // -(--)-(--)-(--)- // -(--)-(--)-(--)- // -(--)-(--)-(--)- 我们就把九宫格分解成 外圆 .内圆.连线三部分 外圆半径Radius,内圆半径dp(5) 建立一个集合来放置 外圆的圆心( 内圆的圆心也一样) private ArrayList<Point> mListCircl

  • 轻松实现安卓(Android)九宫格解锁

    效果图 思路 首先我们来分析一下实现九宫格解锁的思路:当用户的手指触摸到某一个点时,先判断该点是否在九宫格的某一格范围之内,若在范围内,则该格变成选中的状态:之后用户手指滑动的时候,以该格的圆心为中心,用户手指为终点,两点连线.最后当用户手指抬起时,判断划过的九宫格密码是否和原先的密码匹配. 大致的思路流程就是上面这样的了,下面我们可以来实践一下. Point 类 我们先来创建一个 Point 类,用来表示九宫格锁的九个格子.除了坐标 x ,y 之外,还有三种模式:正常模式.按下模式和错误模式.

  • Android 仿小米锁屏实现九宫格解锁功能(无需图片资源)

    最近公司要求做个九宫格解锁,本人用的是小米手机,看着他那个设置锁屏九宫格很好看,就做了该组件,不使用图片资源,纯代码实现. 尊重每个辛苦的博主,在http://blog.csdn.net/mu399/article/details/38734449的基础上进行修改 效果图: 关键代码类: MathUtil.Java /** * @author SoBan * @create 2016/12/5 15:52. */ public class MathUtil { public static dou

随机推荐