仪表盘进度条自定义view(步步生金)

效果图

一、绘制自定义view

这个自定义类因为是第一次发布,就不细说了,直接上代码,注解里面大部分也都有。

public class DashboardView extends View {
    private Context context;
    private int maxProgress = 100; // 总进度
    private int currentProgress;
    private boolean isCharging;
    private Paint backgroundPaint;
    private Paint progressPaint;
    private Paint linePaint;
    private Paint ballPaint;
    private ValueAnimator progressAnimator;
    public DashboardView(Context context) {
        super(context);
        Log.d("DashboardView", "DashboardView constructor called");
        this.context=context;
        init();
    }
    public DashboardView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context=context;
        init();
    }

    private void init(){
        backgroundPaint = new Paint();
        backgroundPaint.setColor(Color.TRANSPARENT);

        progressPaint = new Paint();
        progressPaint.setColor(getResources().getColor(R.color.grey));
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeCap(Paint.Cap.ROUND); // 圆环两端为圆形

        //画白线的笔
        linePaint = new Paint();
        linePaint.setColor(Color.WHITE);
        linePaint.setStrokeWidth(5); // 调整线条的宽度
        progressPaint.setStrokeCap(Paint.Cap.ROUND); // 圆环两端为圆形

        //画白球的笔
        ballPaint = new Paint();
        ballPaint.setColor(Color.WHITE);

        //进度条动画
        progressAnimator = ValueAnimator.ofInt(0, currentProgress);
        progressAnimator.setDuration(1000); // 设置动画时长,单位为毫秒
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentProgress = (int) animation.getAnimatedValue();
                invalidate();
            }
        });

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 获取父布局建议的宽高
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        // 取宽高的最小值,确保是正方形
        int size = Math.min(width, height);

        // 设置测量的宽高
        setMeasuredDimension(size, size);
    }

    public void setMaxProgress(int maxProgress){
        Log.d("DashboardView", "setMaxProgress called with: " + maxProgress);
        this.maxProgress=maxProgress;
        invalidate();
    }

    public void setProgress(int progress) {
        Log.d("DashboardView", "setProgress called with: " + progress);
        if (progress < 0) {
            currentProgress = 0;
        } else if (progress > maxProgress) {
            currentProgress = maxProgress;
        } else {
            progressAnimator.setIntValues(currentProgress, progress);
            progressAnimator.start();
        }
        invalidate();
    }

    public void setCharging(boolean charging) {
        Log.d("DashboardView", "setCharging called with: " + charging);
        isCharging = charging;
        invalidate();
    }

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

        Log.d("DashboardView", "onDraw called");

        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;
        int radius = Math.min(centerX, centerY); // 使用整个视图的半径

        // 绘制底部透明背景
        canvas.drawColor(Color.TRANSPARENT);

        // 绘制进度底色(三分之二圆弧)
        progressPaint.setColor(getResources().getColor(R.color.grey));
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(radius*0.2f); // 设置圆环宽度
        RectF ovalBackground = new RectF(centerX - radius+20,
                centerY - radius+20,
                centerX + radius-20,
                centerY + radius-20);
        canvas.drawArc(ovalBackground, -200, 221, false, progressPaint); // 绘制三分之二的圆弧


        // 线条角度
        float sweepAngle = ((float) currentProgress / maxProgress) * 221;
        linePaint.setStrokeWidth(radius * 0.02f); // 设置线条的宽度
        // 只有在有进度时才绘制辅助线条和圆球
        if (currentProgress > 0) {
            // 绘制展示进度的线条
            drawProgressLines(canvas, centerX, centerY, radius, -200, 221, -radius * 0.1f);

            // 计算圆球的位置
            float internalBallAngle = -200 + sweepAngle;
            float internalBallX = (float) (centerX + (radius - 20) * Math.cos(Math.toRadians(internalBallAngle))); // 内部小球
            float internalBallY = (float) (centerY + (radius - 20) * Math.sin(Math.toRadians(internalBallAngle)));

            // 设置背景画笔
            Paint ballBackgroundPaint = new Paint();
            ballBackgroundPaint.setColor(getResources().getColor(R.color.grey)); // 背景颜色为灰色
            ballBackgroundPaint.setStyle(Paint.Style.FILL); // 填充
            ballBackgroundPaint.setAntiAlias(true); // 抗锯齿

            // 绘制白色背景圆
            canvas.drawCircle(internalBallX, internalBallY, radius * 0.1f, ballBackgroundPaint); // 半径为20

            // 设置边框画笔
            Paint ballBorderPaint = new Paint();
            ballBorderPaint.setColor(Color.parseColor("#23C697")); // 边框颜色
            ballBorderPaint.setStyle(Paint.Style.STROKE); // 仅绘制边框
            ballBorderPaint.setStrokeWidth(radius * 0.02f); // 边框宽度
            ballBorderPaint.setAntiAlias(true); // 抗锯齿

            // 绘制圆形边框
            canvas.drawCircle(internalBallX, internalBallY, radius * 0.1f, ballBorderPaint); // 半径为20

            // 设置圆心点画笔
            Paint ballCenterPaint = new Paint();
            ballCenterPaint.setColor(Color.parseColor("#23C697")); // 圆心点颜色
            ballCenterPaint.setStyle(Paint.Style.FILL); // 填充
            ballCenterPaint.setAntiAlias(true); // 抗锯齿

            // 绘制圆心点
            canvas.drawCircle(internalBallX, internalBallY, radius * 0.025f, ballCenterPaint); // 圆心点半径为5
        }

        // 绘制文字
        drawText(canvas, centerX, centerY, "本次成绩", currentProgress + "");
    }
    private void drawProgressLines(Canvas canvas, float centerX, float centerY, float radius, float startAngle, float sweepAngle, float distance) {
        // 只有在有进度时才绘制线条
        if (currentProgress > 0) {
            // 计算当前进度的百分比
            float progressFraction = (float) currentProgress / maxProgress;

            // 计算应该绘制的线条数量
            int totalLines = 100; // 假设总共可以绘制100条线
            int linesToDraw = (int) (totalLines * progressFraction); // 根据进度计算需要绘制的线条数量

            // 起始颜色和结束颜色
            int startColor = Color.parseColor("#FF6666"); // 红色
            int endColor = Color.parseColor("#23C697");  // 绿色

            // 设置线条颜色
            for (int i = 0; i < linesToDraw; i++) {
                // 计算当前线条的角度
                float lineAngle = startAngle + (sweepAngle / totalLines) * i;

                // 根据进度计算颜色
                float fraction = (float) i / linesToDraw; // 计算当前线条的进度
                int currentColor = getGradientColor(fraction, startColor, endColor);
                linePaint.setColor(currentColor); // 当前进度以内的线条使用渐变颜色
                linePaint.setAlpha(255); // 设置完全不透明

                float adjustedRadius = radius - 5; // 向内移动像素(可以根据需要调整这个值)
                float startX = (float) (centerX + (adjustedRadius + distance) * Math.cos(Math.toRadians(lineAngle)));
                float startY = (float) (centerY + (adjustedRadius + distance) * Math.sin(Math.toRadians(lineAngle)));

                float distanceToProgress = 10; // 渐变色线条的高度值
                float endX = (float) (centerX + (radius - distanceToProgress) * Math.cos(Math.toRadians(lineAngle)));
                float endY = (float) (centerY + (radius - distanceToProgress) * Math.sin(Math.toRadians(lineAngle)));

                // 绘制进度线条
                canvas.drawLine(startX, startY, endX, endY, linePaint);
            }
        }
    }
    private void drawText(Canvas canvas,int centerX , int centerY, String chargingText, String percentage) {
        int radius=Math.min(centerX,centerY);//动态获取半径

        TextPaint percentagePaint = new TextPaint();
        percentagePaint.setColor(getResources().getColor(R.color.green));
        percentagePaint.setTextSize(spToPx(45));
        percentagePaint.setAntiAlias(true);
        float percentageWidth = percentagePaint.measureText(percentage);

        TextPaint percentSignPaint = new TextPaint();
        percentSignPaint.setColor(getResources().getColor(R.color.green));
        percentSignPaint.setTextSize(spToPx(45));
        percentSignPaint.setAntiAlias(true);
        float percentSignWidth = percentSignPaint.measureText("%");

        float totalWidth = percentageWidth + percentSignWidth;


        percentagePaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
        canvas.drawText(percentage, centerX - totalWidth / 2, centerY - radius * 0.08f, percentagePaint);

        percentSignPaint.setTextSize(spToPx(22));
        canvas.drawText("分", centerX + totalWidth / 2 - (percentSignWidth - 10), centerY - radius * 0.08f, percentSignPaint);

        TextPaint chargingPaint = new TextPaint();
        chargingPaint.setColor(getResources().getColor(R.color.text_grey));
        chargingPaint.setTextSize(spToPx(18));
        chargingPaint.setAntiAlias(true);
        chargingPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
        float chargingWidth = chargingPaint.measureText(chargingText);
        canvas.drawText(chargingText, centerX - chargingWidth / 2, centerY + radius * 0.25f, chargingPaint);
    }

    //将SP转换为像素值。
    private float spToPx(float sp) {
        float scale = getResources().getDisplayMetrics().scaledDensity;
        return sp * scale;
    }

    //根据进度计算渐变颜色
    private int getGradientColor(float fraction, int startColor, int endColor) {

        int startRed = (startColor >> 16) & 0xFF;
        int startGreen = (startColor >> 8) & 0xFF;
        int startBlue = startColor & 0xFF;

        int endRed = (endColor >> 16) & 0xFF;
        int endGreen = (endColor >> 8) & 0xFF;
        int endBlue = endColor & 0xFF;

        int currentRed = (int) (startRed + (endRed - startRed) * fraction);
        int currentGreen = (int) (startGreen + (endGreen - startGreen) * fraction);
        int currentBlue = (int) (startBlue + (endBlue - startBlue) * fraction);

        return Color.rgb(currentRed, currentGreen, currentBlue);
    }

}

二、页面布局

自定义view的布局大小根据自己需求可以自动调整,但是要注意调整之后的效果,如果大小不合适,会造成部分图丢失。

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

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

        <EditText
            android:id="@+id/edit_content"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:inputType="number"
            android:layout_height="wrap_content"
            android:hint="请输入分数"></EditText>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btn_submit"
            android:text="提交"></Button>
    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:gravity="center">
        <com.example.printprac.DashboardView
            android:layout_width="250dp"
            android:layout_height="250dp"
            android:id="@+id/dashboard_id"></com.example.printprac.DashboardView>
    </RelativeLayout>

</LinearLayout>

 三、页面代码

public class MainActivity extends AppCompatActivity {

    private EditText editText;
    private Button button;
    private DashboardView dashboardView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText=findViewById(R.id.edit_content);
        button=findViewById(R.id.btn_submit);
        dashboardView=findViewById(R.id.dashboard_id);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String editStr = editText.getText().toString();
                try {
                    // 将输入的字符串转换为整数
                    int progress = Integer.parseInt(editStr);
                    // 进行边界检查
                    if (progress > 100) {
                        progress = 100;
                        Toast.makeText(MainActivity.this, "输入的值不能超过 100,已将进度设置为 100", Toast.LENGTH_SHORT).show();
                    }
                    // 调用 DashboardView 的 setProgress 方法设置进度
                    dashboardView.setProgress(progress);
                    // 清空 EditText 的内容
                    editText.setText("");
                } catch (NumberFormatException e) {
                    // 如果输入的不是有效的整数,给出提示
                    Toast.makeText(MainActivity.this, "请输入有效的整数", Toast.LENGTH_SHORT).show();
                    e.printStackTrace();
                }
            }
        });

        dashboardView.setMaxProgress(100);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值