效果图
一、绘制自定义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);
}
}