开源 java android app 开发(十六)自定义绘图控件--圆环

Android自定义圆环控件开发

 文章的目的为了记录使用java 进行android app 开发学习的经历。本职为嵌入式软件开发,公司安排开发app,临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

   相关链接:

开源 java android app 开发(一)开发环境的搭建-优快云博客

开源 java android app 开发(二)工程文件结构-优快云博客

开源 java android app 开发(三)GUI界面布局和常用组件-优快云博客

开源 java android app 开发(四)GUI界面重要组件-优快云博客

开源 java android app 开发(五)文件和数据库存储-优快云博客

开源 java android app 开发(六)多媒体使用-优快云博客

开源 java android app 开发(七)通讯之Tcp和Http-优快云博客

开源 java android app 开发(八)通讯之Mqtt和Ble-优快云博客

开源 java android app 开发(九)后台之线程和服务-优快云博客

开源 java android app 开发(十)广播机制-优快云博客

开源 java android app 开发(十一)调试、发布-优快云博客

开源 java android app 开发(十二)封库.aar-优快云博客

开源 java android app 开发(十三)自定义绘图控件--游戏摇杆

开源 java android app 开发(十四)自定义绘图控件--波形图

开源 java android app 开发(十五)自定义绘图控件--仪表盘

开源 java android app 开发(十六)自定义绘图控件--圆环

推荐链接:

开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-优快云博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-优快云博客

开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-优快云博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-优快云博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-优快云博客

本章节主要内容是:自定义的圆环控件的使用,随机指向速度。

1.代码分析

2.所有源码

3.效果演示

一、代码分析

DataBoard 自定义视图详细分析
1. 类结构和成员变量
1.1 默认常量定义

private static final int DEFAULT_OUTER_DIAMETER = 200; // dp
private static final int DEFAULT_RING_WIDTH = 20; // dp
// ... 其他默认值
定义了所有可配置参数的默认值

尺寸单位使用dp,颜色使用Android系统常量

1.2 绘制相关对象

private Paint mRingPaint;    // 圆环画笔
private Paint mTextPaint;    // 文字画笔
private RectF mRingRect;     // 圆环绘制区域
private Rect mTextBounds;    // 文字边界测量


1.3 自定义属性变量

private float mOuterDiameter;    // 外径
private float mRingWidth;        // 圆环宽度
private int mRingBaseColor;      // 基础圆环颜色
// ... 其他属性


2. 构造函数和初始化
2.1 构造函数链
java
public DataBoard(Context context) {
    this(context, null);
}

public DataBoard(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public DataBoard(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs);
}
遵循Android自定义View的标准构造函数模式

最终都调用init()方法进行统一初始化

2.2 init() 方法

private void init(Context context, AttributeSet attrs) {
    // 密度转换
    float density = getResources().getDisplayMetrics().density;
    mOuterDiameter = DEFAULT_OUTER_DIAMETER * density;
    mRingWidth = DEFAULT_RING_WIDTH * density;
    // ... 其他默认值设置
    
    // 自定义属性处理
    if (attrs != null) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DataBoard);
        mOuterDiameter = ta.getDimension(R.styleable.DataBoard_db_outerDiameter, mOuterDiameter);
        // ... 逐个获取属性值
        ta.recycle();  // 重要:必须回收TypedArray
    }
    
    // 画笔初始化
    mRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mRingPaint.setStyle(Paint.Style.STROKE);
    mRingPaint.setStrokeWidth(mRingWidth);
    mRingPaint.setStrokeCap(Paint.Cap.ROUND);  // 圆角端点
    
    mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTextPaint.setColor(mTextColor);
    mTextPaint.setTextSize(mTextSize);
    mTextPaint.setTextAlign(Paint.Align.CENTER);  // 文字水平居中
    
    // 矩形区域初始化
    mRingRect = new RectF();
    mTextBounds = new Rect();
    
    setBackgroundColor(Color.WHITE);
}


3. 视图测量和布局
3.1 onMeasure() 方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 计算期望尺寸(包含padding)
    int desiredWidth = (int) mOuterDiameter + getPaddingLeft() + getPaddingRight();
    int desiredHeight = (int) mOuterDiameter + getPaddingTop() + getPaddingBottom();
    
    // 获取测量模式和尺寸
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    
    int width, height;
    
    // 宽度测量逻辑
    if (widthMode == MeasureSpec.EXACTLY) {
        width = widthSize;  // 父视图指定确切尺寸
    } else if (widthMode == MeasureSpec.AT_MOST) {
        width = Math.min(desiredWidth, widthSize);  // 不能超过父视图限制
    } else {
        width = desiredWidth;  // 未指定限制,使用期望尺寸
    }
    
    // 高度测量逻辑(同上)
    if (heightMode == MeasureSpec.EXACTLY) {
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        height = Math.min(desiredHeight, heightSize);
    } else {
        height = desiredHeight;
    }
    
    setMeasuredDimension(width, height);
}


3.2 onSizeChanged() 方法
java
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    
    // 计算中心点坐标
    mCenterX = w / 2f;
    mCenterY = h / 2f;
    
    // 计算可用空间(考虑padding)
    float availableWidth = w - getPaddingLeft() - getPaddingRight();
    float availableHeight = h - getPaddingTop() - getPaddingBottom();
    
    // 计算半径(缩小10%并考虑圆环宽度)
    float sizeReduction = 0.1f;
    float reducedSize = Math.min(availableWidth, availableHeight) * (1 - sizeReduction);
    mRadius = reducedSize / 2f - mRingWidth / 2f;  // 减去圆环宽度的一半
    
    // 设置圆环绘制区域
    float left = mCenterX - mRadius;
    float top = mCenterY - mRadius;
    float right = mCenterX + mRadius;
    float bottom = mCenterY + mRadius;
    mRingRect.set(left, top, right, bottom);
}


4. 绘制方法详解
4.1 onDraw() 主绘制方法

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    
    drawRingEdges(canvas);    // 1. 绘制渐变边缘
    drawBaseRing(canvas);     // 2. 绘制基础圆环
    drawDataRing(canvas);     // 3. 绘制数据圆环
    drawCenterText(canvas);   // 4. 绘制中心文字
}


4.2 drawRingEdges() - 渐变边缘绘制
java
private void drawRingEdges(Canvas canvas) {
    Paint edgePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    edgePaint.setStyle(Paint.Style.STROKE);
    
    // 外圈渐变(深灰到黑)
    Shader outerShader = new RadialGradient(
            mCenterX, mCenterY, mRadius + mRingWidth / 2,  // 外圈半径
            mOuterStartColor, mOuterEndColor, Shader.TileMode.CLAMP);
    edgePaint.setShader(outerShader);
    edgePaint.setStrokeWidth(2);  // 边缘线宽
    canvas.drawCircle(mCenterX, mCenterY, mRadius + mRingWidth / 2, edgePaint);
    
    // 内圈渐变(白到浅灰)
    Shader innerShader = new RadialGradient(
            mCenterX, mCenterY, mRadius - mRingWidth / 2,  // 内圈半径
            mInnerStartColor, mInnerEndColor, Shader.TileMode.CLAMP);
    edgePaint.setShader(innerShader);
    canvas.drawCircle(mCenterX, mCenterY, mRadius - mRingWidth / 2, edgePaint);
}


4.3 drawBaseRing() - 基础圆环绘制
java
private void drawBaseRing(Canvas canvas) {
    mRingPaint.setColor(mRingBaseColor);
    canvas.drawArc(mRingRect, 0, 360, false, mRingPaint);  // 绘制完整圆环
}


4.4 drawDataRing() - 数据圆环绘制
java
private void drawDataRing(Canvas canvas) {
    if (mCurrentValue <= 0) return;  // 无数据时不绘制
    
    // 计算扫描角度(基于当前值与最大值的比例)
    float sweepAngle = (mCurrentValue / mMaxValue) * 360;
    sweepAngle = Math.min(sweepAngle, 360);  // 限制最大360度
    
    mRingPaint.setColor(mRingDataColor);
    // 从270度开始(12点方向),顺时针绘制
    canvas.drawArc(mRingRect, 270, sweepAngle, false, mRingPaint);
    
    // 注释掉的半量程颜色变化逻辑
    /*
    if (mCurrentValue <= mMaxValue / 2) {
        // 半量程内使用数据颜色
        mRingPaint.setColor(mRingDataColor);
        canvas.drawArc(mRingRect, 270, sweepAngle, false, mRingPaint);
    } else {
        // 超过半量程,后半部分使用基础颜色
        float firstHalfAngle = 180;
        float secondHalfAngle = sweepAngle - firstHalfAngle;
        
        mRingPaint.setColor(mRingDataColor);
        canvas.drawArc(mRingRect, 270, firstHalfAngle, false, mRingPaint);
        
        mRingPaint.setColor(mRingBaseColor);
        canvas.drawArc(mRingRect, 270 + firstHalfAngle, secondHalfAngle, false, mRingPaint);
    }
    */
}


4.5 drawCenterText() - 中心文字绘制


private void drawCenterText(Canvas canvas) {
    // 格式化文本:"当前值/最大值"
    String text = String.format("%.1f/% .1f", mCurrentValue, mMaxValue);
    
    // 获取文字边界用于垂直居中计算
    mTextPaint.getTextBounds(text, 0, text.length(), mTextBounds);
    
    // 计算垂直居中位置
    float textY = mCenterY - (mTextBounds.top + mTextBounds.bottom) / 2f;
    
    canvas.drawText(text, mCenterX, textY, mTextPaint);
}


5. 公共方法(属性设置)
5.1 数值设置方法


public void setCurrentValue(float value) {
    mCurrentValue = Math.max(0, Math.min(value, mMaxValue));  // 边界检查
    invalidate();  // 请求重绘
}

public void setMaxValue(float maxValue) {
    mMaxValue = Math.max(0, maxValue);
    if (mCurrentValue > mMaxValue) {
        mCurrentValue = mMaxValue;  // 调整当前值不超过新最大值
    }
    invalidate();
}


5.2 样式设置方法
java
public void setRingWidth(float width) {
    mRingWidth = width;
    mRingPaint.setStrokeWidth(mRingWidth);
    requestLayout();  // 需要重新布局(尺寸可能改变)
    invalidate();     // 需要重绘
}

public void setOuterDiameter(float diameter) {
    mOuterDiameter = diameter;
    requestLayout();  // 尺寸改变,需要重新测量布局
    invalidate();
}
 

二、所有源码

需要修改的文件有5个

1.  DataBoard.java代码

package com.example.dashboard;



import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;

public class DataBoard extends View {

    // 默认值
    private static final int DEFAULT_OUTER_DIAMETER = 200; // dp
    private static final int DEFAULT_RING_WIDTH = 20; // dp
    private static final int DEFAULT_BASE_COLOR = Color.LTGRAY;
    private static final int DEFAULT_DATA_COLOR = Color.BLUE;
    private static final int DEFAULT_TEXT_COLOR = Color.BLACK;
    private static final int DEFAULT_TEXT_SIZE = 16; // sp
    private static final float DEFAULT_MAX_VALUE = 100f;
    private static final float DEFAULT_CURRENT_VALUE = 0f;

    // 绘制相关
    private Paint mRingPaint;
    private Paint mTextPaint;
    private RectF mRingRect;
    private Rect mTextBounds;

    // 自定义属性
    private float mOuterDiameter;
    private float mRingWidth;
    private int mRingBaseColor;
    private int mRingDataColor;
    private int mTextColor;
    private float mTextSize;
    private float mMaxValue;
    private float mCurrentValue;
    private int mInnerStartColor;
    private int mInnerEndColor;
    private int mOuterStartColor;
    private int mOuterEndColor;

    // 计算属性
    private float mCenterX;
    private float mCenterY;
    private float mRadius;

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

    public DataBoard(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DataBoard(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        // 初始化默认值
        float density = getResources().getDisplayMetrics().density;
        mOuterDiameter = DEFAULT_OUTER_DIAMETER * density;
        mRingWidth = DEFAULT_RING_WIDTH * density;
        mRingBaseColor = DEFAULT_BASE_COLOR;
        mRingDataColor = DEFAULT_DATA_COLOR;
        mTextColor = DEFAULT_TEXT_COLOR;
        mTextSize = DEFAULT_TEXT_SIZE * density;
        mMaxValue = DEFAULT_MAX_VALUE;
        mCurrentValue = DEFAULT_CURRENT_VALUE;
        mInnerStartColor = Color.WHITE;
        mInnerEndColor = Color.LTGRAY;
        mOuterStartColor = Color.DKGRAY;
        mOuterEndColor = Color.BLACK;

        // 获取自定义属性
        if (attrs != null) {
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DataBoard);
            mOuterDiameter = ta.getDimension(R.styleable.DataBoard_db_outerDiameter, mOuterDiameter);
            mRingWidth = ta.getDimension(R.styleable.DataBoard_db_ringWidth, mRingWidth);
            mRingBaseColor = ta.getColor(R.styleable.DataBoard_db_ringBaseColor, mRingBaseColor);
            mRingDataColor = ta.getColor(R.styleable.DataBoard_db_ringDataColor, mRingDataColor);
            mTextColor = ta.getColor(R.styleable.DataBoard_db_textColor, mTextColor);
            mTextSize = ta.getDimension(R.styleable.DataBoard_db_textSize, mTextSize);
            mMaxValue = ta.getFloat(R.styleable.DataBoard_db_maxValue, mMaxValue);
            mCurrentValue = ta.getFloat(R.styleable.DataBoard_db_currentValue, mCurrentValue);
            mInnerStartColor = ta.getColor(R.styleable.DataBoard_db_innerStartColor, mInnerStartColor);
            mInnerEndColor = ta.getColor(R.styleable.DataBoard_db_innerEndColor, mInnerEndColor);
            mOuterStartColor = ta.getColor(R.styleable.DataBoard_db_outerStartColor, mOuterStartColor);
            mOuterEndColor = ta.getColor(R.styleable.DataBoard_db_outerEndColor, mOuterEndColor);
            ta.recycle();
        }

        // 初始化圆环画笔
        mRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mRingPaint.setStyle(Paint.Style.STROKE);
        mRingPaint.setStrokeWidth(mRingWidth);
        mRingPaint.setStrokeCap(Paint.Cap.ROUND);

        // 初始化文字画笔
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setTextAlign(Paint.Align.CENTER);

        mRingRect = new RectF();
        mTextBounds = new Rect();

        // 设置白色背景
        setBackgroundColor(Color.WHITE);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int desiredWidth = (int) mOuterDiameter + getPaddingLeft() + getPaddingRight();
        int desiredHeight = (int) mOuterDiameter + getPaddingTop() + getPaddingBottom();

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width, height;

        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else if (widthMode == MeasureSpec.AT_MOST) {
            width = Math.min(desiredWidth, widthSize);
        } else {
            width = desiredWidth;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else if (heightMode == MeasureSpec.AT_MOST) {
            height = Math.min(desiredHeight, heightSize);
        } else {
            height = desiredHeight;
        }

        setMeasuredDimension(width, height);
    }
/*
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        // 计算中心点
        mCenterX = w / 2f;
        mCenterY = h / 2f;

        // 计算半径(考虑padding)
        float availableWidth = w - getPaddingLeft() - getPaddingRight();
        float availableHeight = h - getPaddingTop() - getPaddingBottom();
        mRadius = Math.min(availableWidth, availableHeight) / 2f - mRingWidth / 2f;

        // 设置圆环绘制区域
        float left = mCenterX - mRadius;
        float top = mCenterY - mRadius;
        float right = mCenterX + mRadius;
        float bottom = mCenterY + mRadius;
        mRingRect.set(left, top, right, bottom);
    }
*/


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        // 计算中心点
        mCenterX = w / 2f;
        mCenterY = h / 2f;

        // 计算半径(考虑padding和额外的边距)
        float availableWidth = w - getPaddingLeft() - getPaddingRight();
        float availableHeight = h - getPaddingTop() - getPaddingBottom();

        // 设置圆环比可用空间小一些(例如小10%)
        float sizeReduction = 0.1f; // 缩小10%
        float reducedSize = Math.min(availableWidth, availableHeight) * (1 - sizeReduction);
        mRadius = reducedSize / 2f - mRingWidth / 2f;

        // 或者固定缩小一定的像素值
        // float fixedMargin = 20f; // 缩小20像素
        // float baseSize = Math.min(availableWidth, availableHeight);
        // mRadius = (baseSize - fixedMargin * 2) / 2f - mRingWidth / 2f;

        // 设置圆环绘制区域
        float left = mCenterX - mRadius;
        float top = mCenterY - mRadius;
        float right = mCenterX + mRadius;
        float bottom = mCenterY + mRadius;
        mRingRect.set(left, top, right, bottom);
    }

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

        // 绘制内外径边的渐变效果
        drawRingEdges(canvas);

        // 绘制基础圆环
        drawBaseRing(canvas);

        // 绘制数据圆环
        drawDataRing(canvas);

        // 绘制中心文字
        drawCenterText(canvas);
    }

    private void drawRingEdges(Canvas canvas) {
        // 绘制外径边的渐变
        Paint edgePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        edgePaint.setStyle(Paint.Style.STROKE);

        // 外圈渐变
        Shader outerShader = new RadialGradient(
                mCenterX, mCenterY, mRadius + mRingWidth / 2,
                mOuterStartColor, mOuterEndColor, Shader.TileMode.CLAMP);
        edgePaint.setShader(outerShader);
        edgePaint.setStrokeWidth(2);
        canvas.drawCircle(mCenterX, mCenterY, mRadius + mRingWidth / 2, edgePaint);

        // 内圈渐变
        Shader innerShader = new RadialGradient(
                mCenterX, mCenterY, mRadius - mRingWidth / 2,
                mInnerStartColor, mInnerEndColor, Shader.TileMode.CLAMP);
        edgePaint.setShader(innerShader);
        canvas.drawCircle(mCenterX, mCenterY, mRadius - mRingWidth / 2, edgePaint);
    }

    private void drawBaseRing(Canvas canvas) {
        mRingPaint.setColor(mRingBaseColor);
        // 绘制完整的圆环作为背景
        canvas.drawArc(mRingRect, 0, 360, false, mRingPaint);
    }

    private void drawDataRing(Canvas canvas) {
        if (mCurrentValue <= 0) return;

        // 计算数据对应的角度(270°开始,顺时针)
        float sweepAngle = (mCurrentValue / mMaxValue) * 360;
        sweepAngle = Math.min(sweepAngle, 360); // 不超过360度



        /*
        // 判断是否超过半量程
        if (mCurrentValue <= mMaxValue / 2) {
            // 半量程以内,全部使用数据颜色
            mRingPaint.setColor(mRingDataColor);
            canvas.drawArc(mRingRect, 270, sweepAngle, false, mRingPaint);
        } else {
            // 超过半量程,前半部分数据颜色,后半部分基本颜色
            float firstHalfAngle = 180; // 前半部分180度
            float secondHalfAngle = sweepAngle - firstHalfAngle;

            // 绘制前半部分(数据颜色)
            mRingPaint.setColor(mRingDataColor);
            canvas.drawArc(mRingRect, 270, firstHalfAngle, false, mRingPaint);

            // 绘制后半部分(基本颜色)
            mRingPaint.setColor(mRingBaseColor);
            canvas.drawArc(mRingRect, 270 + firstHalfAngle, secondHalfAngle, false, mRingPaint);
        }
         */

        mRingPaint.setColor(mRingDataColor);
        canvas.drawArc(mRingRect, 270, sweepAngle, false, mRingPaint);
    }

    private void drawCenterText(Canvas canvas) {
        String text = String.format("%.1f/% .1f", mCurrentValue, mMaxValue);
        mTextPaint.getTextBounds(text, 0, text.length(), mTextBounds);

        // 计算文字垂直居中位置
        float textY = mCenterY - (mTextBounds.top + mTextBounds.bottom) / 2f;
        canvas.drawText(text, mCenterX, textY, mTextPaint);
    }

    // 公共方法 - 设置当前值
    public void setCurrentValue(float value) {
        mCurrentValue = Math.max(0, Math.min(value, mMaxValue));
        invalidate();
    }

    // 公共方法 - 设置最大值
    public void setMaxValue(float maxValue) {
        mMaxValue = Math.max(0, maxValue);
        if (mCurrentValue > mMaxValue) {
            mCurrentValue = mMaxValue;
        }
        invalidate();
    }

    // 公共方法 - 设置圆环宽度
    public void setRingWidth(float width) {
        mRingWidth = width;
        mRingPaint.setStrokeWidth(mRingWidth);
        requestLayout();
        invalidate();
    }

    // 公共方法 - 设置圆环基本颜色
    public void setRingBaseColor(int color) {
        mRingBaseColor = color;
        invalidate();
    }

    // 公共方法 - 设置圆环数据颜色
    public void setRingDataColor(int color) {
        mRingDataColor = color;
        invalidate();
    }

    // 公共方法 - 设置文字颜色
    public void setTextColor(int color) {
        mTextColor = color;
        mTextPaint.setColor(mTextColor);
        invalidate();
    }

    // 公共方法 - 设置外圈直径
    public void setOuterDiameter(float diameter) {
        mOuterDiameter = diameter;
        requestLayout();
        invalidate();
    }

    // 获取当前值
    public float getCurrentValue() {
        return mCurrentValue;
    }

    // 获取最大值
    public float getMaxValue() {
        return mMaxValue;
    }
}

2.  mainactivity.java代码

package com.example.dashboard;


import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Button;
import android.widget.SeekBar;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private DataBoard dataBoard;
    private Button btnIncrease, btnDecrease;
    private SeekBar seekBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
        setupListeners();
    }

    private void initViews() {
        dataBoard = findViewById(R.id.dataBoard);
        btnIncrease = findViewById(R.id.btnIncrease);
        btnDecrease = findViewById(R.id.btnDecrease);
        seekBar = findViewById(R.id.seekBar);

        // 设置SeekBar初始值
        seekBar.setProgress((int) dataBoard.getCurrentValue());
    }

    private void setupListeners() {
        btnIncrease.setOnClickListener(v -> {
            float current = dataBoard.getCurrentValue();
            if (current < dataBoard.getMaxValue()) {
                dataBoard.setCurrentValue(current + 10);
                seekBar.setProgress((int) dataBoard.getCurrentValue());
            }
        });

        btnDecrease.setOnClickListener(v -> {
            float current = dataBoard.getCurrentValue();
            if (current > 0) {
                dataBoard.setCurrentValue(current - 10);
                seekBar.setProgress((int) dataBoard.getCurrentValue());
            }
        });

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser) {
                    dataBoard.setCurrentValue(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {}

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {}
        });
    }


}

3.  acivitiy_main.xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:padding="16dp"
    android:background="#FFFFFF"
    tools:context=".MainActivity">

    <com.example.dashboard.DataBoard
        android:id="@+id/dataBoard"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:background="#FFFFFF"
        app:db_outerDiameter="180dp"
        app:db_ringWidth="20dp"
        app:db_ringBaseColor="#E0E0E0"
        app:db_ringDataColor="#2196F3"
        app:db_textColor="#333333"
        app:db_textSize="18sp"
        app:db_maxValue="100"
        app:db_currentValue="75"
        app:db_innerStartColor="#FFFFFF"
        app:db_innerEndColor="#E0E0E0"
        app:db_outerStartColor="#BDBDBD"
        app:db_outerEndColor="#616161" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="20dp">

        <Button
            android:id="@+id/btnIncrease"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="增加"
            />

        <Button
            android:id="@+id/btnDecrease"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="减少"
             />

    </LinearLayout>

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:max="100" />

</LinearLayout>

4.  attrs.xml代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="DataBoard">
        <!-- 圆环外圈直径 -->
        <attr name="db_outerDiameter" format="dimension" />
        <!-- 圆环宽度 -->
        <attr name="db_ringWidth" format="dimension" />
        <!-- 圆环基本颜色 -->
        <attr name="db_ringBaseColor" format="color" />
        <!-- 圆环数据颜色 -->
        <attr name="db_ringDataColor" format="color" />
        <!-- 中心文字颜色 -->
        <attr name="db_textColor" format="color" />
        <!-- 中心文字大小 -->
        <attr name="db_textSize" format="dimension" />
        <!-- 量程最大值 -->
        <attr name="db_maxValue" format="float" />
        <!-- 当前值 -->
        <attr name="db_currentValue" format="float" />
        <!-- 内圈渐变起始颜色 -->
        <attr name="db_innerStartColor" format="color" />
        <!-- 内圈渐变结束颜色 -->
        <attr name="db_innerEndColor" format="color" />
        <!-- 外圈渐变起始颜色 -->
        <attr name="db_outerStartColor" format="color" />
        <!-- 外圈渐变结束颜色 -->
        <attr name="db_outerEndColor" format="color" />
    </declare-styleable>
</resources>

5.  colors.xml代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>


    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>

    <!-- 仪表盘自定义颜色 -->
    <color name="dialColor">#3F51B5</color>
    <color name="needleColor">#FF4081</color>
    <color name="textColor">#212121</color>
    <color name="backgroundColor">#FFFFFF</color>

</resources>

三、效果演示

主活动中添加了圆环控件,使用按钮可以增加和减少圆环控件的显示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值