按照设置的百分比数组,设置的百分比数组之和要等于1,如果大于1会出现覆盖的情况。
这个效果里面有2个关注点:
1、通过百分比数组绘制多大的环。
2、切换比例的时候动画效果。
3、正在变化时接收到百分比变化的处理

1、通过百分比数组绘制多大的环

圆环一圈是360度,如果百分比是0.2,那么圆环就绘制360 * 0.2 = 72,起始位置是在前面进度的结尾处。代码如下:

var startAngle = -90Ffor (i in 0 until mPercentages.size) {if (i > mPercentageColors.size - 1) {//如果没有为每一段百分比设置颜色值,则使用最后一种颜色值mPaint.color = mPercentageColors[mPercentageColors.size - 1]} else {mPaint.color = mPercentageColors[i]}val sweepAngle = mPercentages[i] * 360canvas?.drawArc(mArcRectF, startAngle, sweepAngle, false, mPaint)startAngle += sweepAngle}

2、切换比例的时候动画效果

1、补全数组

2个不同的百分比数组有可能长度不同,我们要较短那个后面补上0

/*** 补全数组,使2个数组的长度相等* fromValues : [0.1,0.2,0.4,0.3]*    toValues: [0.1,0.2,0.7]** 将toValues变为:[0.1,0.2,0.7,0]*/private fun completionArrays(fromValues: Array<Float>, toValues: Array<Float>): Values {return when {fromValues.size == toValues.size -> Values(fromValues, toValues)fromValues.size > toValues.size -> {//补全toValuesvar newValues: Array<Float> = Array(fromValues.size) { 0F }for (i in 0 until toValues.size) {newValues[i] = toValues[i]}Values(fromValues, newValues)}else -> {//fromValuesvar newValues: Array<Float> = Array(toValues.size) { 0F }for (i in 0 until fromValues.size) {newValues[i] = fromValues[i]}Values(newValues, toValues)}}}

Values是一个内部类:

data class Values(val fromValues: Array<Float>, val toValues: Array<Float>)

2、开启一个动画,根据动画的进度计算当前百分比

/*** 启动动画**/private fun startAnimator(fromValues: Array<Float>, toValues: Array<Float>) {isChange = trueval (fv, tv) = completionArrays(fromValues, toValues)val animator = ValueAnimator.ofFloat(0F, 1F)animator.duration = mAnimDurationanimator.addUpdateListener {computePercentages(it.animatedValue as Float, fv, tv)postInvalidate()if (it.animatedValue as Float == 1F) {//动画结束isChange = false
//                val list = mPercentages.filter { it > 0 }
//                mPercentages = Array(list.size) { i -> list[i] }mNextToValues?.let { startAnimator(mPercentages, mNextToValues!!) }mNextToValues = null}}animator.start()}
/*** 计算当前动画百分比的值*/private fun computePercentages(animatedValue: Float, fromValues: Array<Float>, toValues: Array<Float>) {var newValues = Array(fromValues.size) { 0F }for (i in 0 until fromValues.size) {newValues[i] = fromValues[i] - (fromValues[i] - toValues[i]) * animatedValue}mPercentages = newValues}

根据当前的进度实时绘制饼状图。

3、正在变化时接收到百分比变化的处理

如果当前正在变化,又接收到百分比变化,则记录此次变化,等上个动画结束时马上再次启动动画。

整体code:
PieChart.kt:

class PieChart @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : View(context, attrs, defStyleAttr, defStyleRes) {/*** 百分比数组,数组和要=1*/private var mPercentages: Array<Float> = arrayOf(1.0F)/*** 不同百分比的颜色值*/private var mPercentageColors: Array<Int> = arrayOf(Color.parseColor("#fff4e0"),Color.parseColor("#f8b500"),Color.parseColor("#ff4d4d"),Color.parseColor("#42d3b7"),Color.parseColor("#334d5c"))/*** 圆环画笔*/private var mPaint: Paint = Paint()/*** 圆环的大小*/private var mArcRectF: RectF = RectF()/*** 圆环宽度*/private var mStrokeWidth: Float = 20F/*** 圆环变化时是否使用动画*/private var mUseAnimation: Boolean = true/*** 动画时长,单位ms*/private var mAnimDuration: Long = 500L/*** 圆环正在改变时又有新的数据来记录下来,等圆环改变结束在刷新新的数据*/private var mNextToValues: Array<Float>? = nullprivate var isChange = falseinit {val a = context.theme.obtainStyledAttributes(attrs, R.styleable.PercentageRing, defStyleAttr, 0)mStrokeWidth = a.getDimension(R.styleable.PercentageRing_stroke_width, 10F)//设置圆环画笔mPaint.style = Paint.Style.STROKEmPaint.strokeWidth = mStrokeWidthmPaint.isAntiAlias = true}override fun onDraw(canvas: Canvas?) {drawRing(canvas)}/*** 绘制圆环** @param canvas 画布*/private fun drawRing(canvas: Canvas?) {var ringWidth = widthvar height = heightmArcRectF.left = mStrokeWidth / 2mArcRectF.right = width - mStrokeWidth / 2mArcRectF.top = mStrokeWidth / 2mArcRectF.bottom = height - mStrokeWidth / 2var startAngle = -90Ffor (i in 0 until mPercentages.size) {if (i > mPercentageColors.size - 1) {//如果没有为每一段百分比设置颜色值,则使用最后一种颜色值mPaint.color = mPercentageColors[mPercentageColors.size - 1]} else {mPaint.color = mPercentageColors[i]}val sweepAngle = mPercentages[i] * 360canvas?.drawArc(mArcRectF, startAngle, sweepAngle, false, mPaint)startAngle += sweepAngle}}/*** 设置百分比数据和中间显示文案** @param percentages 百分比*/fun setPercentages(percentages: Array<Float>) {if (mUseAnimation) {if (isChange) {mNextToValues = percentagesreturn}startAnimator(mPercentages, percentages)} else {mPercentages = percentagespostInvalidate()}}fun setPercentages(percentages: Array<Float>, colors: Array<Int>) {mPercentages = percentagessetColors(colors)postInvalidate()}/*** 设置圆环颜色** @param colors 颜色RGB值*/fun setColors(colors: Array<Int>) {mPercentageColors = colors}/*** 启动动画**/private fun startAnimator(fromValues: Array<Float>, toValues: Array<Float>) {isChange = trueval (fv, tv) = completionArrays(fromValues, toValues)val animator = ValueAnimator.ofFloat(0F, 1F)animator.duration = mAnimDurationanimator.addUpdateListener {computePercentages(it.animatedValue as Float, fv, tv)postInvalidate()if (it.animatedValue as Float == 1F) {//动画结束isChange = false
//                val list = mPercentages.filter { it > 0 }
//                mPercentages = Array(list.size) { i -> list[i] }mNextToValues?.let { startAnimator(mPercentages, mNextToValues!!) }mNextToValues = null}}animator.start()}/*** 补全数组,使2个数组的长度相等* fromValues : [0.1,0.2,0.4,0.3]*    toValues: [0.1,0.2,0.7]** 将toValues变为:[0.1,0.2,0.7,0]*/private fun completionArrays(fromValues: Array<Float>, toValues: Array<Float>): Values {return when {fromValues.size == toValues.size -> Values(fromValues, toValues)fromValues.size > toValues.size -> {//补全toValuesvar newValues: Array<Float> = Array(fromValues.size) { 0F }for (i in 0 until toValues.size) {newValues[i] = toValues[i]}Values(fromValues, newValues)}else -> {//fromValuesvar newValues: Array<Float> = Array(toValues.size) { 0F }for (i in 0 until fromValues.size) {newValues[i] = fromValues[i]}Values(newValues, toValues)}}}/*** 计算当前动画百分比的值*/private fun computePercentages(animatedValue: Float, fromValues: Array<Float>, toValues: Array<Float>) {var newValues = Array(fromValues.size) { 0F }for (i in 0 until fromValues.size) {newValues[i] = fromValues[i] - (fromValues[i] - toValues[i]) * animatedValue}mPercentages = newValues}data class Values(val fromValues: Array<Float>, val toValues: Array<Float>)companion object {const val TAG = "PieChart"}
}

自定义属性 attr.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="PieChart"><attr name="stroke_width" format="dimension" /></declare-styleable>
</resources>

使用及测试 TestActivity.kt :

class TestActivity : AppCompatActivity() {val handler = object : Handler() {override fun handleMessage(msg: Message?) {pieChart.setPercentages(list[index++ % list.size])sendEmptyMessageDelayed(0, 1000)}}var list = arrayListOf<Array<Float>>(arrayOf(0.1F, 0.2F, 0.3F, 0.4F),arrayOf(0.2F, 0.3F, 0.2F, 0.3F),arrayOf(0.5F, 0.5F),arrayOf(0.1F, 0.2F, 0.3F, 0.4F),arrayOf(0.3F, 0.5F, 0.2F),arrayOf(0.1F, 0.8F, 0.1F))var index = 0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_test)handler.sendEmptyMessage(0)}
}

布局文件 activity_test.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"xmlns:app="http://schemas.android.com/apk/res-auto"><PieChartandroid:id="@+id/pieChart"android:layout_width="300dp"android:layout_height="300dp"app:stroke_width="@dimen/dp_11"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintBottom_toBottomOf="parent"/></android.support.constraint.ConstraintLayout>

自定义View-饼状图(百分比图)相关推荐

  1. android自定义View: 饼状图绘制(四)

    本系列自定义View全部采用kt 系统mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 本篇效果: 画矩 ...

  2. Python的可视化包 – Matplotlib 2D图表(点图和线图,.柱状或饼状类型的图),3D图表(曲面图,散点图和柱状图)...

    Python的可视化包 – Matplotlib Matplotlib是Python中最常用的可视化工具之一, 可以非常方便地创建海量类型地2D图表和一些基本的3D图表.Matplotlib最早是为了 ...

  3. android 轨迹生成图,Android自定义View实现公交成轨迹图

    本文实例为大家分享了Android自定义View实现公交成轨迹图的具体代码,供大家参考,具体内容如下 总体分析下:水平方向recyclewview,item包含定位点,站台位置和站台名称. 下面看实现 ...

  4. Android使用自定义View来实现K线图、分时图

    使用自定义View来实现K线图.分时图 实现原理 1.分时图 2.K线图 3.十字标 4.缩放.滑动 实现步骤 基类BaseChart 画图 总结 下面附上Demo的下载地址 废话不多说 先上效果图 ...

  5. 自定义View 实现左右拖动脉象图

    写在前面 (平时开发遇到的问题和解决思路) 项目需求:采集过的脉象图,可以左右滑动,类似查看历史波形,我这里采用 的办法是用HorScrollView 嵌套View,讲view的宽度设置宽一点,监听用 ...

  6. android 图片颜色渐变动画,自定义View之颜色渐变折线图

    今日科技快讯 近日,汽车之家宣布与湖南卫视达成独家战略合作.据悉,双方开辟了"台网互动"营销模式,将共同开展汽车广告业务,并联合推出品效合一的新型汽车类广告产品"芒果汽车 ...

  7. android布局中画圆角矩形,Android 自定义View之圆角矩形轨迹图

    一.原理说明 主要是通过计算轨迹的坐标点加入到集合中,然后对集合进行相应截取,传入canvas中. 二.具体代码实现 /** * 原理是先通过尺寸把各个轨迹的坐标计算出来,然后再截取相应坐标,进行重绘 ...

  8. android 文字锯齿,android自定义view锯齿状效果

    效果图 public class SawtoothBlackView extends View { //自定义画笔的属性 private Paint paint; //获取屏幕的系数 private ...

  9. Flutter 自定义View之 饼状图

    版权声明:本文为博主原创文章,转载请注明出处! 今天跟大家分享的是用Flutter来实现的自定义饼状图,下面来看看效果! 通过点击左右两侧的按钮,可以实现扇形切换,被选中的扇形有个放大的效果,中间的百 ...

  10. Android自定义View之扇形饼状图

    前言:继上次写了自定义圆形进度条后,今天给大家带来自定义扇形饼状图.先上效果图: 是不是很炫?看上去还有点立体感.下面带大家一起来瞧一瞧吧. 一.定义成员变量,重写构造方法 看着这个效果图,我们可以想 ...

最新文章

  1. 如何向5岁小孩解释什么是支持向量机(SVM)?
  2. iOS 关于枚举的使用
  3. 程序员成长之路 java面试指导(作者说的极好要看) 静下心看
  4. Honeycomb——BFS
  5. php历史上的今天源码,代码获取历史上的今天发生的事_基础知识
  6. java openmp库_OpenMP的环境变量及库函数
  7. c语言怎么产生随机字母,菜鸟求助,写一个随机输出26个英文字母的程序
  8. python有两个运行程序分别是什么_运行python程序的两种方式
  9. java 生成多叉树_java中多叉树(tree)的生成与显示 | 学步园
  10. 101个微软提供的Visual Studio 2005示例
  11. 微博上一些有用的话(四)
  12. iptv服务器维护中,iptv升级服务器地址
  13. c++ 建立MFC应用程序
  14. CAD转换高清图片该如何进行设置
  15. MySQL的索引失效问题
  16. PreferenceActivity 分屏显示 分析
  17. 软件缺陷报告模板(微信缺陷报告案例)
  18. java week of year_JDK Calendar类获取WEEK_OF_YEAR时的陷阱
  19. 无法正常启动0xc0000142的错误
  20. 【正点原子FPGA连载】第二十七章DS18B20数字温度传感器实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

热门文章

  1. uni-app 接入高德地图案例
  2. 华尔街英语宝典,架构师必备技能
  3. 买二手房和买新房-----这几年的是没差别的
  4. 有关孙卫琴的个人网站
  5. Win10系统如何调整分区大小?
  6. Hyperlynx使用心得
  7. 人人都应该成为数据分析师
  8. 从制造大国到制造强国,智能制造改变制造业未来
  9. pspice仿真库DC电源设置请教
  10. 大脑记忆系统研究取得重大进展:或被用于开发新的芯片和操作系统