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

年后这个新版本加入了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);}@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.mzph.cn/web/8681.shtml

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

相关文章

ETL工具中JSON格式的转换方式

JSON的用处 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;其设计初衷是为了提升网络应用中数据的传输效率及简化数据结构的解析过程。自其诞生以来&#xff0c;JSON 已成为Web开发乃至众多软件开发领域中不可或缺的一部分&a…

神经网络案例实战

&#x1f50e;我们通过一个案例详细使用PyTorch实战 &#xff0c;案例背景&#xff1a;你创办了一家手机公司&#xff0c;不知道如何估算手机产品的价格。为了解决这个问题&#xff0c;收集了多家公司的手机销售数据&#xff1a;这些数据维度可以包括RAM、存储容量、屏幕尺寸、…

# 光标变为下划线怎么办?

光标变为下划线怎么办&#xff1f; 光标变为下划线通常表示您处于覆盖模式。在这种模式下&#xff0c;您键入的任何内容都将覆盖光标位置处的文本。如果想要恢复光标为正常显示&#xff0c;您可以尝试以下方法&#xff1a; 1、在桌面或文档编辑界面&#xff0c;按键盘上的 【I…

Object类

Object类 概念&#xff1a;Object类是所有类的父类&#xff0c;也就是说任何一个类在定义时候如果没有明确的指定继承一个父类的话&#xff0c;那么它就都默认继承Object类&#xff0c;因此Object类被称为所有类的父类&#xff0c;也叫做基类/超类。 常用方法 方法类型描述eq…

每日OJ题_记忆化搜索①_力扣509. 斐波那契数(四种解法)

目录 记忆化搜索概念和使用场景 力扣509. 斐波那契数 解析代码1_循环 解析代码2_暴搜递归 解析代码3_记忆化搜索 解析代码4_动态规划 记忆化搜索概念和使用场景 记忆化搜索是一种典型的空间换时间的思想&#xff0c;可以看成带备忘录的爆搜递归。 搜索的低效在于没有能够…

《手把手教你怎么上手做一个小程序》

准备工作&#xff1a; 硬件准备&#xff1a; 装有微信的手机一台。 账号注册&#xff1a; 进入https://mp.weixin.qq.com/cgi-bin/registermidpage?actionindex&langzh_CN&token注册一个微信小程序账号。 然后输入邮箱注册账号。一个邮箱只能注册一个微信公众平台…

【面试经典 150 | 数组】找出字符串中第一个匹配项的下标

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;find方法二&#xff1a;暴力匹配方法三&#xff1a;KMP 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;…

算法学习006-瓷砖总数 广度优先算法BFS 中小学算法思维学习 信奥算法解析 c++实现

目录 C瓷砖总数 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、推荐资料 C瓷砖总数 一、题目要求 1、编程实现 在一个长方形房间&#xff0c;铺着不同颜色的的瓷砖&#xff0c;有红色和黑色&#…

AR人脸道具SDK解决方案,实现道具与人脸的自然融合

AR人脸道具SDK解决方案&#xff0c;实现道具与人脸的自然融合美摄科技以其卓越的技术实力和创新能力&#xff0c;为企业带来了全新的AR人脸道具SDK解决方案。这一解决方案将为企业打开全新的市场机会&#xff0c;为用户带来前所未有的互动体验。 颠覆传统&#xff0c;开启AR人…

AI预测福彩3D第10套算法实战化赚米验证第1弹2024年5月5日第1次测试

从今天开始&#xff0c;准备启用第10套算法&#xff0c;来验证下本算法的可行性。因为本算法通过近三十期的内测&#xff08;内测版没有公开预测结果&#xff09;&#xff0c;发现本算法的预测结果优于其他所有算法的效果。彩票预测只有实战才能检验是否有效&#xff0c;只有真…

产业项目招商活动会议课程报名签到h5小程序pc开源版开发

产业项目招商活动会议课程报名签到h5小程序pc开源版开发 一个集PC和移动端功能于一体的解决方案&#xff0c;线上线下进行服务&#xff0c;围绕 活动报名、在线课程、项目大厅、线下签到、会员系统等。为商会提供了更加便捷高效的管理方式&#xff0c;提升了商会活动和项目的组…

qt day 3

优化登录框&#xff0c;点击登录按钮&#xff0c;如果账号和密码匹配&#xff0c;则弹出 信息对话框 给出提示信息“登录成功”&#xff0c;并给出一个 ok 按钮&#xff0c;当用户点击 ok 后&#xff0c;关闭当前界面&#xff0c;跳转到另一个界面&#xff1b;如果账号和密码不…

labview技术交流-将时间字符串转换成时间格式

应用场景 我们在数据库中设计了datetime类型的字段&#xff0c;比如字段名就叫“保存时间”&#xff0c;当我们使用labview将表中数据读取出来后datetime类型的数据是以字符串的格式显示的。而我们想计算两条数据“保存时间”的间隔时间时&#xff0c;用字符串类型自然是没法计…

OpenMV 图像串口传输示例

注意&#xff1a;本程序根据 OpenMV采集图片通过串口发送&#xff0c;PC接收并保存为图片 更改。 一、例程说明 这个例程主要实现了以下功能: 1. OpenMV 端采集图像:使用OpenMV开发板上的摄像头采集实时图像数据。 2. 通过串口传输图像数据:将采集到的图像数据打包成字节流,…

idea 项目 修改项目文件名 教程

文章目录 目录 文章目录 修改流程 小结 概要流程技术细节小结 概要 原项目名 修改流程 关掉当前项目的idea页面 修改之后的文件名 重新打开idea。选择项目打开项目页面 技术细节 出现下面这个问题&#xff0c;可以参考作者新的一编文章idea开发工具 项目使用Spring框架开发解…

【Linux】25. 网络基础(一)

网络基础(一) 计算机网络背景 网络发展 独立模式: 计算机之间相互独立; 网络互联: 多台计算机连接在一起, 完成数据共享; 其实本质上一台计算机内部也是一个小型网络结构(如果我们将计算机内部某个硬件不存放在电脑中&#xff0c;而是拉根长长的线进行连接。这其实也就是网…

在做题中学习(51): x的平方根

69. x 的平方根 - 力扣&#xff08;LeetCode&#xff09;​​​​​​ 解法&#xff1a;二分查找 思路&#xff1a;看示例2&#xff1a; 可以看到8的平方根是2.82&#xff0c;在2^2和3^2之间&#xff0c;所以可以把数组分为两部分&#xff0c;(具有二段性) 而2.82去掉小数部分…

【Kolmogorov-Arnold网络 替代多层感知机MLPs】KAN: Kolmogorov-Arnold Networks

KAN: Kolmogorov-Arnold Networks 论文地址 代码地址 知乎上的讨论&#xff08;看一下评论区更正&#xff09; Abstract Inspired by the Kolmogorov-Arnold representation theorem, we propose Kolmogorov-Arnold Networks (KANs) as promising alternatives to Multi-Layer…

【OceanBase 系列】—— OceanBase v4.3 特性解读:查询性能提升之利器列存储引擎

原文链接&#xff1a;OceanBase 社区 对于分析类查询&#xff0c;列存可以极大地提升查询性能&#xff0c;也是 OceanBase 做好 HTAP 和 OLAP 的一项不可缺少的特性。本文介绍 OceanBase 列存的实现特色。 OceanBase从诞生起就一直坚持LSM-Tree架构&#xff0c;不断打磨功能支…

2020 年国考【计算机专业】真题及答案

真题及答案 第一部分数学基础课程 一、&#xff08;共 5 分&#xff09;用逻辑符号表达下列语句&#xff08;论域为包含一切事物的合集&#xff09; &#xff08;2 分&#xff09;确诊者并不都有症状&#xff08;注&#xff1a;需给出两种形式表达, 一种用存在量词, 一种用全称…