android绘图---绘制颜色渐变的圆圈进度条

本文介绍如何在Android中创建一个带有颜色渐变效果的圆形进度条。通过使用LinearGradient实现线性渲染,动态调整渐变颜色和位置,达到动画效果。代码示例详细展示了渐变颜色的设置以及在View的onSizeChanged和onDraw方法中应用动画的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.example.fox_draw_view;

/**
* @auth:foxcen
* @功能:画圆形进度条
*/
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
* 仿iphone带进度的进度条,线程安全的View,可直接在线程中更新进度
* @author xiaanming
*
*/
public class RoundProgressBar extends View {
/**
* 画笔对象的引用
*/
private Paint paint;

/**
 * 圆环的颜色
 */
private int roundColor;

/**
 * 圆环进度的颜色
 */
private int roundProgressColor;

/**
 * 中间进度百分比的字符串的颜色
 */
private int textColor;

/**
 * 中间进度百分比的字符串的字体
 */
private float textSize;

/**
 * 圆环的宽度
 */
private float roundWidth;

/**
 * 最大进度
 */
private int max;

/**
 * 当前进度
 */
private int progress;
/**
 * 是否显示中间的进度
 */
private boolean textIsDisplayable;

/**
 * 进度的风格,实心或者空心
 */
private int style;

public static final int STROKE = 0;
public static final int FILL = 1;

public RoundProgressBar(Context context) {
    this(context, null);
}


public RoundProgressBar(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public RoundProgressBar(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    paint = new Paint();



    TypedArray mTypedArray = context.obtainStyledAttributes(attrs,
            R.styleable.RoundProgressBar);

    //获取自定义属性和默认值
    roundColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundColor, Color.WHITE);
    roundProgressColor = mTypedArray.getColor(R.styleable.RoundProgressBar_roundProgressColor, Color.YELLOW);
    textColor = mTypedArray.getColor(R.styleable.RoundProgressBar_textColor, Color.WHITE);
    textSize = mTypedArray.getDimension(R.styleable.RoundProgressBar_textSize, 64);
    roundWidth = mTypedArray.getDimension(R.styleable.RoundProgressBar_roundWidth, 8);
    max = mTypedArray.getInteger(R.styleable.RoundProgressBar_max, 100);
    textIsDisplayable = mTypedArray.getBoolean(R.styleable.RoundProgressBar_textIsDisplayable, true);
    style = mTypedArray.getInt(R.styleable.RoundProgressBar_style, 0);

    mTypedArray.recycle();
}



@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    /**
     * 画最外层的大圆环
     */
    int centre = getWidth()/2; //获取圆心的x坐标
    int radius = (int) (centre - roundWidth/2); //圆环的半径
    paint.setColor(roundColor); //设置圆环的颜色
    paint.setStyle(Paint.Style.STROKE); //设置空心
    paint.setStrokeWidth(roundWidth); //设置圆环的宽度
    paint.setAntiAlias(true);  //消除锯齿 
    canvas.drawCircle(centre, centre, radius, paint); //画出圆环

    Log.e("log", centre + "");

    /**
     * 画进度百分比
     */
    paint.setStrokeWidth(0); 
    paint.setColor(textColor);
    paint.setTextSize(textSize);
    paint.setTypeface(Typeface.DEFAULT_BOLD); //设置字体
    int percent = (int)(((float)progress / (float)max) * 100);  //中间的进度百分比,先转换成float在进行除法运算,不然都为0
    float textWidth = paint.measureText("");   //测量字体宽度,我们需要根据字体的宽度设置在圆环中间

    if(textIsDisplayable && percent != 0 && style == STROKE){
        canvas.drawText("", centre - textWidth / 2, centre + textSize/2, paint); //画出进度百分比
    }


    /**
     * 画圆弧 ,画圆环的进度
     */

    //设置进度是实心还是空心
    paint.setStrokeWidth(roundWidth); //设置圆环的宽度
    paint.setColor(roundProgressColor);  //设置进度的颜色
    RectF oval = new RectF(centre - radius, centre - radius, centre
            + radius, centre + radius);  //用于定义的圆弧的形状和大小的界限
            Shader mShader=new  SweepGradient(centre, centre, context.getResources().getColor(R.color.start_color), context.getResources().getColor(R.color.end_color));

    switch (style) {
    case STROKE:{
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawArc(oval, -90, 360 * progress / max, false, paint);  //根据进度画圆弧
        break;
    }
    case FILL:{
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        if(progress !=0)
            canvas.drawArc(oval, -90, 360 * progress / max, true, paint);  //根据进度画圆弧
        break;
    }
    }
    setProgress(40);
}


public synchronized int getMax() {
    return max;
}


/**
 * 设置进度的最大值
 * @param max
 */
public synchronized void setMax(int max) {
    if(max < 0){
        throw new IllegalArgumentException("max not less than 0");
    }
    this.max = max;
}


/**
 * 获取进度.需要同步
 * @return
 */
public synchronized int getProgress() {
    return progress;
}


/**
 * 设置进度,此为线程安全控件,由于考虑多线的问题,需要同步
 * 刷新界面调用postInvalidate()能在非UI线程刷新
 * @param progress
 */
public synchronized void setProgress(int progress) {
    if(progress < 0){
        throw new IllegalArgumentException("progress not less than 0");
    }
    if(progress > max){
        progress = max;
    }
    if(progress <= max){
        this.progress = progress;
        postInvalidate();
    }

}


public int getCricleColor() {
    return roundColor;
}


public void setCricleColor(int cricleColor) {
    this.roundColor = cricleColor;
}


public int getCricleProgressColor() {
    return roundProgressColor;
}


public void setCricleProgressColor(int cricleProgressColor) {
    this.roundProgressColor = cricleProgressColor;
}


public int getTextColor() {
    return textColor;
}


public void setTextColor(int textColor) {
    this.textColor = textColor;
}


public float getTextSize() {
    return textSize;
}


public void setTextSize(float textSize) {
    this.textSize = textSize;
}


public float getRoundWidth() {
    return roundWidth;
}


public void setRoundWidth(float roundWidth) {
    this.roundWidth = roundWidth;
}

}

知识点1:
就像上面的显示效果一样一束白光闪过,这种效果主要还是使用了LinearGradient类来进行的

LinearGradient也称作线性渲染,LinearGradient的作用是实现某一区域内颜色的线性渐变效果

它有两个构造函数
代码如下 复制代码
public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)

其中,参数x0表示渐变的起始点x坐标;参数y0表示渐变的起始点y坐标;参数x1表示渐变的终点x坐标;参数y1表示渐变的终点y坐标 ;color0表示渐变开始颜色;color1表示渐变结束颜色;参数tile表示平铺方式。

Shader.TileMode有3种参数可供选择,分别为CLAMP、REPEAT和MIRROR:

CLAMP的作用是如果渲染器超出原始边界范围,则会复制边缘颜色对超出范围的区域进行着色

REPEAT的作用是在横向和纵向上以平铺的形式重复渲染位图

MIRROR的作用是在横向和纵向上以镜像的方式重复渲染位图

public LinearGradient (float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile);

其中,参数x0表示渐变的起始点x坐标;参数y0表示渐变的起始点y坐标;参数x1表示渐变的终点x坐标;参数y1表示渐变的终点y坐标;参数colors表示渐变的颜色数组;参数positions用来指定颜色数组的相对位置;参数tile表示平铺方式。通常,参数positions设为null,表示颜色数组以斜坡线的形式均匀分布。

下面这段代码是直接从git上面的项目拷贝下来的
代码如下 复制代码

package com.example.shimmer;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.widget.TextView;

public class MyTextView extends TextView {

private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private Paint mPaint;
private int mViewWidth = 0;
private int mTranslate = 0;

private boolean mAnimating = true;

public MyTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    if (mViewWidth == 0) {
        mViewWidth = getMeasuredWidth();
        if (mViewWidth > 0) {
            mPaint = getPaint();
            mLinearGradient = new LinearGradient(-mViewWidth, 0, 0, 0,
                    new int[] { 0x33ffffff, 0xffffffff, 0x33ffffff },
                    new float[] { 0, 0.5f, 1 }, Shader.TileMode.CLAMP);
            mPaint.setShader(mLinearGradient);
            mGradientMatrix = new Matrix();
        }
    }
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (mAnimating && mGradientMatrix != null) {
        mTranslate += mViewWidth / 10;
        if (mTranslate > 2 * mViewWidth) {
            mTranslate = -mViewWidth;
        }
        mGradientMatrix.setTranslate(mTranslate, 0);
        mLinearGradient.setLocalMatrix(mGradientMatrix);
        postInvalidateDelayed(50);
    }
}

}

这段代码主要是分两步:一个是在onSizeChanged()即大小发生改变的时候,另外一个是onDraw()主要是用来做动画的效果的,

首先我们先来onSizeChanged()里面的代码,在这段代码中主要是定义了LinearGradient:
代码如下 复制代码

mLinearGradient = new LinearGradient(-mViewWidth, 0, 0, 0, new int[] { 0x33ffffff, 0xffffffff, 0x33ffffff },new float[] { 0, 0.5f, 1 }, Shader.TileMode.CLAMP);

这段代码可以这么理解,它定义了一组渐变的数值是{ 0x33ffffff, 0xffffffff, 0x33ffffff },这组数值分别在相对应的0,0.5,1中显示,0位置对应0x33ffffff颜色,0.5位置对应0xffffffff,1位置对应0x33ffffff,这个渐变的初始位置是在手机屏幕的外面x=(-mViewWidth,0)就是屏幕外面

最后来看一下这个onDraw()方法里面是如何做动画的
代码如下 复制代码

mTranslate += mViewWidth / 10;很简单表示每一次运动的递增值

    if (mTranslate > 2 * mViewWidth) {
        mTranslate = -mViewWidth;
    }
### 关于uni-app中实现圆形进度条 在uni-app开发过程中,可以利用Vue的组件化特性来创建自定义的圆形进度条组件。通过结合CSS样式和Canvas绘图功能,或者借助第三方图表库(如ECharts、UCharts),可以轻松实现圆形进度条的效果。 以下是两种常见的方法: #### 方法一:纯CSS与HTML实现 这种方法适用于简单的圆形进度条效果,适合不需要动态数据更新的场景。 ```html <template> <view class="progress-circle"> <view :style="{ '--percentage': percentage }" class="circle"></view> <text>{{ percentage }}%</text> </view> </template> <script> export default { data() { return { percentage: 75, // 进度百分比 }; }, }; </script> <style scoped> .progress-circle { position: relative; width: 100px; height: 100px; } .circle { --size: 100px; /* 圆形大小 */ --stroke-width: 10px; /* 边框宽度 */ --color-primary: #4caf50; /* 主颜色 */ --color-secondary: #e0e0e0; /* 背景颜色 */ position: absolute; top: 0; left: 0; width: var(--size); height: var(--size); background: conic-gradient(var(--color-primary) calc(var(--percentage) * 1%), var(--color-secondary) 0%); border-radius: 50%; } .text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 16px; font-weight: bold; } </style> ``` 此代码片段展示了如何使用CSS变量`--percentage`控制圆环的颜色渐变,从而模拟进度变化[^1]。 --- #### 方法二:基于Canvas绘制 对于更复杂的交互需求,可以通过Canvas API手动绘制圆形进度条。 ```html <template> <view> <canvas canvas-id="progressCircle" id="progressCircle" style="width: 200px; height: 200px;"></canvas> </view> </template> <script> export default { onReady() { const ctx = uni.createCanvasContext('progressCircle'); this.drawProgress(ctx, 75); // 设置初始进度为75% }, methods: { drawProgress(ctx, progress) { const centerX = 100; const centerY = 100; const radius = 80; // 清除画布 ctx.clearRect(0, 0, 200, 200); // 绘制背景圆圈 ctx.setStrokeStyle('#e0e0e0'); // 背景色 ctx.setLineWidth(10); ctx.beginPath(); ctx.arc(centerX, centerY, radius, 0, Math.PI * 2, false); ctx.stroke(); // 绘制进度圆弧 ctx.strokeStyle = '#4caf50'; // 前景色 const endAngle = (Math.PI * 2 * progress) / 100; ctx.beginPath(); ctx.arc(centerX, centerY, radius, -Math.PI / 2, endAngle - Math.PI / 2, false); ctx.stroke(); // 展示当前进度文字 ctx.setFontSize(20); ctx.setFillStyle('#333'); ctx.setTextAlign('center'); ctx.fillText(`${progress}%`, centerX, centerY + 5); ctx.draw(); }, }, }; </script> ``` 这段代码实现了通过Canvas绘制一个带有动态文本显示的圆形进度条[^1]。 --- #### 使用第三方插件(推荐) 如果项目中有更多图形展示需求,建议引入成熟的图表库,例如 **UCharts** 或 **ECharts for Uni-app**。这些工具提供了丰富的API接口,能够快速构建高质量的可视化界面。 以下是一个基于UCharts的例子: ```javascript import uCharts from '@/components/u-charts/u-charts.js'; const _self = this; _self.chartData = { series: [ { name: '已完成', data: 75 }, // 已完成部分占比 { name: '未完成', data: 25 }, // 未完成部分占比 ], }; // 初始化图表 new uCharts({ context: _self, canvasId: 'progressChart', type: 'ring', legend: true, fontSize: 11, title: '', subtitle: '', extra: { ring: { activeBgColor: ['#4CAF50', '#FFC107'], borderWidth: 10, }, }, }); ``` 以上代码片段演示了如何配置UCharts生成一个饼状图形式的圆形进度条[^2]。 --- ### 总结 无论是通过原生CSS/Canvas还是依赖外部库的方式,在uni-app中都可以灵活实现圆形进度条的功能。具体选择取决于项目的复杂程度和个人偏好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值