自定义控件之圆形统计图表

今天来做一做自定义的圆形统计图表,其实这个老早就做好了,只是今天项目不赶,我就把这个发出来。 先看效果图:

效果图

思路篇:

其实这个仔细想想,就是利用多个颜色不同的圆弧组合起来的一个圆, 所以,我们调用的时候,应该就是传入一个List,类里面就是值,和相应的颜色:

class Element(@ColorInt val color: Int, val progress: Float)

我们这里自定义一个类,构造参数,第一个为颜色,第二个则为对应的值,

我们在用canvas画的时候,会去遍历这个List,然后算出总值,再去自动算出当前的比例,在用当前element的颜色去画出来,关键代码如下:

override fun onDraw(canvas: Canvas?)
{
        var angle = startAngle//起始画的角度
        paint.strokeWidth = strokeWidth;//画笔的宽度
        var allValue = 0f//list里所有值得总数
        multiElement.all { entry -> //这里把所有遍历,拿到总数
            allValue += entry.value
            true
        }
        for (entry in multiElement)
        {
            val sweepAngle = (entry.value.toFloat() / allValue) * 360//这里算出当前值除以总和,再乘以360度,得出本次item所占的角度
            paint.color = entry.key//设置颜色
            canvas?.drawArc(rectf, angle, sweepAngle, false, paint)//开始画
            angle += sweepAngle//下一个画的开始,则是这次的结尾,因为需要衔接起来画一个圆
        }
        super.onDraw(canvas)
}

这里有一个需要注意的地方,就是因为画出的是一个弧度,所以由于画笔的宽度,画出来的会有一半被控件宽度遮住,所以为了完整的显示效果,在计算画图的区域的时候,需要把画笔的宽度去掉计算进去,才能显示完整的效果。

private val rectf by lazy {
        when
        {
            mWidth > mHeight  -> RectF(mWidth / 2 - mHeight / 2 + strokeWidth / 2, 0f + strokeWidth / 2, mWidth / 2 - mHeight / 2 + mHeight - strokeWidth / 2, mHeight - strokeWidth / 2)
            mWidth == mHeight -> RectF(0f + strokeWidth / 2, 0f + strokeWidth / 2, min(mWidth, mHeight) - strokeWidth / 2, min(mWidth, mHeight) - strokeWidth / 2)
            else              -> RectF(0f + strokeWidth / 2, mHeight / 2 - mWidth / 2 + strokeWidth / 2, mWidth - strokeWidth / 2, mHeight / 2 - mWidth / 2 + mWidth - strokeWidth / 2)
        }
    }

这段代码展示的是,在宽高不同的情况,计算不同的区域,能显示在控件的最中间,并且把画笔宽度计算进去,能显示出完整的效果。

代码篇

这里贴出我的完整代码,其实就是一个类,只不过,因为是自定义View,保不齐下一次也用的上,所以这里做一个记录,github就不上传了,就一个kt文件,懒得搞了。

package com.yongxing.HuYing.widgets

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.support.annotation.ColorInt
import android.support.v4.util.ArrayMap
import android.util.AttributeSet
import android.view.View
import com.yongxing.HuYing.utils.DensityUtil
import kotlin.math.min

/**
 * Created by xiaolei on 2017/11/29.
 */

class MultiElementProgress @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr)
{
    private var strokeWidth = 20f //画笔宽度
    private var startAngle = -45f //开始角度
    private val paint by lazy {
        Paint().apply {
            isAntiAlias = true
            color = Color.RED
            style = Paint.Style.STROKE
            strokeWidth = this@MultiElementProgress.strokeWidth
        }
    }
    private val rectf by lazy {
        when
        {
            mWidth > mHeight  -> RectF(mWidth / 2 - mHeight / 2 + strokeWidth / 2, 0f + strokeWidth / 2, mWidth / 2 - mHeight / 2 + mHeight - strokeWidth / 2, mHeight - strokeWidth / 2)
            mWidth == mHeight -> RectF(0f + strokeWidth / 2, 0f + strokeWidth / 2, min(mWidth, mHeight) - strokeWidth / 2, min(mWidth, mHeight) - strokeWidth / 2)
            else              -> RectF(0f + strokeWidth / 2, mHeight / 2 - mWidth / 2 + strokeWidth / 2, mWidth - strokeWidth / 2, mHeight / 2 - mWidth / 2 + mWidth - strokeWidth / 2)
        }
    }
    private val multiElement by lazy { ArrayMap<Int, Float>() }
    private var mHeight: Float = 0f
    private var mWidth: Float = 0f

    init
    {
        multiElement.put(Color.parseColor("#eadc4b"), 50f)
        multiElement.put(Color.parseColor("#f9a844"), 50f)
        multiElement.put(Color.parseColor("#b5db39"), 50f)
        strokeWidth = DensityUtil.dip2px(context, 16f).toFloat()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
    {
        mHeight = View.MeasureSpec.getSize(heightMeasureSpec).toFloat()
        mWidth = View.MeasureSpec.getSize(widthMeasureSpec).toFloat()
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    override fun onDraw(canvas: Canvas?)
    {
        var angle = startAngle
        paint.strokeWidth = strokeWidth;
        var allValue = 0f
        multiElement.all { entry ->
            allValue += entry.value
            true
        }
        for (entry in multiElement)
        {
            val sweepAngle = (entry.value.toFloat() / allValue) * 360
            paint.color = entry.key
            canvas?.drawArc(rectf, angle, sweepAngle, false, paint)
            angle += sweepAngle
        }
        super.onDraw(canvas)
    }

    /**
     * 设置每一个选项的 Item
     */
    public fun setProgress(elements: List<Element>)
    {
        multiElement.clear()
        for (element in elements)
        {
            multiElement.put(element.color, element.progress)
        }
        postInvalidate()
    }

    /**
     * 设置画笔宽度
     */
    public fun setStrokeWidth(strokeWidth: Float)
    {
        this.strokeWidth = strokeWidth
        postInvalidate()
    }

    /**
     * 设置开始画的角度
     */
    public fun setStartAngle(startAngle: Float)
    {
        this.startAngle = startAngle
        postInvalidate()
    }


    public class Element(@ColorInt val color: Int, val progress: Float)

}

要用的就自己拷贝拿走吧,根据自己的需求改吧改吧用用也行啊。 _##

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jack的Android之旅

教你实现最简单的QQ消息item侧滑菜单

这次跟大家介绍怎么简单的实现类似QQ消息Item的左右滑动菜单的实现。首先见效果图先:

1412
来自专栏向治洪

Palette状态栏颜色提取,写的不错就分享了

Palette 说Palette之前先说下前面提到的Pager。ViewPager是什么大家应该都是知道的了,一般ViewPager、xxxTabStrip...

2346
来自专栏Android Note

Android-可旋转、平移的饼状图(PieChartView)

4662
来自专栏Android干货

关于安卓开发实现侧滑菜单效果

1.1K8
来自专栏学海无涯

Android开发之View动画

Android动画主要分为3种 View动画 帧动画 属性动画 何为View动画? View动画主要是对View对象进行变换所达到的动画效果,如平移、缩放、旋转...

2765
来自专栏Android知识点总结

1-VII-RecyclerView基本使用

1063
来自专栏Android开发小工

View的位置参数及其Scroller类的理解

x和y是View左上角的坐标,translationX和translationY是View左上角相对于容器的偏移量。这几个参数都是相对于父容器的坐标。

1204
来自专栏androidBlog

自定义Drawable实现圆角图片和圆形图片

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

2092
来自专栏Android干货

Android项目实战(十五):自定义不可滑动的ListView和GridView

2965
来自专栏向治洪

android 打造不同的Seekbar

最近项目需要用到双向的seekbar,网上找了好多野不能达到要求,偶然一次机会看到了大众点评的例子,然后我最他做了优化,并对常用的seekbar做了总结. 向上...

4257

扫码关注云+社区

领取腾讯云代金券