内容简介:最近项目中需要用到一个圆环统计,如下图所示,于是手撸了一个超级简单的分享给大家。 就当做给萌新自定义View的入门了,有需要的也可以直接拷贝过去就能用,先看效果图先构思一波,这个东西应该怎么实现,因为本身没有能直接画出圆环的api,所以我们需要换一个角度来, 可以用扇形来表示圆,中间加个小圆形覆盖在上面,这样就成了我们看到的圆环了。有了思路再一看这个,就很简单了。 剩下的就是控制几段,所占比例,以及颜色了。 所以只需要用到 drawCircle 和 drawArc 画圆和扇形首先咱们要确定有几段数据,每段的
最近项目中需要用到一个圆环统计,如下图所示,于是手撸了一个超级简单的分享给大家。 就当做给萌新自定义View的入门了,有需要的也可以直接拷贝过去就能用,先看效果图
日常分析一波
先构思一波,这个东西应该怎么实现,因为本身没有能直接画出圆环的api,所以我们需要换一个角度来, 可以用扇形来表示圆,中间加个小圆形覆盖在上面,这样就成了我们看到的圆环了。有了思路再一看这个,就很简单了。 剩下的就是控制几段,所占比例,以及颜色了。 所以只需要用到 drawCircle 和 drawArc 画圆和扇形
public void drawArc(@RecentlyNonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @RecentlyNonNull Paint paint) { throw new RuntimeException("Stub!"); } public void drawCircle(float cx, float cy, float radius, @RecentlyNonNull Paint paint) { throw new RuntimeException("Stub!"); } 复制代码
撸代码
1.先准备数据元素
首先咱们要确定有几段数据,每段的颜色用什么标识,同时所占比例是多少。这些都不确定,需要可以手动设置,所以把他们放在一个实体类里面
fun setElementList(elements: MutableList<CircularRingElement>) { this.elements = elements postInvalidate() } data class CircularRingElement(@ColorInt val color: Int, val value: Float) 复制代码
2.计算View的大小
同时将圆心坐标,半径,以及扇形所在区域的矩形一并计算出来了
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) mWidth = MeasureSpec.getSize(widthMeasureSpec) mHeight = MeasureSpec.getSize(heightMeasureSpec) if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) { mWidth = dp2px(200f) //设置默认宽高 } if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) { mHeight = dp2px(200f) } //外圈扇形所在矩形区域 rectF.run { left = 0f + paddingLeft top = 0f + paddingTop right = mWidth.toFloat() - paddingRight bottom = mHeight.toFloat() - paddingBottom } innerCircleX = mWidth * 0.5f innerCircleY = mHeight * 0.5f innerCircleRadius = mWidth * 0.5f - mStrokeWidth - (paddingStart + paddingEnd) //保存测量结果 setMeasuredDimension(mWidth, mHeight) } 复制代码
3.Draw
最后再根据数据画出对应的扇形和内圈圆就OK了
override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) var startAngle = 0f var valueAll = 0f elements?.run { all { valueAll += it.value true } for (entry in this) { val sweepAngle = entry.value / valueAll * 360f canvas?.drawArc(rectF, startAngle, sweepAngle, true, mPaint.apply { color = entry.color }) startAngle += sweepAngle } } canvas?.drawCircle(innerCircleX, innerCircleY, innerCircleRadius, mPaint.apply { color = Color.WHITE }) } 复制代码
全部代码
方便需要的朋友直接拷贝了
fun View.dp2px(dp: Float): Int { val scale = this.resources.displayMetrics.density return (dp * scale + 0.5f).toInt() } fun View.px2dp(px: Float): Int { val scale = this.resources.displayMetrics.density return (px / scale + 0.5f).toInt() } /** * actor 晴天 create 2019/6/24 */ class CircularRingView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private var elements: MutableList<CircularRingElement>? = null //内圈小圆的圆心 private var innerCircleX = 0f private var innerCircleY = 0f //内圈小圆的半径 private var innerCircleRadius = 0f //View的宽高 private var mWidth = 0 private var mHeight = 0 //圆环宽度 private var mStrokeWidth = dp2px(10f) //画笔 private val mPaint by lazy { Paint().apply { isAntiAlias = true color = Color.RED style = Paint.Style.FILL strokeWidth = 10f } } //扇形所在的矩形区域 private val rectF = RectF() override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) mWidth = MeasureSpec.getSize(widthMeasureSpec) mHeight = MeasureSpec.getSize(heightMeasureSpec) if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) { mWidth = dp2px(200f) //设置默认宽高 } if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) { mHeight = dp2px(200f) } //外圈扇形所在矩形区域 rectF.run { left = 0f + paddingLeft top = 0f + paddingTop right = mWidth.toFloat() - paddingRight bottom = mHeight.toFloat() - paddingBottom } innerCircleX = mWidth * 0.5f innerCircleY = mHeight * 0.5f innerCircleRadius = mWidth * 0.5f - mStrokeWidth - (paddingStart + paddingEnd) //保存测量结果 setMeasuredDimension(mWidth, mHeight) } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) var startAngle = 0f var valueAll = 0f elements?.run { all { valueAll += it.value true } for (entry in this) { val sweepAngle = entry.value / valueAll * 360f canvas?.drawArc(rectF, startAngle, sweepAngle, true, mPaint.apply { color = entry.color }) startAngle += sweepAngle } } canvas?.drawCircle(innerCircleX, innerCircleY, innerCircleRadius, mPaint.apply { color = Color.WHITE }) } /** * 设置圆环厚度 */ fun setRingThickness(value: Int) { this.mStrokeWidth = value } /** * 设置数据元素 */ fun setElementList(elements: MutableList<CircularRingElement>) { this.elements = elements postInvalidate() } /** * 数据元素 */ data class CircularRingElement(@ColorInt val color: Int, val value: Float) } 复制代码
总体来说,很简单,本来想着不传上来的,但是想着以后可能又会遇到其他各种各样的统计图, 于是乎专门新建了这个工程,到时候可以直接归个总,避免重复造轮子了。同时有朋友要是有别的需求可以留言出来, 我抽空可以帮忙研究研究,就当作提高自己了
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Redis 深度历险:核心原理与应用实践
钱文品 / 电子工业出版社 / 2019-1 / 79
Redis 是互联网技术架构在存储系统中使用得最为广泛的中间件,也是中高级后端工程师技术面试中面试官最喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。《Redis 深度历险:核心原理与应用实践》作者老钱在使用 Redis 上积累了丰富的实战经验,希望帮助更多后端开发者更快、更深入地掌握 Redis 技能。 ......一起来看看 《Redis 深度历险:核心原理与应用实践》 这本书的介绍吧!