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

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

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

进度条Blog

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

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

年过半许

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

基础概要

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

功能分析

在这里插入图片描述

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

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

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

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

组件解析

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

作用范围

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

属性说明

在这里插入图片描述

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

在这里插入图片描述

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

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

在这里插入图片描述

绘制当前进度条,除了公共部分,可以直接看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);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);switch (mShowMode) {case SHOW_MODE_ROUND://half circledrawBackgroundCircleMode(canvas);drawProgressCircleMode(canvas);drawBorderCircleMode(canvas);break;case SHOW_MODE_RECT://rectdrawBackgroundRectMode(canvas);drawProgressRectMode(canvas);drawBorderRectMode(canvas);break;case SHOW_MODE_ROUND_RECT://custom radiusdrawBackgroundRoundRectMode(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 colorcolors[0] = mGradientFrom;positions[0] = 0;//to colorcolors[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);//gradientmGradientPaint.setShader(shader);int radius = width > getHeight() ? getHeight() / 2 : width / 2;if (dx < getHeight()) {//left circleif (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 lineRectF 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 circleif (mProgress == 0) {if (mShowZeroPoint) {canvas.drawCircle(left, left, progressHeight / 2.0f, mProgressPaint);}} else {canvas.drawCircle(left, left, progressHeight / 2.0f, mProgressPaint);}//right circleif (mProgress == 0) {if (mShowZeroPoint) {canvas.drawCircle(left + dx, left, progressHeight / 2.0f, mProgressPaint);}} else {canvas.drawCircle(left + dx, left, progressHeight / 2.0f, mProgressPaint);}//middle lineRectF rectF = new RectF(left, mPadding, left + dx, mPadding + progressHeight);canvas.drawRect(rectF, mProgressPaint);}//draw second progressif (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 colorsecondColors[0] = mSecondGradientFrom;secondPositions[0] = 0;//to colorsecondColors[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);//gradientmSecondGradientPaint.setShader(secondShader);int secondRadius = width > getHeight() ? getHeight() / 2 : width / 2;if (secondDx < getHeight()) {//left circleif (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 lineRectF rectF = new RectF(mPadding, mPadding, mPadding + secondDx, mPadding + secondProgressHeight);canvas.drawRoundRect(rectF, secondRadius, secondRadius, mSecondGradientPaint);}} else {//no gradientif (mSecondProgressShape == 0) {//point shapeint secondProgressWidth = width - mPadding * 2;float secondDx = secondProgressWidth * secondPercent;//progress linefloat 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 shapeint secondProgressWidth = width - mPadding * 2 - secondProgressHeight;float dx = secondProgressWidth * secondPercent;mSecondProgressPaint.setColor(mSecondProgressColor);//left circleif (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 circleif (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 lineRectF 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 colorcolors[0] = mGradientFrom;positions[0] = 0;//to colorcolors[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);//gradientmGradientPaint.setShader(shader);//progress lineRectF 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 progressif (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 colorsecondColors[0] = mSecondGradientFrom;secondPositions[0] = 0;//to colorsecondColors[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);//gradientmSecondGradientPaint.setShader(secondShader);//progress lineRectF rectF = new RectF(mPadding, mPadding, mPadding + secondDx, mPadding + secondProgressHeight);canvas.drawRect(rectF, mSecondGradientPaint);} else {//no gradient//line shapeint 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 colorcolors[0] = mGradientFrom;positions[0] = 0;//to colorcolors[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);//gradientmGradientPaint.setShader(shader);//progress linefloat 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 progressif (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 colorsecondColors[0] = mSecondGradientFrom;secondPositions[0] = 0;//to colorsecondColors[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);//gradientmSecondGradientPaint.setShader(secondShader);//progress linefloat 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 shapemSecondProgressPaint.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 circlecanvas.drawCircle(bgHeight / 2.0f, bgHeight / 2.0f, bgHeight / 2.0f, mBgPaint);//right circlecanvas.drawCircle(width - bgHeight / 2.0f, bgHeight / 2.0f, bgHeight / 2.0f, mBgPaint);//middle lineRectF 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.lineprogressimport androidx.appcompat.app.AppCompatActivity
import android.os.Bundleclass 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.ZzHorizontalProgressBarandroid: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.ZzHorizontalProgressBarandroid: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.ZzHorizontalProgressBarandroid: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.rhkb.cn/news/322622.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【YOLO】目标检测 YOLO框架之train.py参数含义及配置总结手册(全)

1.一直以来想写下基于YOLO开源框架的系列文章&#xff0c;该框架也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下YOLO目标检测相关知识体系&#xff0c;之前实战配置时总是临时性检索些注释含义&#xff0c;但…

spring模块(六)spring监听器(2)@EventListener

一、介绍 监听器的简化写法 二、原理 三、使用 Slf4j Component public class MyTask {EventListenerpublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof ContextRefreshedEvent) {log.info("监听到 ContextRefreshedEvent...");}if…

水电抄表方案是什么?

1.概述&#xff1a;水电抄表方案的重要性 水电抄表方案是现代城市管理中不可或缺的一部分&#xff0c;它涉及到了能源管理、费用结算和公共服务等多个领域。传统的抄表方式需要工作人员上门服务&#xff0c;费时费力且效率低下。随着科技的发展&#xff0c;智能化的水电抄表方…

融知财经:期货交易原理是怎样的?期货交易有哪些特征?

期货的原理是基于对某期货品种未来走势的判断而形成对其合约的买卖交易&#xff0c;因此期货可以解释为买涨或买跌。买涨&#xff0c;即看多交易&#xff0c;预期某期货品种未来价格上涨而进行的买入开仓交易&#xff1b;买跌&#xff0c;即看空交易&#xff0c;预期某期货品种…

Java学习第05天-编程思维与编程能力

文章目录 综合应用案例&#xff1a;找素数数组元素的复制数字加密模拟双色球 综合应用 涉及的知识点&#xff1a; 变量、数组运算符&#xff1a;基本运算符、关系运算符、逻辑运算符流程控制&#xff1a;if、switch、for、while、死循环、循环嵌套跳转关键字&#xff1a;break、…

Ps 滤镜:像素化

Ps菜单&#xff1a;滤镜/像素化 Filter/Pixelate “像素化”子菜单中的滤镜可以将图像以其他形状的元素重新再现出来。它并不是真正地改变了图像像素点的形状&#xff0c;它只是在图像中表现出某种基础形状的特征&#xff0c;以形成一些类似像素化的形状变化。 彩块化 Facet “…

17.接口自动化学习-日志

1.日志输出渠道 &#xff08;1&#xff09;文件格式 xx.log &#xff08;2&#xff09;控制台输出 2.日志级别 debug<info<warnning<error<critical 3.代码实现 from utils.handle_path import log_path import logging import datetime def logger(fileLogTr…

mac通过termius连接Linux服务器

mac上安装 linux系统 如果有 linux服务器账号密码&#xff0c;那么上一步可忽略&#xff1b; 比如&#xff1a;直接连接阿里云或腾讯云账号 1. 安装termius 链接: https://pan.baidu.com/s/1iYsZPZThPizxqtkLPT89-Q?pwdbw6j 提取码: bw6j 官网 Termius - SSH platform for …

springcloud报错:Failed to start bean‘webServerStartStop‘

如果你正在使用nacos进行服务注册&#xff0c;然后报一下错误&#xff1a; 那就说明的nacos没有打开&#xff0c;所以找到你的下载nacos的文件夹 好了&#xff0c;错误完美解决~

elk + filebeat 8.4.3 收集nginx日志(docker部署)

ELK filebeat docker部署 一、 elasticsearch部署1、运行elasticsearch临时配置容器2、拷贝文件目录到本地3、检查elasticsearch.yml4、删除之前elastic&#xff0c;运行正式容器5、docker logs记录启动日志 二、部署kibana1、运行kibana临时配置容器2、docker拷贝配置文件到本…

sql 注入 1

当前在email表 security库 查到user表 1、第一步&#xff0c;知道对方goods表有几列&#xff08;email 2 列 good 三列&#xff0c;查的时候列必须得一样才可以查&#xff0c;所以创建个临时表&#xff0c;select 123 &#xff09; 但是你无法知道对方goods表有多少列 用order …

毕业论文怎么写? 推荐4个AI工具

写作这件事一直让我们从小学时期就开始头痛&#xff0c;初高中时期800字的作文让我们焦头烂额&#xff0c;一篇作文里用尽了口水话&#xff0c;拼拼凑凑才勉强完成。 大学时期以为可以轻松顺利毕业&#xff0c;结果毕业前的最后一道坎拦住我们的是毕业论文&#xff0c;这玩意不…

带你入门React

目录 前言一&#xff0c;基本配置1.1 环境搭建1.2 页面初始化渲染二&#xff0c;基础学习2.1 结构与样式开发2.2 数据展示2.3 行内样式2.4 条件渲染2.5 列表渲染2.6 点击事件 三&#xff0c;页面更新3.1 组件数据3.2 组件数据共享 总结 前言 笔者之前的工作经验都局限于Vue&am…

01-单片机商业项目编程,从零搭建低功耗系统设计

一、引言 这是关于《单片机商业编程之从零搭建低功耗系统》的第一篇章&#xff0c;个人善忘&#xff0c;平常项目设计当中的一些思路&#xff0c;以前年轻的时候习惯性的录制成视频&#xff0c;也算是当作是自己的笔记&#xff0c;无奈现在喉咙实在扛不住&#xff0c;因此先尝试…

适用于 macOS 的最佳独立 HBO Max 客户端

适用于 macOS 的最佳独立 HBO Max 应用程序。不再在浏览器选项卡之间切换。只需直接从 Dock 启动 Clicker for HBO Max 即可开始狂欢。 HBO Max 客户端 Clicker for HBO Max 下载 Clicker for HBO Max mac版安装教程 软件下载完成后&#xff0c;双击pkg根据提示进行安装 Clic…

2024最新同城吃喝玩乐小程序源码+同城分类信息小程序搭建+开源无需授权+详细图文安装部署教程

在繁忙的都市生活中&#xff0c;生活节奏飞快&#xff0c;人们希望能够快速、便捷地获取各类生活信息&#xff0c;满足日常的吃喝玩乐需求。同城吃喝玩乐同城分类信息小程序&#xff0c;非常受欢迎&#xff0c;提供一站式便捷生活新体验。 分享一款2024最新同城吃喝玩乐小程序…

QT作业3

1、思维导图 2、聊天界面 //头文件1 #ifndef MYWIDGET_H #define MYWIDGET_H#include <QWidget> #include<QIcon> //图标类 #include<QLabel> //标签类 #include<QMovie> //动图类 #include<QLineEdit> //行编辑器类 #include<QPushButton&g…

《Fundamentals of Power Electronics》——示例:Buck-Boost转换器模型变为正则形式

为了说明正则电路模型推导的步骤&#xff0c;让我们将buck-boost转换器的等效电路操作成规范形式。buck-boost转换器的一个小信号交流等效电路如下图所示。 为了将上图所示网络转换成正则形式&#xff0c;需要将所有独立源d(t)转换到左侧&#xff0c;而将所有电感转换到右侧与变…

Linux——基础IO2

引入 之前在Linux——基础IO(1)中我们讲的都是(进程打开的文件)被打开的文件 那些未被打开的文件呢&#xff1f; 大部分的文件都是没有被打开的文件&#xff0c;这些文件在哪保存&#xff1f;磁盘(SSD) OS要不要管理磁盘上的文件&#xff1f;(如何让OS快速定位一个文件) 要…

华为数据之道第二部分导读

目录 导读 第二部分 第4章 面向“业务交易”的信息架构建设 信息架构的四个组件 数据资产目录 数据标准 数据模型 数据分布 信息架构原则&#xff1a;建立企业层面的共同行为准则 信息架构建设核心要素&#xff1a;基于业务对象进行设计和落地 按业务对象进行架构设…