跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java

Android 自定义节点进度条实现

展示了如何在 Android 中通过继承 View 类实现自定义的节点进度条组件。核心逻辑包括初始化资源、计算节点位置与偏移量、在 Canvas 上绘制空心圆、实心圆及连接进度条,并根据当前进度动态更新节点颜色与进度条长度。提供了完整的 Java 代码示例,涵盖布局测量、绘图逻辑及进度控制方法。修复了原代码中的语法错误并清理了无关的推广内容。

片刻发布于 2025/2/7更新于 2026/9/963 浏览
Android 自定义节点进度条实现

本文展示了如何在 Android 中通过继承 View 类实现自定义的节点进度条组件。核心逻辑包括初始化资源、计算节点位置与偏移量、在 Canvas 上绘制空心圆、实心圆及连接进度条,并根据当前进度动态更新节点颜色与进度条长度。

核心代码实现

package utils.android.view.lxz;
import java.util.ArrayList;
import java.util.List;
import com.community.custom.android.R;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;

/**
 * 2015-1-24
 * 小林爱
 */
public class NodeProgressBar extends View implements Runnable {
    private float dn = 1f;
    DisplayMetrics metrics;
    {
        metrics = new DisplayMetrics();
        ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
        dn = 1 * 3 / metrics.density;
    }

    /**读取空进度条图片*/
    private BitmapDrawable db_empty;
    /**View 宽度*/
    private int viewWidth;
    /**View 高度*/
    private int viewHeight;
    /**
     * 白色空心圆图片集合
     */
    private List<BitmapDrawable> list_whitecircle;
    /**
     * 蓝色实心圆图片集合
     */
    private List<BitmapDrawable> list_bluecircle;
    /**蓝色进度条*/
    private BitmapDrawable db_blue;
    /**进度比值 控制蓝色进度条*/
    private double ratio = 0;
    /**节点名称*/
    private String[] textArr = new String[]{"等待服务", "服务中", "等待评价", "成功"};
    /**节点数*/
    private int count = textArr.length;
    /**当前节点进度*/
    private int index = 0;
    /**X 轴对称 偏移值*/
    private int offX = (int) (50 / dn);
    /**Y 轴 偏移值*/
    private int offY = (int) (70 / dn);
    /**文字与节点 偏移值*/
    private int offTextY = (int) (65 / dn);
    /**白色空心圆偏移值*/
    private int offWhiteCirle_y = (int) (-3 / dn);
    /**白色空心进度条偏移值*/
    private int offWhiteRect_y = (int) (-2 / dn);
    /**白色空心进度条偏移值*/
    private BitmapDrawable db_blue_half_circle;
    /**创建一只新画笔*/
    private Paint paint = new Paint();
    /**白色空心圆半径*/
    private int r_white = (int) (76 / dn);
    /**蓝色空心圆半径*/
    private int r_blue = (int) (56 / dn);
    /**白色进度条高度*/
    private int whiteProgressHeight = (int) (30 / dn);
    /**蓝色进度条高度*/
    private int blueProgressHeight = (int) (16 / dn);
    /**文本颜色*/
    private String textColor = "#46A3FF";
    /**文本未激活颜色*/
    private String textColorNotEnabled = "#7E7E81";
    /**文本框大小*/
    private int textSize = (int) (32 / dn);
    /**白色空心进度条宽度*/
    private int maxProgressWidth;
    /**半圆蓝色进度条宽度*/
    private int half_blueWidth = (int) (12 / dn);

    public String[] getTextArr() {
        return textArr;
    }

    public void setTextArr(String[] textArr) {
        this.textArr = textArr;
        count = textArr.length;
        init();
        this.post(new Runnable() {
            @Override
            public void run() {
                //读取 View 宽度
                viewWidth = NodeProgressBar.this.getWidth();
                //读取 View 高度
                viewHeight = NodeProgressBar.this.getHeight();
                // 进度条宽度计算
                maxProgressWidth = viewWidth - r_white - offX * 2;
                //绘制
                invalidate();
            }
        });
    }

    @SuppressWarnings("deprecation")
    public NodeProgressBar(Context context) {
        super(context);
        init();
    }

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

    /**控制蓝色进度条*/
    public void setProgressOnly(int i) {
        ratio = i / 100d;
        invalidate();
    }

    /**以节点数来空值进度条 至少大于 1*/
    public void setProgressByNode(final double d)
    {
        this.post(new Runnable() {
            @Override
            public void run() {
                int progress;
                if (d == 1)
                {
                    progress = 1;
                } else
                {
                    progress = (int) ((100d / ((count - 1) * 1.0d)) * (d - 1));
                }
                setProgressAndIndex(progress);
            }
        });
    }

    /**控制蓝色进度条并且对节点染色*/
    public void setProgressAndIndex(int i){
        if(i==0){
            index=0;
            ratio=0;
            invalidate();
            return ;
        }
        //获得相对进度条长度
        int adbProgress = maxProgressWidth-(count-1)*r_white;
        //得到每一个节点所需进度值
        int k=100/(count-1);
        //计算当前进度需要染色的节点个数
        index=1+i/k;
        if(index!=count)
        {
            //获得节点磁力比率
            double wh=1.0d*(r_white/2)/(double)maxProgressWidth;
            //计算蓝色进度条和染色节点宽度
            ratio=i%100==0?ratio=1:wh+wh*2*(index-1)+1.0d*((double)adbProgress/(double)maxProgressWidth)*(i/100d);
        }
        else
        {
            //设置进度条为满
            ratio=1;
        }
        invalidate();
    }

    /**
     * 初始化图片资源,和基础数值
     */
    @SuppressWarnings("deprecation")
    private void init() {
        metrics=new DisplayMetrics();
        ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
        Log.d("lxz", ""+metrics.density);
        dn=1*3/metrics.density;
        list_whitecircle=new ArrayList<BitmapDrawable>();
        list_bluecircle=new ArrayList<BitmapDrawable>();
        //根据节点个数 初始化空心圆和实心圆
        for (int i = 0; i < count; i++) {
            BitmapDrawable drawable1 = new BitmapDrawable(
                BitmapFactory.decodeResource(getResources(),
                    R.drawable.progress_white_circle));
            list_whitecircle.add(drawable1);
            BitmapDrawable drawable2 = new BitmapDrawable(
                BitmapFactory.decodeResource(getResources(),
                    R.drawable.progress_blue_circle));
            list_bluecircle.add(drawable2);
        }
        //初始化蓝色小半圆
        db_blue_half_circle = new BitmapDrawable(BitmapFactory.decodeResource(
            getResources(), R.drawable.progress_blue_half_circle));
        //初始化空心进度条
        db_empty = new BitmapDrawable(BitmapFactory.decodeResource(
            getResources(), R.drawable.progress_white_groove));
        //初始化
        db_blue = new BitmapDrawable(BitmapFactory.decodeResource(
            getResources(), R.drawable.progress_blue_groove));
        //UI 线程初始化数值
        this.post(this);
    }

    @Override
    public void run() {
        //读取 View 宽度
        viewWidth = NodeProgressBar.this.getWidth();
        //读取 View 高度
        viewHeight = NodeProgressBar.this.getHeight();
        // 进度条宽度计算
        maxProgressWidth = viewWidth - r_white-offX*2;
        //绘制
        invalidate();
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        //获得 X 轴偏转值
        int offAbs_x=(int)((viewWidth-maxProgressWidth)/2.0d);
        canvas.drawColor(Color.TRANSPARENT);
        //绘制空心进度条
        drawRect(canvas, db_empty, viewWidth / 2, r_white / 2+offY+offWhiteRect_y, maxProgressWidth,whiteProgressHeight);
        paint.setTextSize(textSize);
        paint.setFakeBoldText(true);
        //绘制白色空心园
        for (int i = 0, j = list_whitecircle.size(); i < j; i++) {
            BitmapDrawable db_whitecircle = list_whitecircle.get(i);
            int x=maxProgressWidth / (count - 1)* i+offAbs_x;
            int y=r_white/2 + offWhiteCirle_y+offY;
            drawCircle(canvas, db_whitecircle,x, y, r_white);
            String str=textArr[i];
            if(i<index)
            {
                paint.setColor(Color.parseColor(textColor));
            }
            else
            {
                paint.setColor(Color.parseColor(textColorNotEnabled));
            }
            float textWidht = paint.measureText(str);
            canvas.drawText(str, x-textWidht/2, y+offTextY, paint);
        }
        //绘制蓝色进度条
        drawRect(canvas, db_blue, (int) 
            ((maxProgressWidth * ratio) / 2)+offAbs_x , 
            r_white / 2+offY,
            (int) (maxProgressWidth * ratio),blueProgressHeight);
        //绘制蓝色小半圆
        if (ratio > 0) {
            drawRect(canvas, db_blue_half_circle,(int) ((maxProgressWidth * ratio) / 2) 
                + (int) (maxProgressWidth * ratio) / 2 + half_blueWidth 
                / 2+offAbs_x, r_white / 2+offY, half_blueWidth, 
                blueProgressHeight);
        }
        //绘制蓝色圆
        for (int i = 0, j = index; i < j; i++) {
            BitmapDrawable db_bluecircle = list_bluecircle.get(i);
            drawCircle(canvas, db_bluecircle, 
                maxProgressWidth / (count - 1) * i+offAbs_x, r_white/2+offY, r_blue);
        }
    }

    /**传统矩形坐标方法*/
    public void drawRect(Canvas canvas, Drawable d, int x, int y, int width,
        int height) {
        d.setBounds(x - width / 2, y - height / 2, x + width / 2, y + height 
            / 2);
        d.draw(canvas);
    }

    /**传统圆点坐标计算方法*/
    public void drawCircle(Canvas canvas, Drawable d, int x, int y, int r) {
        d.setBounds(x - r / 2, y - r / 2, x + r / 2, y + r / 2);
        d.draw(canvas);
    }

    /**绘制文字 传统坐标计算方法*/
    public void drawText(Canvas canvas,Paint paint, String str, int x, int y, int w,int h) {
        canvas.drawText(str, x-w/2, y-h/2, x+w, y+h, paint);
    }

    public double getRatio() {
        return ratio;
    }

    public void setRatio(double ratio) {
        this.ratio = ratio;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }
}

使用说明

  1. 资源准备:确保项目 res/drawable 目录下包含以下图片资源:
    • progress_white_circle:白色空心圆
    • progress_blue_circle:蓝色实心圆
    • progress_blue_half_circle:蓝色半圆(用于进度末端)
    • progress_white_groove:白色空心进度条背景
    • progress_blue_groove:蓝色进度条填充
  2. 布局使用:在 XML 布局文件中添加该自定义 View。
  3. 进度控制:通过 setProgressAndIndex(int) 或 setProgressOnly(int) 方法更新进度状态。

目录

  1. 核心代码实现
  2. 使用说明

更多推荐文章

查看全部
  • HarmonyOS6 RcText 组件扩展能力详解
  • 小皮面板 MySQL 启动后立即停止的解决方案
  • 提升 AI 生成前端 UI 设计感的三个关键技巧
  • SpringBoot 整合 DL4J 构建智能写作助手
  • macOS 清理:用免费开源工具 Pearcleaner 彻底卸载应用
  • Ubuntu 22.04 系统安装 MuJoCo 完整指南
  • 大模型技术详解:定义、架构、主流模型及应用场景分析
  • Go Web 开发:网络通信、Socket 与数据库实战
  • VSCode 本地运行 DeepSeek 模型配置指南
  • RAG 系统优化:应对 7 大挑战提升 LLM 性能
  • Claude Code 核心执行模式详解:权限、计划与自动编辑
  • AI 概念解析:LLM、AIGC、AGI 与 GPT 的核心关系及 2025 动态
  • 2026 年 Python 发展局势:AI 时代的通用基础设施语言
  • Criminisi 图像修复算法 MATLAB 实现详解
  • Microsoft Edge WebView2 运行时安装与故障排查指南
  • ChatGPT 结构化 Prompt 的高级应用
  • 大模型分类与主流模型一览
  • 前端微前端架构:利弊分析与实施建议
  • AI 网络技术演进对路由协议的重塑分析
  • 线性表、顺序表与链表详解(C 语言版)

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online