Android实现绚丽的自定义进度条

 更新时间:2022年01月20日 14:15:18   作者:吐尔洪江Coding  
进度条是在Android项目中很常用的组件之一,本文将为大家详细地介绍一下自定义进度条的实现过程。感兴趣的小伙伴可以跟随小编一起学习一下

前言

进度条是在Android项目中很常用的组件之一,如果想要理解它是怎么实现的,首先还需要了解自定义view和Android坐标系相关的知识,下面我来详细地介绍一下自定义进度条的实现过程。

本项目源码:https://gitee.com/tu_erhongjiang/android-progress-bar

效果图

实现步骤

1.绘制背景圆形矩形

首先要画出一个圆形矩形,RectF里面传递的是矩形左上角和右下角的xy坐标,这是用来确定矩形的位置和大小,然后在矩形内部画出一个圆形矩形。

核心代码:canvas.drawRoundRect()

private void drawBackground(Canvas canvas){
   //圆角矩形
    RectF rectF = new RectF(padding, padding, mWidth - padding, mHeight - padding);
    canvas.drawRoundRect(rectF, round, round, mPaintRoundRect);
}

2.绘制进度

其实里面的进度条也是圆形矩形,只不过进度条的画笔是实心的。内部进度条矩形的大小需要略小于外面的矩形,这样就可以实现上面的这种效果。如果进度条矩形大于或等于背景矩形大小的话那就完全压住背景中的边框,显示出来的只是一个没有边框的进度条,所以这里需要减掉strokeWidth。

private void drawProgress(Canvas canvas){
        if (process!=0){
            RectF rectProgress = new RectF(padding + strokeWidth, padding + strokeWidth, process, mHeight - padding - strokeWidth);//内部进度条
            canvas.drawRoundRect(rectProgress, round, round, mPaint);
        }
    }

3.绘制文字

下面来看看怎么居中文字:

getWidth() / 2 得到的结果是中间位置的x坐标,但是从这里开始绘制文字的话不能实现居中的效果。所以还需要计算出文字的长度,然后把文字整体左移。mTxtWidth / 2 是文字的中心位置,也就是说文字的中心位置移到矩形的中心位置就可以实现居中的效果。

    private void updateText(Canvas canvas) {
        String finishedText = getResources().getString(R.string.finished);
        String defaultText = getResources().getString(R.string.defaultText);
        int percent = (int) (process / (mWidth - padding - strokeWidth) * 100);
        Paint.FontMetrics fm = mPaintText.getFontMetrics();
        int mTxtWidth = (int) mPaintText.measureText(finishedText, 0, defaultText.length());
        int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
        int x = getWidth() / 2 - mTxtWidth / 2; //文字在画布中的x坐标
        int y = getHeight() / 2 + mTxtHeight / 4; //文字在画布中的y坐标
        if (percent < 100) {
            canvas.drawText(percent + "%", x, y, mPaintText);
        } else {
            canvas.drawText(finishedText, x, y, mPaintText);
        }
    }

4.加入动画 

最后就是加入动画效果了,让进度条动起来。我这里用到的是属性动画中的valueAnimator。这种动画不能直接修改view,它是类似于timer,需要我们传递一个数值范围和执行时间。比如说3秒内从1加到100。然后在接口回调时拿到当前的进度,执行view的invalidate()方法,刷新UI。

 //属性动画
    public void start() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mWidth - padding - strokeWidth);
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(animation -> {
            process = (float) animation.getAnimatedValue();
            invalidate();
        });
        valueAnimator.start();
    }

完整代码

package com.example.floatingwindow.widget;
 
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
 
import androidx.annotation.Nullable;
 
import com.example.floatingwindow.R;
 
public class HorizontalProgressView extends View {
 
    private Paint mPaint;
    private Paint mPaintRoundRect;
    private Paint mPaintText;
    private int mWidth;
    private int mHeight;
    private int padding = 5;
    private int strokeWidth = 8;
    private int textSize = 15;
    private long duration = 3500;
    private int round;
    private float process;
 
    public HorizontalProgressView(Context context) {
        super(context);
        init();
    }
 
    public HorizontalProgressView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }
 
    public HorizontalProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
 
    //初始化画笔
    private void init(){
        mPaintRoundRect = new Paint();//圆角矩形
        mPaintRoundRect.setColor(getResources().getColor(R.color.back));//圆角矩形颜色
        mPaintRoundRect.setAntiAlias(true);// 抗锯齿效果
        mPaintRoundRect.setStyle(Paint.Style.STROKE);//设置画笔样式
        mPaintRoundRect.setStrokeWidth(strokeWidth);//设置画笔宽度
 
        mPaint = new Paint();
        mPaint.setColor(getResources().getColor(R.color.inner));
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(strokeWidth);
 
        mPaintText = new Paint();
        mPaintText.setAntiAlias(true);
        mPaintText.setStyle(Paint.Style.FILL);
        mPaintText.setColor(getResources().getColor(R.color.back));
        mPaintText.setTextSize(sp2px(textSize));
    }
 
    public void setPadding(int padding) {
        this.padding = padding;
    }
 
    public void setStrokeWidth(int strokeWidth) {
        this.strokeWidth = strokeWidth;
    }
 
    public void setTextSize(int textSize) {
        this.textSize = textSize;
    }
 
    public void setDuration(long duration) {
        this.duration = duration;
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        //MeasureSpec.EXACTLY,精确尺寸
        if (widthSpecMode == MeasureSpec.EXACTLY || widthSpecMode == MeasureSpec.AT_MOST) {
            mWidth = widthSpecSize;
        } else {
            mWidth = 0;
        }
        //MeasureSpec.AT_MOST,最大尺寸,只要不超过父控件允许的最大尺寸即可,MeasureSpec.UNSPECIFIED未指定尺寸
        if (heightSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.UNSPECIFIED) {
            mHeight = defaultHeight();
        } else {
            mHeight = heightSpecSize;
        }
        //设置控件实际大小
        round = mHeight / 2;//圆角半径
        setMeasuredDimension(mWidth, mHeight);
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBackground(canvas);//绘制背景矩形
        drawProgress(canvas);//绘制进度
        updateText(canvas);//处理文字
    }
 
    private void drawBackground(Canvas canvas){
        RectF rectF = new RectF(padding, padding, mWidth - padding, mHeight - padding);//圆角矩形
        canvas.drawRoundRect(rectF, round, round, mPaintRoundRect);
    }
 
    private void drawProgress(Canvas canvas){
        if (process!=0){
            RectF rectProgress = new RectF(padding + strokeWidth, padding + strokeWidth, process, mHeight - padding - strokeWidth);//内部进度条
            canvas.drawRoundRect(rectProgress, round, round, mPaint);
        }
    }
 
    private void updateText(Canvas canvas) {
        String finishedText = getResources().getString(R.string.finished);
        String defaultText = getResources().getString(R.string.defaultText);
        int percent = (int) (process / (mWidth - padding - strokeWidth) * 100);
        Paint.FontMetrics fm = mPaintText.getFontMetrics();
        int mTxtWidth = (int) mPaintText.measureText(finishedText, 0, defaultText.length());
        int mTxtHeight = (int) Math.ceil(fm.descent - fm.ascent);
        int x = getWidth() / 2 - mTxtWidth / 2; //文字在画布中的x坐标
        int y = getHeight() / 2 + mTxtHeight / 4; //文字在画布中的y坐标
        if (percent < 100) {
            canvas.drawText(percent + "%", x, y, mPaintText);
        } else {
            canvas.drawText(finishedText, x, y, mPaintText);
        }
    }
 
    //属性动画
    public void start() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mWidth - padding - strokeWidth);
        valueAnimator.setDuration(duration);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(animation -> {
            process = (float) animation.getAnimatedValue();
            invalidate();
        });
        valueAnimator.start();
    }
 
    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp,
                getResources().getDisplayMetrics());
    }
 
    //进度条默认高度,未设置高度时使用
    private int defaultHeight() {
        float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (20 * scale + 0.5f * (20 >= 0 ? 1 : -1));
    }
 
}

以上就是横向进度条的实现步骤,整体来说还是比较简单的,如果你对Android坐标系和canvas比较熟悉的话自定义view实现起来还是很容易的。

到此这篇关于Android实现绚丽的自定义进度条的文章就介绍到这了,更多相关Android自定义进度条内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Android实现录音监听动画的示例代码

    Android实现录音监听动画的示例代码

    在很多app种内置了语音助手,也存在各种动画,这篇文章主要为大家详细介绍了Android实现录音监听动画的示例代码,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-12-12
  • 在Android中动态添加Panel框架的实现代码

    在Android中动态添加Panel框架的实现代码

    项目经常会有这种需求,就是想动态的在某个界面上添加一个Panel。比如,有一个按钮,点击后会显示下拉菜单式的界面。这种需求,就属于动态添加一个Panel。需求多了,就要研究是否可以抽象出通用的框架代码,以方便开发,所以就有了以下内容
    2013-05-05
  • 解决Android SDK下载和更新失败的方法详解

    解决Android SDK下载和更新失败的方法详解

    本篇文章是对Android SDK下载和更新失败的解决方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • Convert WebP to PNG using java

    Convert WebP to PNG using java

    本文主要介绍Convert WebP to PNG using java,这里对 WebP 做了详细说明,并讲解Linux 环境下WebP 转png格式的示例,有兴趣的小伙伴可以参考下
    2016-08-08
  • AndroidStudio中重载方法@Override的使用详解

    AndroidStudio中重载方法@Override的使用详解

    这篇文章主要介绍了AndroidStudio中重载方法@Override的使用详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-04-04
  • Android开发之串口编程原理和实现方式

    Android开发之串口编程原理和实现方式

    提到串口编程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符类:FileDescriptor;下面我分别对JNI、FileDescriptor以及串口的一些知识点和实现的源码进行分析说明,感兴趣的朋友可以了解下
    2013-01-01
  • Android实现压缩字符串的方法示例

    Android实现压缩字符串的方法示例

    最近在做Android开发,遇到了需要压缩字符串的功能,下面这篇文章主要给大家介绍了Android实现压缩字符串的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面跟着小编来一起学习学习吧。
    2017-08-08
  • 详解Android 7.0 Settings 加载选项

    详解Android 7.0 Settings 加载选项

    本篇文章主要介绍了Android 7.0 Settings 加载选项,Android 7.0 Settings顶部多了一个建议选项,多了个侧边栏,操作更加便捷了,有兴趣的可以了解一下。
    2017-02-02
  • ListView的Adapter使用 之 初学ArrayAdapter String

    ListView的Adapter使用 之 初学ArrayAdapter String

    ListView是Android中经常会使用的东西,绑定数据对于初学者来说,尤其是刚接触编程的人来说,往往会觉得很难理解,我上大二的时候学的java,但是基本上相当于没有学,什么都没写过,真正接触编程就是开始上手学android,把这些记录下来,自己可以回头看下,也可以让新手更好的理解
    2013-06-06
  • android dialog背景模糊化效果实现方法

    android dialog背景模糊化效果实现方法

    这篇文章主要为大家详细介绍了android dialog背景模糊化效果的实现方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04

最新评论