Android 模仿华为健康 自定义计步View

作者: 杨体仁 | 来源:发表于2017-09-27 17:12 被阅读282次

    通过一个简单的自定义的View,模仿华为健康里面消耗热量和计步的圆弧View,来深入的理解RectF以及 canvas的drawArc方法。

    华为健康的View是这样的:

    最近写了一些自定义的view,其中用到RectF,Rect的地方比较多,于是有了这篇文章,来深入理解下RectF对象。先上效果图:

    效果还是有点不太一样,但是别太在意那些细节了。。。

    闲言少叙,分析下这个简单的View,可以看到是由两个圆弧和三段文字组成的,这两个圆弧,扫过的区域都是180度,两个圆弧之间需要留有间隙。
    而要实现画一段圆弧,其实只需要两个步骤:

    1:算出圆弧中线的一个矩形的方阵,这个地方就需要用到RectF了,为什么说是圆弧中线呢,因为圆弧是由宽度的,但是在绘制的时候,事实上是以圆弧最中间的那条线为基础画的,如果画360度的话,刚好是这个正方形的内切圆。

    2:通过canvas的drawArc方法,就可以了。
    这个oval 参数,就是我们 第一步得到的,圆弧的外切正方形,startAngle是开始的角度,需要注意的是,这个地方开始的0度是几何里面的180度,也就是中心点右边开始计数。

    sweepAngle,就是圆弧横扫的角度,很明显,我们的view都是180度。

    useCenter,是否包含中心点,我们这里明显不需要

    paint 就是圆弧的画笔了

    public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) {
    throw new RuntimeException("Stub!");
    }

    分析完成之后,就开始动手操作了,

    //默认开始角度
    private int startAngle = 180;
    
    //默认扫过的弧度
    private int defaultSweepAngle = 180;
    
       //中心点坐标
        float centerX = getWidth() / 2;
        //热量外矩形区域
        RectF hotRectF = new RectF();
        float hotL = hotStrokeWidth / 2;
        float hotT = hotStrokeWidth / 2;
        float hotR = centerX * 2 - hotStrokeWidth / 2;
        float hotB = hotR;
        hotRectF.set(hotL, hotT, hotR, hotB);
    
    
    //默认的灰色区域
      private void drawDefaultHotStroke(Canvas canvas, RectF f, float strokeWidth) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(defaultStrokeColor);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(strokeWidth);
        canvas.drawArc(f, startAngle, defaultSweepAngle, false, paint);
    }
    //当前进度的圆弧
    private void drawProgressHotStroke(Canvas canvas, RectF f) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(hotStrokeProgressColor);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(hotStrokeWidth);
        canvas.drawArc(f, startAngle, currentHotLength, false, paint);
    }
    

    不知道大家有没有注意到计算外切正方形的时候,减去了画笔的宽度的一般,这一点就是上面提到的,绘制弧线的基准是以最中间那条线为准的,如果不减去的话,你会发现,圆弧的上下左右四个点会超出这个view之外。

    绘制下面的圆弧的原理和这个一样,就不再赘述了,现在来分析下RectF

        RectF (float left, 
                float top, 
                float right, 
                float bottom)
    

    这是RectF的构造函数,官网的解释是代表左上右下四个方向的坐标,但是其实这样解释并不准确,根据自己的实验,我们可以这样理解,前两个参数是矩阵左上角点的坐标,后两个是右下角点的坐标,这样,一个矩形就出来了。需要注意的是,要保证left <= right , top <= bottom.嗯,差不多,还是很好理解的。

    那么 RectF 和 Rect 的区别是什么呢?
    Rect的坐标点为int值,所以精度就没有RectF高了,RectF的构造函数可以直接把 RectF或者Rect传进去构建矩阵。其它的方法,都基本类似。

    绘制文字的时候需要注意两个地方,文字的Y坐标,需要加上自身的高度的一半,避免出现覆盖的情况,这个地方使用了Paint的一个方法 getTextBounds 事实上把文字的属性都给 Rect 对象了,这样就可以拿到文字的所有想要的属性,而我们只需要传递文字内容和长度就可以了。

     private void drawText(Canvas canvas, float centerX) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        paint.setTextSize(stepTextSize);
        paint.setTextAlign(Paint.Align.CENTER);
        String textTop = "目标" + " " + targetStepNum;
        Rect textF = new Rect();
        paint.getTextBounds(textTop, 0, textTop.length(), textF);
        //文字高度
        stepTextHeight = textF.height();
        float textY = textF.height() / 2 + hotStrokeWidth / 2 + stepStrokeWidth + 2 * stokeOffset;
        canvas.drawText(textTop, centerX, textY, paint);
    }
    

    至此,这个简单的自定义View 已经写完了,之所以写这篇文章,是因为两点有猫腻的地方,一个是RectF的坐标点的定义,还有一个是canvas的drawArc 方法。现在只是个简单的view,并没有实现计步的功能。后续再完善。

    源码传送门:
    https://github.com/yangpin/ViewDemo

    相关文章

      网友评论

      • AWeiLoveAndroid:学习了,原来自定义View 这么好玩 后期 如果加一个进度变化的动画 就更完美了
        杨体仁:这个也很好实现哒,用一个属性动画就可以啦

      本文标题:Android 模仿华为健康 自定义计步View

      本文链接:https://www.haomeiwen.com/subject/geguextx.html