Android进阶之路 - 静态会员进度条

news2025/1/7 10:43:45

年后这个新版本加入了VIP模块,有幸正好由我来负责,可以再积累一下这方面的知识。

那段时间看了一本书,书中说到初级码农的特性之一就是完全集中于某些功能,忽略了了很多成长机会,所以重复性劳作带来的成长值有限,大家应该去接触更广、更深的内容

进度条Blog

  • Android进阶之路 - 静态等级进度条
  • Android进阶之路 - 静态会员进度条

静态进度条一般只用于实现显示效果,无其他任何交互行为
在这里插入图片描述

年过半许

    • 基础概要
      • 功能分析
      • 组件解析
    • 开发实践
      • 自定义属性
      • 自定义控件
      • 静态使用方式
    • 项目场景
      • 圆角尺寸与设计图不符?
      • 设置对应进度值后,显示异常?

基础概要

起初想着自己写一下自定义控件就行,但是通过查询相关控件后发现自己考虑还是有限,所以直接借鉴了早期前辈写的控件,毕竟考虑比我当前全面,功能扩展也多一些

功能分析

在这里插入图片描述

要实现类似进度条,起码有以下几点要考虑到

  • 需绘制双进度条:一条 width 默认为控件宽度,用于背景效果;一条 width 需进行计算,实时显示当前进度
  • 进度条计算:计算的规则至少需要当前进度值最大值,否则无法进行基础计算
  • 双进度条配置不同:对应 Paint 画笔,除大部分配置相同外,颜色至少要不同
  • padding考虑:有些控件为了显示效果更佳,会加入padding设置,这时候计算widthheight时要考虑到padding尺寸

通过以上简单分析,我们可以分析出我们需要的一些基本自定义属性,例如

  • 当前成长值、最大成长值
  • 背景进度条颜色、当前进度条颜色

组件解析

看了不少进度条控件,最后还是选了 ZzHorizontalProgressBar ,虽然并不是多么出名,但自己用的舒服就好,下面简单介绍一下我对这款控件的理解

作用范围

  • 支持 静态设置、动态设置
  • 支持 三种显示模式,矩形,圆角形等
  • 支持 自定义圆角大小
  • 支持 渐变色
  • 支持 双进度条
  • 支持 成长值回调
  • 支持 设置进度条边框、边框色

属性说明

在这里插入图片描述

有兴趣的主要看这部分方法的实现就好,对应的分别是绘制进度条背景、绘制当前进度条、绘制边框

在这里插入图片描述

以矩形为例,一起看看 绘制进度条背景、绘制当前进度条

绘制进度条背景实现简单,可以简单了解

在这里插入图片描述

绘制当前进度条,除了公共部分,可以直接看else部分(如果有渐变需求的可以看看 if 部分)

在这里插入图片描述


开发实践

为了减少外部引用带来的影响,我将 ZzHorizontalProgressBar 的核心部分 copy 了出来 ,主要有自定义属性+自定义控件,为了方便 Demo 效果采用了静态设置方式,建议自行根据开发场景选择对应方式(在实际项目中我是结合静态+动态的方式来实现效果)

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ZzHorizontalProgressBar">
        <attr name="zpb_padding" format="dimension" />
        <attr name="zpb_bg_color" format="color|reference" />
        <attr name="zpb_pb_color" format="color|reference" />
        <attr name="zpb_second_pb_color" format="color|reference" />
        <attr name="zpb_max" format="integer" />
        <attr name="zpb_progress" format="integer" />
        <attr name="zpb_show_zero_point" format="boolean" />
        <attr name="zpb_show_second_progress" format="boolean" />
        <attr name="zpb_second_progress" format="integer" />
        <attr name="zpb_show_second_point_shape" format="enum">
            <enum name="point" value="0"/>
            <enum name="line" value="1"/>
        </attr>
        <attr name="zpb_open_gradient" format="boolean" />
        <attr name="zpb_gradient_from" format="color|reference" />
        <attr name="zpb_gradient_to" format="color|reference" />
        <attr name="zpb_open_second_gradient" format="boolean" />
        <attr name="zpb_second_gradient_from" format="color|reference" />
        <attr name="zpb_second_gradient_to" format="color|reference" />
        <attr name="zpb_show_mode" format="enum" >
            <enum name="round" value="0"/>
            <enum name="rect" value="1"/>
            <enum name="round_rect" value="2"/>
        </attr>
        <attr name="zpb_round_rect_radius" format="dimension|reference"/>
        <attr name="zpb_draw_border" format="boolean"/>
        <attr name="zpb_border_width" format="dimension|reference"/>
        <attr name="zpb_border_color" format="color|reference"/>

    </declare-styleable>
</resources>

自定义控件

为表尊重,保持作者原始注释

Tip:因类内有小千行代码,建议使用者直接copy该类

package com.example.lineprogress;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * 水平进度条
 *
 * Created by 周卓 on 2016/9/22.
 */
public class ZzHorizontalProgressBar extends View {

    public static final int SHOW_MODE_ROUND = 0;
    public static final int SHOW_MODE_RECT = 1;
    public static final int SHOW_MODE_ROUND_RECT = 2;

    public static final int SHAPE_POINT = 0;
    public static final int SHAPE_LINE = 1;

    private int mMax;
    private int mProgress;
    private int mBgColor;
    private int mProgressColor;
    private int mPadding;
    private boolean mOpenGradient;
    private int mGradientFrom;
    private int mGradientTo;
    private boolean mShowSecondProgress;
    private int mSecondProgress;
    private int mSecondProgressShape;
    private boolean mShowZeroPoint;

    private Paint mSecondProgressPaint;
    private Paint mSecondGradientPaint;
    private Paint mProgressPaint;
    private Paint mGradientPaint;
    private Paint mBgPaint;
    private boolean mOpenSecondGradient;
    private int mSecondGradientFrom;
    private int mSecondGradientTo;
    private int mSecondProgressColor;

    private int mRadius;
    private boolean mDrawBorder = false;
    private int mBorderColor;
    private int mBorderWidth;

    private int mShowMode = 0;
    private Paint mBorderPaint;

    @IntDef({SHOW_MODE_ROUND, SHOW_MODE_RECT, SHOW_MODE_ROUND_RECT})
    @Target({ElementType.PARAMETER, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    private @interface ShowMode {
    }

    @IntDef({SHAPE_POINT, SHAPE_LINE})
    @Target({ElementType.PARAMETER, ElementType.METHOD})
    @Retention(RetentionPolicy.SOURCE)
    private @interface SecondProgressShape {
    }

    private OnProgressChangedListener mOnProgressChangedListener;

    public interface OnProgressChangedListener {
        void onProgressChanged(ZzHorizontalProgressBar progressBar, int max, int progress);

        void onSecondProgressChanged(ZzHorizontalProgressBar progressBar, int max, int progress);
    }

    public ZzHorizontalProgressBar(Context context) {
        super(context);
        init(context, null);
    }

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

    public ZzHorizontalProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        initAttrs(context, attrs);
        initPaths();
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZzHorizontalProgressBar);
        mMax = a.getInteger(R.styleable.ZzHorizontalProgressBar_zpb_max, 100);
        mProgress = a.getInteger(R.styleable.ZzHorizontalProgressBar_zpb_progress, 0);
        mBgColor = a.getColor(R.styleable.ZzHorizontalProgressBar_zpb_bg_color, 0xff3F51B5);
        mProgressColor = a.getColor(R.styleable.ZzHorizontalProgressBar_zpb_pb_color, 0xffFF4081);
        mSecondProgressColor = a.getColor(R.styleable.ZzHorizontalProgressBar_zpb_second_pb_color, 0xffFF4081);
        mPadding = a.getDimensionPixelSize(R.styleable.ZzHorizontalProgressBar_zpb_padding, 0);
        mShowZeroPoint = a.getBoolean(R.styleable.ZzHorizontalProgressBar_zpb_show_zero_point, false);
        mShowSecondProgress = a.getBoolean(R.styleable.ZzHorizontalProgressBar_zpb_show_second_progress, false);
        mSecondProgress = a.getInteger(R.styleable.ZzHorizontalProgressBar_zpb_second_progress, 0);
        mSecondProgressShape = a.getInteger(R.styleable.ZzHorizontalProgressBar_zpb_show_second_point_shape, SHAPE_POINT);
        mOpenGradient = a.getBoolean(R.styleable.ZzHorizontalProgressBar_zpb_open_gradient, false);
        mGradientFrom = a.getColor(R.styleable.ZzHorizontalProgressBar_zpb_gradient_from, 0xffFF4081);
        mGradientTo = a.getColor(R.styleable.ZzHorizontalProgressBar_zpb_gradient_to, 0xffFF4081);
        mOpenSecondGradient = a.getBoolean(R.styleable.ZzHorizontalProgressBar_zpb_open_second_gradient, false);
        mShowMode = a.getInt(R.styleable.ZzHorizontalProgressBar_zpb_show_mode, SHOW_MODE_ROUND);
        mSecondGradientFrom = a.getColor(R.styleable.ZzHorizontalProgressBar_zpb_second_gradient_from, 0xffFF4081);
        mSecondGradientTo = a.getColor(R.styleable.ZzHorizontalProgressBar_zpb_second_gradient_to, 0xffFF4081);
        mRadius = a.getDimensionPixelSize(R.styleable.ZzHorizontalProgressBar_zpb_round_rect_radius, 20);
        mDrawBorder = a.getBoolean(R.styleable.ZzHorizontalProgressBar_zpb_draw_border, false);
        mBorderWidth = a.getDimensionPixelSize(R.styleable.ZzHorizontalProgressBar_zpb_border_width, 1);
        mBorderColor = a.getColor(R.styleable.ZzHorizontalProgressBar_zpb_border_color, 0xffff001f);
        a.recycle();
    }

    private void initPaths() {
        //常规进度条效果
        mProgressPaint = new Paint();
        mProgressPaint.setColor(mProgressColor);
        mProgressPaint.setStyle(Paint.Style.FILL);
        mProgressPaint.setAntiAlias(true);

        mSecondProgressPaint = new Paint();
        mSecondProgressPaint.setColor(mSecondProgressColor);
        mSecondProgressPaint.setStyle(Paint.Style.FILL);
        mSecondProgressPaint.setAntiAlias(true);

        //渐变效果
        mGradientPaint = new Paint();
        mGradientPaint.setStyle(Paint.Style.FILL);
        mGradientPaint.setAntiAlias(true);

        mSecondGradientPaint = new Paint();
        mSecondGradientPaint.setStyle(Paint.Style.FILL);
        mSecondGradientPaint.setAntiAlias(true);

        //背景效果
        mBgPaint = new Paint();
        mBgPaint.setColor(mBgColor);
        mBgPaint.setStyle(Paint.Style.FILL);
        mBgPaint.setAntiAlias(true);

        mBorderPaint = new Paint();
        mBorderPaint.setColor(mBorderColor);
        mBorderPaint.setStyle(Paint.Style.STROKE);
        mBorderPaint.setStrokeWidth(mBorderWidth);
        mBorderPaint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switch (mShowMode) {
            case SHOW_MODE_ROUND:
                //half circle
                drawBackgroundCircleMode(canvas);
                drawProgressCircleMode(canvas);
                drawBorderCircleMode(canvas);
                break;
            case SHOW_MODE_RECT:
                //rect
                drawBackgroundRectMode(canvas);
                drawProgressRectMode(canvas);
                drawBorderRectMode(canvas);
                break;
            case SHOW_MODE_ROUND_RECT:
                //custom radius
                drawBackgroundRoundRectMode(canvas);
                drawProgressRoundRectMode(canvas);
                drawBorderRoundRect(canvas);
                break;
        }
    }

    /**
     * 绘制半圆形进度
     */
    private void drawProgressCircleMode(Canvas canvas) {
        int width = getWidth();
        float percent = 0;
        if (mMax != 0) {
            percent = mProgress * 1.0f / mMax;
        }
        int progressHeight = getHeight() - mPadding * 2;
        if (mOpenGradient) {
            int progressWidth = width - mPadding * 2;
            float dx = progressWidth * percent;

            int[] colors = new int[2];
            float[] positions = new float[2];
            //from color
            colors[0] = mGradientFrom;
            positions[0] = 0;
            //to color
            colors[1] = mGradientTo;
            positions[1] = 1;
            LinearGradient shader = new LinearGradient(
                    mPadding + progressHeight / 2.0f, mPadding, mPadding + progressHeight / 2.0f + dx, mPadding + progressHeight,
                    colors,
                    positions,
                    Shader.TileMode.MIRROR);
            //gradient
            mGradientPaint.setShader(shader);

            int radius = width > getHeight() ? getHeight() / 2 : width / 2;
            if (dx < getHeight()) {
                //left circle
                if (mProgress == 0) {
                    if (mShowZeroPoint) {
                        canvas.drawCircle(mPadding + progressHeight / 2.0f, mPadding + progressHeight / 2.0f, progressHeight / 2.0f, mGradientPaint);
                    }
                } else {
                    canvas.drawCircle(mPadding + progressHeight / 2.0f, mPadding + progressHeight / 2.0f, progressHeight / 2.0f, mGradientPaint);
                }

            } else {
                //progress line
                RectF rectF = new RectF(mPadding, mPadding, mPadding + dx, mPadding + progressHeight);
                canvas.drawRoundRect(rectF, radius, radius, mGradientPaint);
            }

        } else {
            int progressWidth = width - mPadding * 2 - progressHeight;
            float dx = progressWidth * percent;
            mProgressPaint.setColor(mProgressColor);
            float left = mPadding + progressHeight / 2.0f;
            //left circle
            if (mProgress == 0) {
                if (mShowZeroPoint) {
                    canvas.drawCircle(left, left, progressHeight / 2.0f, mProgressPaint);
                }
            } else {
                canvas.drawCircle(left, left, progressHeight / 2.0f, mProgressPaint);
            }
            //right circle
            if (mProgress == 0) {
                if (mShowZeroPoint) {
                    canvas.drawCircle(left + dx, left, progressHeight / 2.0f, mProgressPaint);
                }
            } else {
                canvas.drawCircle(left + dx, left, progressHeight / 2.0f, mProgressPaint);
            }
            //middle line
            RectF rectF = new RectF(left, mPadding, left + dx, mPadding + progressHeight);
            canvas.drawRect(rectF, mProgressPaint);
        }

        //draw second progress
        if (mShowSecondProgress) {
            float secondPercent = 0;
            if (mMax != 0) {
                secondPercent = mSecondProgress * 1.0f / mMax;
            }
            int secondProgressHeight = getHeight() - mPadding * 2;
            if (mOpenSecondGradient) {
                int secondProgressWidth = width - mPadding * 2;
                float secondDx = secondProgressWidth * secondPercent;

                int[] secondColors = new int[2];
                float[] secondPositions = new float[2];
                //from color
                secondColors[0] = mSecondGradientFrom;
                secondPositions[0] = 0;
                //to color
                secondColors[1] = mSecondGradientTo;
                secondPositions[1] = 1;
                LinearGradient secondShader = new LinearGradient(
                        mPadding + secondProgressHeight / 2.0f, mPadding, mPadding + secondProgressHeight / 2.0f + secondDx, mPadding + secondProgressHeight,
                        secondColors,
                        secondPositions,
                        Shader.TileMode.MIRROR);
                //gradient
                mSecondGradientPaint.setShader(secondShader);

                int secondRadius = width > getHeight() ? getHeight() / 2 : width / 2;
                if (secondDx < getHeight()) {
                    //left circle
                    if (mSecondProgress == 0) {
                        if (mShowZeroPoint) {
                            canvas.drawCircle(mPadding + secondProgressHeight / 2.0f, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondGradientPaint);
                        }
                    } else {
                        canvas.drawCircle(mPadding + secondProgressHeight / 2.0f, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondGradientPaint);
                    }
                } else {
                    //progress line
                    RectF rectF = new RectF(mPadding, mPadding, mPadding + secondDx, mPadding + secondProgressHeight);
                    canvas.drawRoundRect(rectF, secondRadius, secondRadius, mSecondGradientPaint);
                }
            } else {
                //no gradient
                if (mSecondProgressShape == 0) {
                    //point shape
                    int secondProgressWidth = width - mPadding * 2;
                    float secondDx = secondProgressWidth * secondPercent;
                    //progress line
                    float px = mPadding + secondProgressHeight / 2.0f + secondDx;
                    if (px < width - mPadding - secondProgressHeight / 2.0f) {
                        if (mSecondProgress == 0) {
                            if (mShowZeroPoint) {
                                canvas.drawCircle(px, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondProgressPaint);
                            }
                        } else {
                            canvas.drawCircle(px, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondProgressPaint);
                        }
                    } else {
                        canvas.drawCircle(px - secondProgressHeight, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondProgressPaint);
                    }

                } else {
                    //line shape
                    int secondProgressWidth = width - mPadding * 2 - secondProgressHeight;
                    float dx = secondProgressWidth * secondPercent;
                    mSecondProgressPaint.setColor(mSecondProgressColor);
                    //left circle
                    if (mSecondProgress == 0) {
                        if (mShowZeroPoint) {
                            canvas.drawCircle(mPadding + secondProgressHeight / 2.0f, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondProgressPaint);
                        }
                    } else {
                        canvas.drawCircle(mPadding + secondProgressHeight / 2.0f, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondProgressPaint);
                    }
                    //right circle
                    if (mSecondProgress == 0) {
                        if (mShowZeroPoint) {
                            canvas.drawCircle(mPadding + secondProgressHeight / 2.0f + dx, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondProgressPaint);
                        }
                    } else {
                        canvas.drawCircle(mPadding + secondProgressHeight / 2.0f + dx, mPadding + secondProgressHeight / 2.0f, secondProgressHeight / 2.0f, mSecondProgressPaint);
                    }
                    //middle line
                    RectF rectF = new RectF(mPadding + secondProgressHeight / 2.0f, mPadding, mPadding + secondProgressHeight / 2.0f + dx, mPadding + secondProgressHeight);
                    canvas.drawRect(rectF, mSecondProgressPaint);
                }
            }
        }

    }

    /**
     * 绘制方形进度
     */
    private void drawProgressRectMode(Canvas canvas) {
        int width = getWidth();
        float percent = 0;
        if (mMax != 0) {
            percent = mProgress * 1.0f / mMax;
        }
        int progressHeight = getHeight() - mPadding * 2;
        if (mOpenGradient) {
            int progressWidth = width - mPadding * 2;
            float mDx = progressWidth * percent;

            int[] colors = new int[2];
            float[] positions = new float[2];
            //from color
            colors[0] = mGradientFrom;
            positions[0] = 0;
            //to color
            colors[1] = mGradientTo;
            positions[1] = 1;
            LinearGradient shader = new LinearGradient(
                    mPadding + progressHeight / 2.0f, mPadding, mPadding + progressHeight / 2.0f + mDx, mPadding + progressHeight,
                    colors,
                    positions,
                    Shader.TileMode.MIRROR);
            //gradient
            mGradientPaint.setShader(shader);

            //progress line
            RectF rectF = new RectF(mPadding, mPadding, mPadding + mDx, mPadding + progressHeight);
            canvas.drawRect(rectF, mGradientPaint);
        } else {
            int progressWidth = width - mPadding * 2;
            float dx = progressWidth * percent;
            mProgressPaint.setColor(mProgressColor);
            RectF rectF = new RectF(mPadding, mPadding, mPadding + dx, mPadding + progressHeight);
            canvas.drawRect(rectF, mProgressPaint);
        }

        //draw second progress
        if (mShowSecondProgress) {
            float secondPercent = 0;
            if (mMax != 0) {
                secondPercent = mSecondProgress * 1.0f / mMax;
            }
            int secondProgressHeight = getHeight() - mPadding * 2;
            if (mOpenSecondGradient) {
                int secondProgressWidth = width - mPadding * 2;
                float secondDx = secondProgressWidth * secondPercent;

                int[] secondColors = new int[2];
                float[] secondPositions = new float[2];
                //from color
                secondColors[0] = mSecondGradientFrom;
                secondPositions[0] = 0;
                //to color
                secondColors[1] = mSecondGradientTo;
                secondPositions[1] = 1;
                LinearGradient secondShader = new LinearGradient(
                        mPadding + secondProgressHeight / 2.0f, mPadding, mPadding + secondProgressHeight / 2.0f + secondDx, mPadding + secondProgressHeight,
                        secondColors,
                        secondPositions,
                        Shader.TileMode.MIRROR);
                //gradient
                mSecondGradientPaint.setShader(secondShader);

                //progress line
                RectF rectF = new RectF(mPadding, mPadding, mPadding + secondDx, mPadding + secondProgressHeight);
                canvas.drawRect(rectF, mSecondGradientPaint);
            } else {
                //no gradient
                //line shape
                int secondProgressWidth = width - mPadding * 2;
                float dx = secondProgressWidth * secondPercent;
                mSecondProgressPaint.setColor(mSecondProgressColor);
                RectF rectF = new RectF(mPadding, mPadding, mPadding + dx, mPadding + secondProgressHeight);
                canvas.drawRect(rectF, mSecondProgressPaint);
            }
        }

    }

    /**
     * 绘制圆角矩形进度
     */
    private void drawProgressRoundRectMode(Canvas canvas) {
        int width = getWidth();
        float percent = 0;
        if (mMax != 0) {
            percent = mProgress * 1.0f / mMax;
        }
        int progressHeight = getHeight() - mPadding * 2;
        int progressWidth = width - mPadding * 2 - mBorderWidth;
        float dx = progressWidth * percent;
        if (mOpenGradient) {
            int[] colors = new int[2];
            float[] positions = new float[2];
            //from color
            colors[0] = mGradientFrom;
            positions[0] = 0;
            //to color
            colors[1] = mGradientTo;
            positions[1] = 1;
            float left = mPadding + progressHeight / 2.0f;
            LinearGradient shader = new LinearGradient(
                    left, mPadding, left + dx, mPadding + progressHeight,
                    colors,
                    positions,
                    Shader.TileMode.MIRROR);
            //gradient
            mGradientPaint.setShader(shader);
            //progress line
            float rectLeft = mPadding + mBorderWidth / 2.0f;
            float rectTop = mPadding + mBorderWidth / 2.0f;
            RectF rectF = new RectF(rectLeft, rectTop, rectLeft + dx, getHeight() - rectTop);
            canvas.drawRoundRect(rectF, mRadius, mRadius, mGradientPaint);
        } else {
            mProgressPaint.setColor(mProgressColor);
            float rectLeft = mPadding + mBorderWidth / 2.0f;
            float rectTop = mPadding + mBorderWidth / 2.0f;
            RectF rectF = new RectF(rectLeft, rectTop, rectLeft + dx, getHeight() - rectTop);
            canvas.drawRoundRect(rectF, mRadius, mRadius, mProgressPaint);
        }

        //draw second progress
        if (mShowSecondProgress) {
            float secondPercent = 0;
            if (mMax != 0) {
                secondPercent = mSecondProgress * 1.0f / mMax;
            }
            int secondProgressHeight = getHeight() - mPadding * 2;
            int secondProgressWidth = width - mPadding * 2 - mBorderWidth;
            float secondDx = secondProgressWidth * secondPercent;
            if (mOpenSecondGradient) {
                int[] secondColors = new int[2];
                float[] secondPositions = new float[2];
                //from color
                secondColors[0] = mSecondGradientFrom;
                secondPositions[0] = 0;
                //to color
                secondColors[1] = mSecondGradientTo;
                secondPositions[1] = 1;
                float left = mPadding + secondProgressHeight / 2.0f;
                LinearGradient secondShader = new LinearGradient(
                        left, mPadding, left + secondDx, mPadding + secondProgressHeight,
                        secondColors,
                        secondPositions,
                        Shader.TileMode.MIRROR);
                //gradient
                mSecondGradientPaint.setShader(secondShader);

                //progress line
                float rectLeft = mPadding + mBorderWidth / 2.0f;
                float rectTop = mPadding + mBorderWidth / 2.0f;
                RectF rectF = new RectF(rectLeft, rectTop, rectLeft + secondDx, getHeight() - rectTop);
                canvas.drawRoundRect(rectF, mRadius, mRadius, mSecondGradientPaint);
            } else {
                //no gradient
                //line shape
                mSecondProgressPaint.setColor(mSecondProgressColor);
                float rectLeft = mPadding + mBorderWidth / 2.0f;
                float rectTop = mPadding + mBorderWidth / 2.0f;
                RectF rectF = new RectF(rectLeft, rectTop, rectLeft + secondDx, getHeight() - rectTop);
                canvas.drawRoundRect(rectF, mRadius, mRadius, mSecondProgressPaint);
            }
        }

    }

    /**
     * 绘制半圆形背景
     */
    private void drawBackgroundCircleMode(Canvas canvas) {
        int bgHeight = getHeight();
        int width = getWidth();
        //left circle
        canvas.drawCircle(bgHeight / 2.0f, bgHeight / 2.0f, bgHeight / 2.0f, mBgPaint);
        //right circle
        canvas.drawCircle(width - bgHeight / 2.0f, bgHeight / 2.0f, bgHeight / 2.0f, mBgPaint);
        //middle line
        RectF rectF = new RectF(bgHeight / 2.0f, 0, width - bgHeight / 2.0f, bgHeight);
        canvas.drawRect(rectF, mBgPaint);
    }

    /**
     * 绘制半圆形边框
     */
    private void drawBorderCircleMode(Canvas canvas) {
        if (mDrawBorder) {
            int bgHeight = getHeight();
            int width = getWidth();
            RectF rect = new RectF(0, 0, width, bgHeight);
            canvas.drawRoundRect(rect, bgHeight / 2.0f, bgHeight / 2.0f, mBorderPaint);
        }
    }

    /**
     * 绘制半方形边框
     */
    private void drawBorderRectMode(Canvas canvas) {
        if (mDrawBorder) {
            int bgHeight = getHeight();
            int width = getWidth();
            RectF rect = new RectF(0, 0, width, bgHeight);
            canvas.drawRect(rect, mBorderPaint);
        }
    }

    /**
     * 绘制圆角矩形边框
     */
    private void drawBorderRoundRect(Canvas canvas) {
        if (mDrawBorder) {
            int bgHeight = getHeight();
            int width = getWidth();
            RectF rect = new RectF(mBorderWidth / 2.0f, mBorderWidth / 2.0f, width - mBorderWidth / 2.0f, bgHeight - mBorderWidth / 2.0f);
            canvas.drawRoundRect(rect, mRadius, mRadius, mBorderPaint);
        }
    }

    /**
     * 绘制方形背景
     */
    private void drawBackgroundRectMode(Canvas canvas) {
        int bgHeight = getHeight();
        int width = getWidth();
        RectF rectF = new RectF(0, 0, width, bgHeight);
        canvas.drawRect(rectF, mBgPaint);
    }

    /**
     * 绘制圆角矩形背景
     */
    private void drawBackgroundRoundRectMode(Canvas canvas) {
        int bgHeight = getHeight();
        int width = getWidth();
        RectF rectF = new RectF(mBorderWidth / 2.0f, mBorderWidth / 2.0f, width - mBorderWidth / 2.0f, bgHeight - mBorderWidth / 2.0f);
        canvas.drawRoundRect(rectF, mRadius, mRadius, mBgPaint);
    }

    /**
     * 获取最大值
     *
     * @return 最大值
     */
    public int getMax() {
        return mMax;
    }

    /**
     * 设置最大值
     *
     * @param max 最大值
     */
    public void setMax(int max) {
        this.mMax = max;
        invalidate();
    }

    /**
     * 获取一级进度值
     *
     * @return 进度值
     */
    public int getProgress() {
        return mProgress;
    }

    /**
     * 设置一级进度值
     *
     * @param progress 进度值
     */
    public void setProgress(int progress) {
        if (progress < 0) {
            this.mProgress = 0;
        } else if (progress > mMax) {
            this.mProgress = mMax;
        } else {
            this.mProgress = progress;
        }
        invalidate();
        if (mOnProgressChangedListener != null) {
            mOnProgressChangedListener.onProgressChanged(this, mMax, this.mProgress);
        }
    }

    /**
     * 是否显示二级进度条
     *
     * @return 是/否
     */
    public boolean isShowSecondProgress() {
        return mShowSecondProgress;
    }

    /**
     * 设置是否显示二级进度条
     *
     * @param showSecondProgress 是/否
     */
    public void setShowSecondProgress(boolean showSecondProgress) {
        this.mShowSecondProgress = showSecondProgress;
        invalidate();
    }

    /**
     * 获取二级进度条进度
     *
     * @return 进度值
     */
    public int getSecondProgress() {
        return mSecondProgress;
    }

    /**
     * 设置二级进度条进度
     *
     * @param secondProgress 进度值
     */
    public void setSecondProgress(int secondProgress) {
        if (secondProgress < 0) {
            this.mSecondProgress = 0;
        } else if (secondProgress > mMax) {
            this.mSecondProgress = mMax;
        } else {
            this.mSecondProgress = secondProgress;
        }
        invalidate();
        if (mOnProgressChangedListener != null) {
            mOnProgressChangedListener.onSecondProgressChanged(this, mMax, this.mSecondProgress);
        }
    }

    /**
     * 获取二级进度条形状
     *
     * @return 形状,点:{@link #SHAPE_POINT} 线:{@link #SHAPE_LINE}
     */
    public int getSecondProgressShape() {
        return mSecondProgressShape;
    }

    /**
     * 设置二级进度条形状
     *
     * @param secondProgressShape 形状,点:{@link #SHAPE_POINT} 线:{@link #SHAPE_LINE}
     */
    public void setSecondProgressShape(@SecondProgressShape int secondProgressShape) {
        this.mSecondProgressShape = secondProgressShape;
        invalidate();
    }

    /**
     * 获取背景色
     *
     * @return 颜色值
     */
    public int getBgColor() {
        return mBgColor;
    }

    /**
     * 设置背景色
     *
     * @param bgColor 颜色值
     */
    public void setBgColor(@ColorInt int bgColor) {
        this.mBgColor = bgColor;
        mBgPaint.setColor(bgColor);
        invalidate();
    }

    /**
     * 获取二级渐变是否启用
     *
     * @return 是/否
     */
    public boolean isOpenSecondGradient() {
        return mOpenSecondGradient;
    }

    /**
     * 设置二级渐变是否启用
     *
     * @param openSecondGradient 是/否
     */
    public void setOpenSecondGradient(boolean openSecondGradient) {
        this.mOpenSecondGradient = openSecondGradient;
        invalidate();
    }

    public int getSecondGradientFrom() {
        return mSecondGradientFrom;
    }

    public int getSecondGradientTo() {
        return mSecondGradientTo;
    }

    /**
     * 获取二级进度条颜色
     *
     * @return 颜色值
     */
    public int getSecondProgressColor() {
        return mSecondProgressColor;
    }

    /**
     * 设置二级进度条颜色
     *
     * @param secondProgressColor 颜色值
     */
    public void setSecondProgressColor(@ColorInt int secondProgressColor) {
        this.mSecondProgressColor = secondProgressColor;
        mSecondProgressPaint.setColor(secondProgressColor);
        invalidate();
    }

    /**
     * 获取一级进度条颜色
     *
     * @return 颜色值
     */
    public int getProgressColor() {
        return mProgressColor;
    }

    /**
     * 设置一级进度条颜色
     *
     * @param progressColor 颜色值
     */
    public void setProgressColor(@ColorInt int progressColor) {
        this.mProgressColor = progressColor;
        mProgressPaint.setColor(progressColor);
        invalidate();
    }

    /**
     * 获取内边距
     *
     * @return 边距值
     */
    public int getPadding() {
        return mPadding;
    }

    /**
     * 设置内边距
     *
     * @param padding 边距值
     */
    public void setPadding(int padding) {
        this.mPadding = padding;
        invalidate();
    }

    /**
     * 设置显示模式
     *
     * @param showMode 显示模式,0:半圆,1:方形,2:圆角矩形
     */
    public void setShowMode(@ShowMode int showMode) {
        switch (showMode) {
            case SHOW_MODE_ROUND:
                this.mShowMode = 0;
                break;
            case SHOW_MODE_RECT:
                this.mShowMode = 1;
                break;
            case SHOW_MODE_ROUND_RECT:
                this.mShowMode = 2;
                break;
        }
        invalidate();
    }

    /**
     * 获取进度百分比,int类型
     *
     * @return percentage value
     */
    public int getPercentage() {
        if (mMax == 0) {
            return 0;
        }
        return (int) (mProgress * 100.0f / mMax + 0.5f);
    }

    /**
     * 获取进度百分比,float类型
     *
     * @return percentage value
     */
    public float getPercentageFloat() {
        if (mMax == 0) {
            return 0f;
        }
        return mProgress * 100.0f / mMax;
    }

    /**
     * 一级渐变色是否启用
     *
     * @return 是/否
     */
    public boolean isOpenGradient() {
        return mOpenGradient;
    }

    /**
     * 设置一级渐变色是否启用
     *
     * @param openGradient 是/否
     */
    public void setOpenGradient(boolean openGradient) {
        this.mOpenGradient = openGradient;
        invalidate();
    }

    public int getGradientFrom() {
        return mGradientFrom;
    }

    public int getGradientTo() {
        return mGradientTo;
    }

    /**
     * 设置边框颜色
     *
     * @param borderColor 颜色值
     */
    public void setBorderColor(@ColorInt int borderColor) {
        this.mBorderColor = borderColor;
        this.mBorderPaint.setColor(this.mBorderColor);
        invalidate();
    }

    /**
     * 设置一级进度条的渐变色
     *
     * @param from 起始颜色
     * @param to   结束颜色
     */
    public void setGradientColor(int from, int to) {
        this.mGradientFrom = from;
        this.mGradientTo = to;
        invalidate();
    }

    /**
     * 设置二级进度条的渐变色
     *
     * @param from 起始颜色
     * @param to   结束颜色
     */
    public void setSecondGradientColor(int from, int to) {
        this.mSecondGradientFrom = from;
        this.mSecondGradientTo = to;
        invalidate();
    }

    /**
     * 设置一级进度条的渐变色和边框颜色
     *
     * @param from        起始颜色
     * @param to          结束颜色
     * @param borderColor 边框颜色
     */
    public void setGradientColorAndBorderColor(int from, int to, int borderColor) {
        this.mGradientFrom = from;
        this.mGradientTo = to;
        this.mBorderColor = borderColor;
        this.mBorderPaint.setColor(this.mBorderColor);
        invalidate();
    }

    /**
     * 获取边框颜色
     *
     * @return 颜色值
     */
    public int getBorderColor() {
        return mBorderColor;
    }

    /**
     * 设置进度变化监听(包括一级和二级进度)
     *
     * @param onProgressChangedListener 进度值变化回调
     */
    public void setOnProgressChangedListener(OnProgressChangedListener onProgressChangedListener) {
        this.mOnProgressChangedListener = onProgressChangedListener;
    }

}

静态使用方式

如采用动态控件设置方式,则需要在对应组件内进行设置(因当前采用的是静态方式,可忽略MainActivity

package com.example.lineprogress

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    }
}

activity_main

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.example.lineprogress.ZzHorizontalProgressBar
        android:layout_width="match_parent"
        android:layout_height="10dp"
        android:layout_marginHorizontal="30dp"
        android:layout_marginTop="20dp"
        app:zpb_bg_color="#7F683F"
        app:zpb_max="100"
        app:zpb_pb_color="#FFDFA8"
        app:zpb_progress="19"
        app:zpb_show_mode="round" />

    <com.example.lineprogress.ZzHorizontalProgressBar
        android:layout_width="match_parent"
        android:layout_height="10dp"
        android:layout_marginHorizontal="30dp"
        android:layout_marginTop="20dp"
        app:zpb_bg_color="#7F683F"
        app:zpb_max="100"
        app:zpb_pb_color="#FFDFA8"
        app:zpb_progress="19"
        app:zpb_show_mode="rect" />

    <com.example.lineprogress.ZzHorizontalProgressBar
        android:layout_width="match_parent"
        android:layout_height="10dp"
        android:layout_marginHorizontal="30dp"
        android:layout_marginTop="20dp"
        app:zpb_bg_color="#7F683F"
        app:zpb_max="100"
        app:zpb_pb_color="#FFDFA8"
        app:zpb_progress="19"
        app:zpb_show_mode="round_rect" />

</androidx.appcompat.widget.LinearLayoutCompat>

项目场景

仅记录我在项目中使用该控件时所遇到的问题

圆角尺寸与设计图不符?

  1. 设置显示模式为 SHOW_MODE_ROUND_RECT
progressLine.setShowMode(SHOW_MODE_ROUND_RECT)
  1. 通过静态方式在xml中设置app:zpb_round_rect_radius尺寸
app:zpb_round_rect_radius="5dp"

设置对应进度值后,显示异常?

有可能显示为0,有可能显示满值,或者也有可能出现别的场景(到时候可以留言哈)

项目伪代码,接口数据一般采用 String 类型接收,为防止直接转换Int报错,可以先转Double再转Int

 progressLine.progress = floor(info?.growthValue?.toDouble() ?: 0.00).toInt()
 progressLine.max = floor(data?.nextGrowthValue?.toDouble() ?: 0.00).toInt()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1653195.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

程序员必备的7大神器,效率飞起!

我们都知道程序员在工作时&#xff0c;会经常遇到任务繁重的情况&#xff0c;为了提高效率&#xff0c;程序员们也会借助一些软件&#xff0c;那么哪些软件可以帮助程序员们提高工作效率呢&#xff1f; 整理不易&#xff0c;关注一波&#xff01;&#xff01; 1. Xftp 7 Xft…

深度学习实例2_车牌识别分割——自学笔记

import cv2 from matplotlib import pyplot as plt import os import numpy as np from PIL import ImageFont, ImageDraw, Image彩色图片显示 def plt_show0(img):b,g,r = cv2.split(img)img = cv2.merge([r, g, b])plt.imshow(img)plt.show()灰度图片显示 def plt_show(img…

【北京迅为】《iTOP-3588开发板快速烧写手册》-第11章 救砖方法

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

最稳定的VPS有哪些?2024年稳定服务器推荐

最稳定的VPS有&#xff1a;DigitalOcean、萤光云、Vultr、Linode、AWS。 VPS的硬件质量、网络稳定性、数据中心设施、数据中心位置等都是评估VPS好坏的重要条件&#xff0c;接下来为一起来看看5个2024年稳定服务器厂商&#xff0c;大家可以自己对比一下。 2024年稳定服务器推荐…

OpenAI 高管:一年后,你会觉得现在的 ChatGPT 像笑话一样糟糕|TodayAI

OpenAI 的首席运营官 Brad Lightcap 表示&#xff0c;一年后&#xff0c;你会觉得现在的 ChatGPT 像笑话一样糟糕。未来的 ChatGPT 版本将会有重大升级。他还讨论了 AI 取代人类工作和对电网的压力的可能性。 虽然我们不知道 OpenAI 何时会推出 GPT-5&#xff0c;但公司高管已…

类和对象、this指针、类里的默认生成函数

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

探索数据结构

什么是数据结构 数据结构是由&#xff1a;“数据”与“结构”两部分组成 数据与结构 数据&#xff1a;如我们所看见的广告、图片、视频等&#xff0c;常见的数值&#xff0c;教务系统里的&#xff08;姓名、性别、学号、学历等等&#xff09;&#xff1b; 结构&#xff1a;当…

C++ 继承篇

面向对象语言的三大特性&#xff1a;封装&#xff0c;继承和多态 根据目前学到的知识&#xff0c;对于封装的理解&#xff0c;大致有两层&#xff1a; 将数据和方法封装&#xff0c;不想让外面看到用private/protected修饰&#xff0c;想让外面看到用public修饰类型的行为不满…

Intel® Platform Firmware Resilience (Intel® PFR):英特尔® 平台固件恢复力(Intel® PFR)

为了降低与固件相关的安全风险&#xff0c;英特尔为服务器平台开发了英特尔平台固件恢复力&#xff08;Intel PFR&#xff09;。 此功能可保护关键固件在启动和运行时免受攻击。这可以被视为是 Cerberus 项目或 NIST SP800-193 的实现。 英特尔平台固件恢复力&#xff08;Int…

SQL 基础 | JOIN 操作介绍

在SQL中&#xff0c;JOIN是一种强大的功能&#xff0c;用于将两个或多个表中的行结合起来&#xff0c;基于相关的列之间的关系。 JOIN操作通常用在SELECT语句中&#xff0c;以便从多个表中检索数据。 以下是几种基本的JOIN类型以及它们的用法&#xff1a; INNER JOIN&#xff1…

探秘编程之旅:Baidu Comate 智能代码助手的魔法揭秘

目录 Baidu Comate智能代码助手1.场景需求2.安装步骤3.功能介绍3.1 /指令3.2 插件3.3 #知识 4.使用体验5.总结 Baidu Comate智能代码助手 智能编程助手的意义在于提升编程体验和效率&#xff0c;使开发人员能够更轻松、更快速地完成编码任务&#xff0c;是如今人工智能技术的一…

2024年颠覆商业模式《本草生活》项目,巧妙三招营销引流裂变套路

2024年颠覆商业模式《本草生活》项目&#xff0c;巧妙三招营销引流裂变套路 文丨微三云营销总监胡佳东&#xff0c;点击上方“关注”&#xff0c;为你分享市场商业模式电商干货。 - 引言&#xff1a;现如今流量枯竭、降本增效、红利不再已是线上营销的常态&#xff0c;互联网…

中金:如何把握不断轮动的资产“风口”

从比特币到日股&#xff0c;到黄金与铜再到当前的港股&#xff0c;每次超预期大涨后都透支回调。 今年以来资产的“风口”不断轮动&#xff0c;从比特币到日股&#xff0c;到黄金与铜&#xff0c;再到当前的港股&#xff0c;资产仿佛“接力”般交替领先&#xff0c;同时“风口”…

【北京迅为】《iTOP-3588开发板快速烧写手册》-第10章 多设备量产升级固件

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

分享6个免费下载电子书的网站

着急看书的宝子们看这里&#xff01; 收藏了一堆电子书网站终于能派上用场了~ 01/Z-Library https://zh.zlibrary-be.se/ 世界上最大的电子图书馆&#xff0c;拥有超千万的书籍和文章资源&#xff0c;99%的书籍资料都能在这里找到。 我给的这个网址现在还能正常打开使用&…

vue+sortablejs来实现列表拖拽——sortablejs的使用

sortablejs官网:https://sortablejs.com/ 最近在看form-builder组件&#xff0c;发现里面有用到sortablejs插件&#xff0c;用于实现拖拽效果。 但是这个官网中的配置&#xff0c;实在是看不懂&#xff0c;太简单又太复杂&#xff0c;不实用。 下面记录一下我的使用&#xff…

sklearn的make_blobs函数

make_blobs是一个用于生成随机数据点的实用函数&#xff0c; from sklearn.datasets import make_blobs X,Y make_blobs(n_samples2000,n_features2,centers12,cluster_std0.05,center_box[-5,5],random_state21)n_samples: 要生成的样本数量。centers: 要生成的簇&#xff0…

JetsonNano —— Windows下对Nano板卡烧录刷机(官方教程)

介绍 NVIDIA Jetson Nano™ 开发者套件是一款面向创客、学习者和开发人员的小型 AI 计算机。按照这个简短的指南&#xff0c;你就可以开始构建实用的 AI 应用程序、酷炫的 AI 机器人等了。 烧录刷机 1、下载 Jetson Nano开发者套件SD卡映像&#xff0c;并记下它在计算机上的保存…

docker部署小试

一 1.1 需求&#xff1a;根据docker部署nginx并且实现https 1.2 前期准备 准备一台装备好的docker-ce虚拟机&#xff0c;容量至少满足4G/2C&#xff0c;同时做好关闭防火墙的操作 systemctl stop firewalld setenforce 0 1.3 实验部署 1.3.1 创建并进入文件夹 1.3.2 编辑run脚本…

XSKY SDS 6.4 重磅更新:NFS 性能飙升 3 倍,对象多站点等 10 多项功能强势升级

近日&#xff0c;XSKY星辰天合发布了 XSKY SDS V6.4 新版本&#xff0c;该版本在文件的性能提升、对象容灾能力完善方面改进异常显著&#xff0c;同时也大幅提高了存储系统的安全特性&#xff0c;适配更多的信创软硬件生态。 近来&#xff0c;软件定义存储&#xff08;SDS&…