自定义一个炫酷、可触控的仪表盘

话不多说,先上图看个明了

效果

在提取知识点之前,先对这个控件做一下简要说明:

       这个控件主要是可以通过滑动射线区域和圆弧区域来修改中间的值大小,可以指定偏低/正常/偏高三个范围的值大小以及对应范围的颜色.

首先申明几点,免得耽误大家时间:

       1.这篇文章不详解这个控件自定义的详细流程,想要看源码的朋友可以到敝人的githug库ScrollSetValueCirclePb里去拉,源码里的注释也很详细的,如果能帮助到你的话,还劳烦各位给个星星鼓励一下下.

       2.也不是从整体上讲Android怎么自定义一个控件,如果对Android自定义控件还不是很熟悉的朋友可以看看这篇文章AndroidNote,写的很棒,当初敝人就是从那里深入自定义控件的啊,在此感谢作者的无私分享.

       3.本文就是纯粹提取敝人在写这个控件时所涉及到的技术点,可以看看下面的知识点有没有朋友你需要的:

1.使用DrawFilter去除绘制锯齿

//从canvas层面去除绘制锯齿
DrawFilter mDrawFilter= new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
canvas.setDrawFilter(mDrawFilter);
//去除画笔锯齿
Paint mLinePaint= new Paint();
mLinePaint.setAntiAlias(true);

2.使用Xfermode指定重叠绘制时的显示效果

Paint mLinePaint= new Paint();
mLinePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
       关于Xfermode的使用,大家可以看这篇文章http://blog.youkuaiyun.com/t12x3456/article/details/10432935

3.画外围射线

       这里的难点是如何计算射线的始/终点坐标,这个控件是每过10度画一条射线,总共360度,那就有36条射线.这里是根据射线所在的角度计算对应的弧度,然后再根据弧度在这条射线上截取对应的起始点.需要注意的是我这里是翻转了Y坐标了的哈,因为下面用到的计算触摸点所在的坐标象限的函数只支持Y轴向上的条件.
//画默认射线
        for (float i = 0; i < 360; i += 10) {
            double rad = i * PI / 180;
            //射线内侧起点
            float startX = (float) (((mRayOutRadius - 35) - circleWidth) *Math.sin(rad));
            float startY = -(float) (((mRayOutRadius - 35) - circleWidth) * Math.cos(rad));
            //射线外侧终点,所以射线长度为 35px
            float stopX = (float) (mRayOutRadius * Math.sin(rad) + 1);
            float stopY = -(float) (mRayOutRadius * Math.cos(rad) + 1);
            canvas.drawLine(startX, startY, stopX, stopY, mLinePaint);
 }

4.通过RegionPath限制控件的可滑动范围

       上面说了,这个控件只可以滑动射线末端到圆弧之间的区域,滑动其他区域是不会响应这个触摸事件的.所以需要我们在前面画外侧射线的时候就得用Region记录下射线扫过的路径(Path)的所有点的坐标,在onTouchEvent方法里判断我们的触摸点的坐标是否包含在这个Region里面,如果包含我们才做对应的逻辑,所以在前面的代码中加入这部分逻辑的代码就是:
        Path rayInnerPath = new Path();
        Path rayOuterPath = new Path();
        //射线区域内侧的点坐标集合
        mRayInnerRegion = new Region();
        //射线区域外侧的点坐标集合
        mRayOuterRegion = new Region();
        //整个控件区域内的所有点坐标集合
        Region viewRegion = new Region(-mRayOutRadius, -mRayOutRadius, mRayOutRadius,mRayOutRadius);
        //画默认射线
        for (float i = 0; i < 360; i += 10) {
            double rad = i * PI / 180;
            //射线内侧起点
            float startX = (float) (((mRayOutRadius - 35) - circleWidth) *Math.sin(rad));
            float startY = -(float) (((mRayOutRadius - 35) - circleWidth) *Math.cos(rad));
            //射线外侧终点,所以射线长度为 35px
            float stopX = (float) (mRayOutRadius * Math.sin(rad) + 1);
            float stopY = -(float) (mRayOutRadius * Math.cos(rad) + 1);

            //取的是射线区域内侧150px的区域的所有点坐标
            rayInnerPath.addCircle(0, 0, mRayOutRadius - 150, Path.Direction.CW);
            mRayInnerRegion.setPath(rayInnerPath, viewRegion);
            //取的是射线区域外侧50px的区域的所有点坐标
            rayOuterPath.addCircle(0, 0, mRayOutRadius + 50, Path.Direction.CW);
            mRayOuterRegion.setPath(rayOuterPath, viewRegion);

            canvas.drawLine(startX, startY, stopX, stopY, mLinePaint);
        }
处理触摸事件:
@Override
    public boolean onTouchEvent(MotionEvent event) {

        //由于画布原点移到了控件中心,所以要矫正触点坐标
        int eventX = (int) (event.getX() - mCentreX);
        int eventY = (int) (mCentreY - event.getY());

        if (mRayOuterRegion != null && mRayInnerRegion != null) {
            //只有当触摸在限制的射线区域内才处理滑动事件
            if (mRayOuterRegion.contains(eventX, eventY) && !mRayInnerRegion.contains(eventX,eventY)) {
                Point point = new Point(eventX, eventY);
                //获取这个触摸点在控件上的角度,根据这个角度绘制值射线和值圆弧
                mRadianByPos = GetRadianByPos(point);
                invalidate();
                if (mValueChangeListener != null) {                 
//将当前值传递出去                    mValueChangeListener.currentValue(mRadianByPos / mUnitScale);
                }
            }
            return true;
        }
        return false;
    }

画值射线和值圆弧我就不多说, 原理跟画外侧射线一样的.
某些地方还有待优化的请各位多多指正,可以评论也可以发邮件给我:xwi_it@163.com谢谢!

最后附上源码地址:

https://github.com/jackBear168/ScrollSetValueCirclePb

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值