2 Star 5 Fork 2

newki/MyCircleProgressView

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

圆环进度的自定义View实现

前言

如果这是一篇自定义View的文章,但凡文章开头如果没有实现之后效果图,我是不看的,没图说个XX。

以己推人,我还是先放个图吧:

image.png

实现背景如下:是的还是我们那个大家都喜爱的UI设计师,需要显示员工出勤率的百分比数据,设计的是带圆环,带阴影,并且带动画逻辑。内部显示对应的百分比数据。

为什么自己要自己写?网上的一些轮子要么就过渡封装,要么就不符合需求,本着圆环的绘制与自定义也并不复杂,这里就记录一下自定义View圆环从零到实现的流程。专属定制一个自定义圆环,面向UI设计师开发!

看到这效果,我仿佛看到了大家的表情。

20200505111631_BSjrG.gif

大家应该或多或少的使用过一些圆形进度,我当然知道这对各位高工来说都是小KESE了,小小圆环还不是手到擒来。但是呢我希望对一些不是那么了解自定义View的同学有一些启发,当然高工们也可以跟着一起复习一下的啦。当然了也是为了后期的一些文章作为进阶,毕竟自定义View的绘制是比较基础的东西了。

话不多说,Let's go

300.png

一、自定义属性

作为一个自定义View,我们需要配置一些属性,那么必不可少的就需要一些自定义属性,比如我们的圆环View,从效果图上看的话,我们需要定义如下的元素:一个内环,一个外环,外环阴影,一个百分比的文本,一个提示文本。

从而我们就需要指定一下内环的宽度、背景颜色、外环的宽度、颜色、阴影的大小、阴影的颜色、百分比文字的大小颜色、提示文本的大小颜色,由于我们还需要做动画展示圆环,所以我们还需要配置是否开启动画。

总的来说一些自定义的属性定义位置如下:

image.png

具体的实现如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- 圆形进度条 -->
    <declare-styleable name="MyCircleProgressView">
        <attr name="mCirWidth" format="dimension" />
        <attr name="mCirColor" format="color" />
        <attr name="mBgCirWidth" format="dimension" />
        <attr name="mBgCirColor" format="color" />
        <attr name="animTime" format="integer" />
        <attr name="value" format="integer" />
        <attr name="maxvalue" format="integer" />
        <attr name="startAngle" format="float" />
        <attr name="sweepAngle" format="float" />
        <attr name="valueSize" format="dimension" />
        <attr name="valueColor" format="color" />
        <attr name="unit" format="string" />
        <attr name="hint" format="string" />
        <attr name="hintSize" format="dimension" />
        <attr name="hintColor" format="color" />
        <attr name="gradient" format="dimension" />
        <attr name="isGradient" format="boolean" />
        <attr name="shadowSize" format="float" />
        <attr name="shadowColor" format="color" />
        <attr name="shadowShow" format="boolean" />
        <attr name="digit" format="integer" />
        <attr name="isanim" format="boolean" />
    </declare-styleable>

</resources>

当我们在配置文件中定义了自定义属性,那么我们就可以在类中通过 StyledAttributes 来拿到我们定义的属性值。

例如:

private fun initAttrs(attrs: AttributeSet?, context: Context?) {
        val typedArray = context!!.obtainStyledAttributes(attrs, R.styleable.MyCircleProgressView)

        isAnim = typedArray.getBoolean(R.styleable.MyCircleProgressView_isanim, true)
        mDigit = typedArray.getInt(R.styleable.MyCircleProgressView_digit, 2)
        mBgCirColor = typedArray.getColor(R.styleable.MyCircleProgressView_mBgCirColor, Color.GRAY)
        mBgCirWidth = typedArray.getDimension(R.styleable.MyCircleProgressView_mBgCirWidth, 15f)
        mCirColor = typedArray.getColor(R.styleable.MyCircleProgressView_mCirColor, Color.YELLOW)
        mCirWidth = typedArray.getDimension(R.styleable.MyCircleProgressView_mCirWidth, 15f)
        mAnimTime = typedArray.getInt(R.styleable.MyCircleProgressView_animTime, 1000)
        mValue = typedArray.getString(R.styleable.MyCircleProgressView_value)
        mMaxValue = typedArray.getFloat(R.styleable.MyCircleProgressView_maxvalue, 100f)
        mStartAngle = typedArray.getFloat(R.styleable.MyCircleProgressView_startAngle, 270f)
        mSweepAngle = typedArray.getFloat(R.styleable.MyCircleProgressView_sweepAngle, 360f)
        mValueSize = typedArray.getDimension(R.styleable.MyCircleProgressView_valueSize, 15f)
        mValueColor = typedArray.getColor(R.styleable.MyCircleProgressView_valueColor, Color.BLACK)
        mHint = typedArray.getString(R.styleable.MyCircleProgressView_hint)
        mHintSize = typedArray.getDimension(R.styleable.MyCircleProgressView_hintSize, 15f)
        mHintColor = typedArray.getColor(R.styleable.MyCircleProgressView_hintColor, Color.GRAY)
        mUnit = typedArray.getString(R.styleable.MyCircleProgressView_unit)
        mShadowColor = typedArray.getColor(R.styleable.MyCircleProgressView_shadowColor, Color.BLACK)
        mShadowIsShow = typedArray.getBoolean(R.styleable.MyCircleProgressView_shadowShow, false)
        mShadowSize = typedArray.getFloat(R.styleable.MyCircleProgressView_shadowSize, 8f)
        isGradient = typedArray.getBoolean(R.styleable.MyCircleProgressView_isGradient, false)
        mGradientColor = typedArray.getResourceId(R.styleable.MyCircleProgressView_gradient, 0)
        if (mGradientColor != 0) {
            mGradientColors = resources.getIntArray(mGradientColor!!)
        }

        typedArray.recycle()
    }

每一种自定义View的属性自定义方式都是类似的。

拿到我们配置的一些自定义属性,赋值给对应的成员变量之后,我们就可以初始化一些画笔与矩阵资源。

二、画笔和矩阵资源的初始化

不同的资源绘制我们需要不同的画笔,所以我们都声明出来

   //是否开启抗锯齿(默认开启)
    private var antiAlias: Boolean = true

    //声明背景圆画笔
    private lateinit var mBgCirPaint: Paint //画笔
    private var mBgCirColor: Int? = null //颜色
    private var mBgCirWidth: Float = 15f //圆环背景宽度

    //声明进度圆的画笔
    private lateinit var mCirPaint: Paint //画笔
    private var mCirColor: Int? = null //颜色
    private var mCirWidth: Float = 15f //主圆的宽度

    //绘制进度数值
    private lateinit var mValuePaint: TextPaint
    private var mValueSize: Float? = null
    private var mValueColor: Int? = null

    //绘制提示文本
    private var mHint: CharSequence? = null
    private lateinit var mHintPaint: TextPaint
    private var mHintSize: Float? = null
    private var mHintColor: Int? = null

然后我们就可以通过我们的自定义属性给这些画笔做初始化和赋值操作。自定义属性的赋值操作上面我们已经赋值过了,这里就看如何初始化画笔资源。

 /**
     * 初始化画笔
     */
    private fun initPaint() {
        //圆画笔(主圆的画笔设置)
        mCirPaint = Paint()
        mCirPaint.isAntiAlias = antiAlias //是否开启抗锯齿
        mCirPaint.style = Paint.Style.STROKE //画笔样式
        mCirPaint.strokeWidth = mCirWidth //画笔宽度
        mCirPaint.strokeCap = Paint.Cap.ROUND  //笔刷样式(圆角的效果)
        mCirPaint.color = mCirColor!!//画笔颜色

        //背景圆画笔(一般和主圆一样大或者小于主圆的宽度)
        mBgCirPaint = Paint()
        mBgCirPaint.isAntiAlias = antiAlias
        mBgCirPaint.style = Paint.Style.STROKE
        mBgCirPaint.strokeWidth = mBgCirWidth
        mBgCirPaint.strokeCap = Paint.Cap.ROUND
        mBgCirPaint.color = mBgCirColor!!


        //初始化主题文字的字体画笔
        mValuePaint = TextPaint()
        mValuePaint.isAntiAlias = antiAlias  //是否抗锯齿
        mValuePaint.textSize = mValueSize!!  //字体大小
        mValuePaint.color = mValueColor!!  //字体颜色
        mValuePaint.textAlign = Paint.Align.CENTER //从中间向两边绘制,不需要再次计算文字
        mValuePaint.typeface = TypefaceUtil.getSFSemobold(context) //字体加粗

        //初始化提示文本的字体画笔
        mHintPaint = TextPaint()
        mHintPaint.isAntiAlias = antiAlias
        mHintPaint.textSize = mHintSize!!
        mHintPaint.color = mHintColor!!
        mHintPaint.textAlign = Paint.Align.CENTER
        mHintPaint.typeface = TypefaceUtil.getSFRegular(context) //自定义字体
    }

由于是自定义View的第一篇,这里我尽量把每一个的自定义属性都注释清楚,方便大家查看,后面别的自定义View就没这么详细了。

那么我们在初始化了资源之后,我们准备绘制,怎么绘制呢?绘制在哪呢?大小都还不知道呢!

是的,如果我们需要画圆的话,我们至少需要圆的中心点,半径,矩阵大小之类的方向位置信息吧。

    //圆心位置
    private lateinit var centerPosition: Point

    //半径
    private var raduis: Float? = null

    //声明边界矩形
    private var mRectF: RectF? = null

    //颜色渐变色
    private var isGradient: Boolean? = null
    private var mGradientColors: IntArray? = intArrayOf(Color.RED, Color.GRAY, Color.BLUE)
    private var mGradientColor: Int? = null
    private var mSweepGradient: SweepGradient? = null

    /**
     * 设置圆形和矩阵的大小,设置圆心位置
     */
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        //圆心位置
        centerPosition.x = w / 2
        centerPosition.y = h / 2

        //半径
        val maxCirWidth = Math.max(mCirWidth, mBgCirWidth)
        val minWidth = Math.min(
            w - paddingLeft - paddingRight - 2 * maxCirWidth,
            h - paddingBottom - paddingTop - 2 * maxCirWidth
        )

        raduis = minWidth / 2

        //矩形坐标
        mRectF!!.left = centerPosition.x - raduis!! - maxCirWidth / 2
        mRectF!!.top = centerPosition.y - raduis!! - maxCirWidth / 2
        mRectF!!.right = centerPosition.x + raduis!! + maxCirWidth / 2
        mRectF!!.bottom = centerPosition.y + raduis!! + maxCirWidth / 2

        if (isGradient!!) {
            setupGradientCircle() //设置圆环画笔颜色渐变
        }
    }


    /**
     * 使用渐变色画圆
     */
    private fun setupGradientCircle() {
        mSweepGradient =
            SweepGradient(
                centerPosition.x.toFloat(),
                centerPosition.y.toFloat(),
                mGradientColors!!,
                null
            )
        mCirPaint.shader = mSweepGradient
    }

我们再 onSizeChanged 即显示出来的时候,我们对半径,中心点,位置矩阵做一些赋值,然后我们顺便设置并支持了渐变的圆形进度支持。

如果支持渐变的话,我们对主圆的画笔颜色设置为渐变的方式。

所有的资源和信息都已经初始化和赋值之后,我们就可以开始绘制了。

三、文本与圆环的绘制

由于是自定义View了,绘制肯定是走 onDraw 方法了。

   /**
     * 核心方法-绘制文本与圆环
     */
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        drawText(canvas)
        drawCircle(canvas)
    }

我们先看文本的绘制,一个是百分比,一个是提示的文本。我们使用 canvas.drawText 来绘制文本。方法如下:

    /**
     * 绘制中心的文本
     */
    private fun drawText(canvas: Canvas?) {

        canvas!!.drawText(
            mValue + mUnit,
            centerPosition.x.toFloat(),
            centerPosition.y.toFloat(),
            mValuePaint
        )

        if (mHint != null || mHint != "") {
            canvas.drawText(
                mHint.toString(),
                centerPosition.x.toFloat(),
                centerPosition.y - mHintPaint.ascent() + 15,   //设置Y轴间距
                mHintPaint
            )
        }

    }

我们赋值的Value比如为60,那么中间的绘制的文本就是60% 。我们绘制的Hilt提示文本就在我们的百分比文本下面,我们对中心点向下偏移15即可定位绘制。

那么绘制圆形怎么绘制,大家应该知道 drawCircle 和 drawArc 一个是圆形,一个是圆环,我们这里使用的是 drawArc 。

我们设置一个起始角度,和一个绘制角度,然后使用绘制背景的一个圆和进度主圆,代码如下:

    //绘制的起始角度和滑过角度(默认从顶部开始绘制,绘制总进度360度)
    private var mStartAngle: Float = 270f
    private var mSweepAngle: Float = 360f

    /**
     * 画圆(主要的圆)
     */
    private fun drawCircle(canvas: Canvas?) {
        canvas?.save()
        if (mShadowIsShow) {
            mCirPaint.setShadowLayer(mShadowSize!!, 0f, 0f, mShadowColor!!) //设置阴影
        }

        //画背景圆(画一整个圆环)
        canvas?.drawArc(mRectF!!, mStartAngle, mSweepAngle, false, mBgCirPaint)

        //画圆(计算出进度吗,按进度来绘制)
        val percent = value.toFloat() / maxValue
        canvas?.drawArc(mRectF!!, mStartAngle, mSweepAngle * percent, false, mCirPaint)
        canvas?.restore()
    }

如此我们就可以画出静态的2个圆环了,并且我们顺便把阴影也绘制出来,使用的是 setShadowLayer 方法,当然阴影的使用我们还有另一种方法 BlurMaskFilter 也可以设置阴影的效果,大家如果有兴趣可以翻看我前面的文章,实现圆角阴影的ViewGroup

其实到这里就已经可以展示出我们文章开头的那种静态展示效果了。

image.png

虽然静态能实现了,但是和我们设计师的需求还是差一点效果,怎么让进度条动起来呢?

四、动起来

思路:定义一个属性动画,定义出开始的百分比进度和计算出总共需要的百分比进度,然后我们通过属性动画从开始进度到总共进度做指定时间的动画。

    //属性动画
    private var mAnimator: ValueAnimator? = null
    //动画进度
    private var mAnimPercent: Float = 0f

    /**
     * 执行属性动画
     */
    private fun startAnim(start: Float, end: Float, animTime: Int) {
        mAnimator = ValueAnimator.ofFloat(start, end)
        mAnimator?.duration = animTime.toLong()
        mAnimator?.addUpdateListener {
            //得到当前的动画进度并赋值
            mAnimPercent = it.animatedValue as Float

            //根据当前的动画得到当前的值
            mValue = if (isAnim) {
                roundByScale((mAnimPercent * mMaxValue).toDouble(), mDigit)
            } else {
                roundByScale(mValue!!.toDouble(), mDigit)
            }

            //不停的重绘当前值-表现出动画的效果
            postInvalidate()
        }
        mAnimator?.start()
    }

    //计算百分比的小数点后面的位数显示
    fun roundByScale(v: Double, scale: Int): String {
        if (scale < 0) {
            throw IllegalArgumentException("参数错误,必须设置大于0的数字")
            }
            if (scale == 0) {
                return DecimalFormat("0").format(v)
            }
            var formatStr = "0."

            for (i in 0 until scale) {
                formatStr += "0"
            }
            return DecimalFormat(formatStr).format(v);
        } 

    /**
     * 画圆(主要的圆)
     */
    private fun drawCircle(canvas: Canvas?) {
        canvas?.save()
        if (mShadowIsShow) {
            mCirPaint.setShadowLayer(mShadowSize!!, 0f, 0f, mShadowColor!!) //设置阴影
        }

        //画背景圆
        canvas?.drawArc(mRectF!!, mStartAngle, mSweepAngle, false, mBgCirPaint)

        //画圆
        canvas?.drawArc(mRectF!!, mStartAngle, mSweepAngle * mAnimPercent, false, mCirPaint)
        canvas?.restore()
    }

总体代码也是比较简单,这样就可以通过动画动态的计算出当前百分比的Value值和当前动画进度值。然后我们修改drawCircle的方法以当前的进度为准来绘制动画,这样就可以做出动画的效果。

动画执行的效果如下:

device-2022-11-10-173613 00_00_00-00_00_30.gif

尾记

本篇为自定义View的基础绘制起始,后期会有一个小系列的自定义View合集,我会在此基础上加入交互的逻辑,下次就没有这么细了哦 - -|

通过这一篇圆形进度的自定义绘制,我们其实可以进行扩展,思路打开。不仅仅是可以绘制圆形,圆环,还能绘制横向,纵向的进度条,还能绘制带进度的文本。只要是Canvas支持的能draw的东西,我们都能以这种方式做成进度的方式。

虽然是比较简单的自定义View了,我还是上传到了Maven,有需要的直接依赖即可

implementation "com.gitee.newki123456:circle_progress_view:1.0.0"

当然如果你想查看源码或者做一些定制化的修改,点击传送门查看源码。

同时,你也可以关注我的Kotlin项目合集,传送门。项目会持续更新。

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

轻量的,定制的,可配置的简单圆形进度控件 展开 收起
Kotlin
Apache-2.0
取消

发行版 (1)

全部

贡献者

全部

近期动态

加载更多
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/newki123456/MyCircleProgressView.git
git@gitee.com:newki123456/MyCircleProgressView.git
newki123456
MyCircleProgressView
MyCircleProgressView
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891