安卓开发-用Kotlin语言自定义View实现百分比加载动画

动画效果

(最后有全部代码)

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第1张图片
1602840951140[00_00_01--00_00_05].gif

第一步:新建Class自定义View

package com.example.percentloadinganimator

import android.content.Context
import android.util.AttributeSet
import android.view.View

class PercentLoading:View {
    constructor(context:Context):super(context)
    constructor(context: Context,attrs:AttributeSet):super(context){}
}

第二步:显示自定义View

方法一:新建自定义View类的对象
方法二:在要显示该自定义View的Activity(这里选择MainActivity)的xml文件中配置该自定义View的属性,使得Activity上能显示该View

    

第三步:重写onSizeChanged方法和onDraw方法

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
    }
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
    }

第四步:准备画一个圆环所需要的元素

    private var cx = 0f//初始化到x轴的距离
    private var cy = 0f//初始化到y轴的距离
    private var radius = 0f//初始化圆的半径
    private val mstrokeWidth = 50f//初始化圆圈的宽度
    private var BackProgressCircle = Paint().apply {
        color = Color.GRAY//设置背景圆圈的颜色
        style = Paint.Style.STROKE//设置圆圈的风格为STROKE
        strokeWidth = mstrokeWidth//设置圆圈的宽度
    }//初始化绘制圆圈的画笔

第五步:确定要画圆圈相对于自定义View的位置

在View的大小确定下来之后,我们需要对圆心的位置以及半径进行确定,这个时候在onSizeChanged方法中修改之前初始化了的cx、cy和radius的值

1.确定圆心

圆心的位置应该处于控件的正中心,只需要取宽度和长度的一半即可

2.确定半径

由于自定义的控件是矩形的,要想使得圆圈不越界且最大,则圆圈要与矩形控件的两条边相切。所以这里需要选取控件较短的边作为确定半径的数据。
此外,由于给圆圈增加了宽度,半径的长度还要减去宽度。

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第2张图片
image.png
 override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        cx = width*0.5f
        cy = height*0.5f
        //定位到控件的中心
        radius = Math.min(width,height)/2f-mstrokeWidth//取View长或宽的一半,再减去圆圈的宽度作为半径
        //这里必须要减去圆圈的宽度,否则会View中就显示不出来
    }

现在我们已经做出了第一个圆圈,作为背景圆圈

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第3张图片
image.png

第六步:绘制进度条圆弧(此项目的难点)

1.方法的参数详解

在此过程中需要使用到drawArc方法,下面对该方法做简单介绍

public void drawArc(float left, float top, float right, float bottom, float startAngle,
            float sweepAngle, boolean useCenter, @NonNull Paint paint) {
        super.drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
    }

该方法的难点在于理解四个方向参数的意义。把所画的圆弧假想为一个完整的圆圈,再假想一个与这个圆圈外切的正方形,其中left、top、right、bottom为正方形相对于自定义控件的4个相对位置,注意,是相对于自定义的控件!通过这4个位置,可以确定所画圆弧的圆心。

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第4张图片
image.png

startAngle为开始绘制的角度,开发工具已经规定好了圆圈的3点钟方向为0°,顺时针方向角度为正。逆时针方向角度为负。
sweepAngle为需要扫过的角度
useCenter表示,在绘制的过程中是否需要与圆心相连。如果是true,则绘制出来的是扇形。
在理清各个参数后,我们不难知道,left和top是圆圈的宽度mstrokeWidth,right是控件的宽度-圆圈的宽度,bottom是控件的高度-圆圈的宽度,起始角度为-90°

2.参数的确定

(1)确定四个方向

通过之前对参数的分析,我们不难计算出
left = mstrokeWidth
top = mstrokeWidth
right = width.toFloat()-mstrokeWidth
bottom = height.toFloat()-mstrokeWidth

(2)确定初始角度

我们从圆圈的顶部开始画,所以startAngle = -90f

(3)确定扫过的角度

扫过的角度应该是时刻变化的,而不是固定的一个角度。所以我们需要时刻获取圆弧当前的属性值,再乘以360,就是当前绘制的角度

   var Progress = 0f//初始化Progress,由于外部要访问该值,不能设为private
        set(value){
            field = value
            invalidate()
        }//通过field来更新Progress的值,并且通过invalidate来刷新(这一步需要通过之后的动画来实现)

(4)初始化进度条(Arc)的画笔

    private var ForeProgressCircle = Paint().apply {
        color = Color.GREEN//设置进度条的颜色
        style = Paint.Style.STROKE
        strokeWidth = mstrokeWidth
    }//进度条圆圈的画笔

(5)更新onDraw方法

    override fun onDraw(canvas: Canvas?) {
        //绘制背景圆圈
        canvas?.drawCircle(cx,cy,radius,progressPaint)
        //绘制进度条
        canvas?.drawArc(
            mstrokeWidth,mstrokeWidth,
            width.toFloat()-mstrokeWidth,
            height.toFloat()-mstrokeWidth,
            -90f,360*Progress,
            false,PercentprogressPaint
        )
    }

第七步:添加进度条属性动画

1.添加开始动画和停止动画按钮

开始动画按钮id为StartAnimatorbtn,停止动画按钮id为StopAnimatorbtn

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第5张图片
image.png

2.在主界面使用懒加载添加属性动画

    private val ProgressPercentAnimotor:ValueAnimator by lazy {
        ValueAnimator.ofFloat(0f,1f).apply {
            duration = 2000
            addUpdateListener{
                percentLoading.Progress =it.animatedValue as Float//获取进度条属性的值
            }
        }
    }

3.实现动画效果

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        StartAnimator.setOnClickListener {
            if(ProgressPercentAnimotor.isPaused){
                ProgressPercentAnimotor.resume()//如果当前动画是停止状态,则点击开始按钮重新运行
            }else{
                ProgressPercentAnimotor.start()
            }
        }
        StopAnimator.setOnClickListener {
            ProgressPercentAnimotor.pause()
        }
    }

4.效果图

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第6张图片
1602838109688[00_00_04--00_00_24].gif

第八步:用drawText方法绘制文本,记录进度

1.drawText方法一共有4种参数类型,这里我们选择第二种

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第7张图片
image.png

第一个参数为要写的内容,第二个参数为TextView的横坐标,第三个参数为TextView的纵坐标,第四个参数为画笔。

2.确定参数

private var text = "${(Progress*100).toInt()}%"//文本内容为一个百分数
private var TextPaint = Paint().apply {
        color  = Color.BLACK
        style = Paint.Style.FILL
        textSize = 100f
        textAlign = Paint.Align.CENTER//字体的位置
    }//初始化绘制字体的画笔
//x = cx,y = cy

3.绘制文本

canvas?.drawText(text,cx,cy,TextPaint)

4.写进onDraw方法里面

    override fun onDraw(canvas: Canvas?) {
        //绘制背景圆圈
        canvas?.drawCircle(cx,cy,radius,progressPaint)
        //绘制进度条
        canvas?.drawArc(
            mstrokeWidth,mstrokeWidth,
            width.toFloat()-mstrokeWidth,
            height.toFloat()-mstrokeWidth,
            -90f,360*Progress,
            false,PercentprogressPaint
        )
        val text = "${(Progress*100).toInt()}%"
        val metrics =  TextPaint.fontMetrics
        var space = (metrics.descent-metrics.ascent)/2f - metrics.descent
        canvas?.drawText(text,cx,cy+space,TextPaint)
    }

5.效果图

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第8张图片
1602839786984[00_00_04--00_00_24].gif

这个时候整个效果就快要实现了,但是我们发现了一个问题,文本并没有处于圆圈的正中心。

6.自定义Text讲解

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第9张图片
8}(Q8%RCG`AWQQ1O(JYVRYK.jpg

整个TextView由两部分构成,一个是这个文本与上个文本之间的间距,第二个是文本自身的size。
为了使Text处于圆圈的中心,我们需要使用Paint类里面的FontMetrics方法,通过这个方法,我们可以获取跟文本有关的4个参数,top,bottom,ascent,descent。
在自定义Text的时候,系统会给文本自动设置一个基准线,如图中红线所示。如果不进行设置,系统将文本的位置自动调为基准线处。但是基准线不是中线,所以视觉上看,文本有偏上的感觉。我们需要将文本设在中线的位置,如图中绿线所示。
4个参数都是相对于基准线来计算的,top表示基准线到文本顶端的距离,bottom表示基准线到文本底端的距离,ascent表示基准线到文本自身顶端的距离,descent表示基准线到文本自身底端的距离。
还有一点需要注意的是,基准线上方的参数为负数,基准线下方的参数为正数。
由此可以计算出,需要下移的距离为space = (descent - ascent)/2 - descent

        val metrics =  TextPaint.fontMetrics
        var space = (metrics.descent-metrics.ascent)/2f - metrics.descent

7.修改后的onDraw方法为

override fun onDraw(canvas: Canvas?) {
        canvas?.drawCircle(cx,cy,radius,BackProgressCircle)
        canvas?.drawArc(mstrokeWidth,mstrokeWidth,width-mstrokeWidth,height-mstrokeWidth,
            -90f,360*Progress,false,ForeProgressCircle)
        var text = "${(Progress*100).toInt()}%"//文本内容为一个百分数
        val metrics =  TextPaint.fontMetrics
        var space = (metrics.descent-metrics.ascent)/2f - metrics.descent
        canvas?.drawText(text,cx,cy+space,TextPaint)
    }

最终效果图

安卓开发-用Kotlin语言自定义View实现百分比加载动画_第10张图片
1602840951140[00_00_01--00_00_05].gif

全部代码

1.MainActivity

package com.example.percentloadinganimator

import android.animation.ValueAnimator
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    private val ProgressPercentAnimotor:ValueAnimator by lazy {
        ValueAnimator.ofFloat(0f,1f).apply {
            duration = 2000
            addUpdateListener{
                percentLoading.Progress =it.animatedValue as Float//获取进度条属性的值
            }
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        StartAnimator.setOnClickListener {
            if(ProgressPercentAnimotor.isPaused){
                ProgressPercentAnimotor.resume()//如果当前动画是停止状态,则点击开始按钮重新运行
            }else{
                ProgressPercentAnimotor.start()
            }
        }
        StopAnimator.setOnClickListener {
            ProgressPercentAnimotor.pause()
        }
    }
}

2.PercentLoading

package com.example.percentloadinganimator

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View

class PercentLoading:View {
    constructor(context:Context):super(context)
    constructor(context: Context,attrs:AttributeSet):super(context,attrs){}
    private var cx = 0f//初始化到x轴的距离
    private var cy = 0f//初始化到y轴的距离
    private var radius = 0f//初始化圆的半径
    private val mstrokeWidth = 50f//初始化圆圈的宽度
    private var BackProgressCircle = Paint().apply {
        color = Color.GRAY//设置背景圆圈的颜色
        style = Paint.Style.STROKE//设置圆圈的风格为STROKE
        strokeWidth = mstrokeWidth//设置圆圈的宽度
    }//初始化绘制圆圈的画笔
     var Progress = 0f//初始化Progress
        set(value){
            field = value
            invalidate()
        }//通过field来更新Progress的值,并且通过invalidate来刷新
    private var ForeProgressCircle = Paint().apply {
        color = Color.GREEN//设置进度条的颜色
        style = Paint.Style.STROKE
        strokeWidth = mstrokeWidth
    }//初始化绘制进度条圆圈的画笔

    private var TextPaint = Paint().apply {
        color  = Color.BLACK
        style = Paint.Style.FILL
        textSize = 100f
        textAlign = Paint.Align.CENTER//字体的位置
    }//初始化绘制字体的画笔
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        cx = width*0.5f
        cy = height*0.5f
        //定位到控件的中心
        radius = Math.min(width,height)/2f-mstrokeWidth//取View长或宽的一半,再减去圆圈的宽度作为半径
        //这里必须要减去圆圈的宽度,否则会View中就显示不出来
    }
    override fun onDraw(canvas: Canvas?) {
        canvas?.drawCircle(cx,cy,radius,BackProgressCircle)
        canvas?.drawArc(mstrokeWidth,mstrokeWidth,width-mstrokeWidth,height-mstrokeWidth,
            -90f,360*Progress,false,ForeProgressCircle)
        var text = "${(Progress*100).toInt()}%"//文本内容为一个百分数
        val metrics =  TextPaint.fontMetrics
        var space = (metrics.descent-metrics.ascent)/2f - metrics.descent
        canvas?.drawText(text,cx,cy+space,TextPaint)
    }
}

3.MainActivity的xml文件



    
    

你可能感兴趣的:(安卓开发-用Kotlin语言自定义View实现百分比加载动画)