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

2022-10-07,,,,

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

代码文件​​​​​​

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);
    }
}

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

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

《Android自定义控件实现九宫格解锁.doc》

下载本文的Word格式文档,以方便收藏与打印。