分享一个Kotlin 写的超级 简单的自定义View,圆环统计

栏目: IOS · Android · 发布时间: 5年前

内容简介:最近项目中需要用到一个圆环统计,如下图所示,于是手撸了一个超级简单的分享给大家。 就当做给萌新自定义View的入门了,有需要的也可以直接拷贝过去就能用,先看效果图先构思一波,这个东西应该怎么实现,因为本身没有能直接画出圆环的api,所以我们需要换一个角度来, 可以用扇形来表示圆,中间加个小圆形覆盖在上面,这样就成了我们看到的圆环了。有了思路再一看这个,就很简单了。 剩下的就是控制几段,所占比例,以及颜色了。 所以只需要用到 drawCircle 和 drawArc 画圆和扇形首先咱们要确定有几段数据,每段的

最近项目中需要用到一个圆环统计,如下图所示,于是手撸了一个超级简单的分享给大家。 就当做给萌新自定义View的入门了,有需要的也可以直接拷贝过去就能用,先看效果图

分享一个Kotlin 写的超级 简单的自定义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 深度历险:核心原理与应用实践

Redis 深度历险:核心原理与应用实践

钱文品 / 电子工业出版社 / 2019-1 / 79

Redis 是互联网技术架构在存储系统中使用得最为广泛的中间件,也是中高级后端工程师技术面试中面试官最喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。《Redis 深度历险:核心原理与应用实践》作者老钱在使用 Redis 上积累了丰富的实战经验,希望帮助更多后端开发者更快、更深入地掌握 Redis 技能。 ......一起来看看 《Redis 深度历险:核心原理与应用实践》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码