先上图

比较简单,1.画背景,2.画进度条背景,3.画进度(圆弧),4.画头部的小圆球,5.最后画中间的数字
package com.zwt.myapplication3.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.core.content.ContextCompat;
import com.zwt.myapplication3.R;
public class VolumeRoundProgressBar extends View {
private int bgColor; //背景颜色
private int outsideColor; //外边框颜色
private float outsideRadius; //圆环半径
private int insideColor; //圆环进度条颜色
private int progressTextColor; //中心文字颜色
private float progressTextSize; //中心文字大小
private float progressWidth; //进度条宽度
private int progress; //当前进度值
private int maxProgress; //最大进度的值
private int direction; //进度条方向,从哪个方向开始(右0下1左2上3)
private Paint mPaint_bg;
private Paint mPaint_out_progress;
private Paint mPaint_progress;
private Paint mPaint_ball;
private Paint mPaint_text;
private int[] mColorArray = new int[]{0xFFd42950, 0xFF3af3d4, 0xFF5adcf7, 0xFFd42950};
int circlePoint;//圆心
int radius; //计算半径
public VolumeRoundProgressBar(Context context) {
this(context, null);
}
public VolumeRoundProgressBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VolumeRoundProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.VolumeRoundProgressBar, defStyleAttr, 0);
bgColor = typedArray.getColor(R.styleable.VolumeRoundProgressBar_background_color, 0xffffffff);
outsideColor = typedArray.getColor(R.styleable.VolumeRoundProgressBar_outside_color, 0x36000000);
outsideRadius = typedArray.getDimension(R.styleable.VolumeRoundProgressBar_outside_radius, 60.0f);
insideColor = typedArray.getColor(R.styleable.VolumeRoundProgressBar_inside_color, 0xFF303F9F);
progressTextColor = typedArray.getColor(R.styleable.VolumeRoundProgressBar_progress_text_color, 0xFF303F9F);
progressWidth = typedArray.getDimension(R.styleable.VolumeRoundProgressBar_progress_width, 60.0f);
progress = typedArray.getInt(R.styleable.VolumeRoundProgressBar_progress, 0);
maxProgress = typedArray.getInt(R.styleable.VolumeRoundProgressBar_max_progress, 100);
direction = typedArray.getInt(R.styleable.VolumeRoundProgressBar_direction, 1);
typedArray.recycle();
mPaint_bg = new Paint();
mPaint_out_progress = new Paint();
mPaint_progress = new Paint();
mPaint_ball = new Paint();
mPaint_text = new Paint();
initPaint();
}
public VolumeRoundProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
circlePoint = getWidth() / 2;
radius = (int) (circlePoint-progressWidth);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int circlePoint = getWidth() / 2;
int radius = (int) (circlePoint-progressWidth); //计算半径
//0.背景色圆
canvas.drawCircle(circlePoint, circlePoint, (int) (circlePoint-progressWidth/2), mPaint_bg);// 画出圆
//1.画背景(内层圆)
canvas.drawCircle(circlePoint, circlePoint, radius, mPaint_out_progress);// 画出圆
//2.画进度(圆弧)
Matrix matrix = new Matrix();//用于定义的圆弧的形状和大小的界限
matrix.postRotate(getDegree(direction), circlePoint, circlePoint);
SweepGradient sweepGradient = new SweepGradient(circlePoint, circlePoint, mColorArray, null);
sweepGradient.setLocalMatrix(matrix); //设置背景图片开始位置
// mPaint1.setShader(new LinearGradient(0, 0, 0, getMeasuredWidth(), mColorArray, null, Shader.TileMode.MIRROR));
mPaint_progress.setShader(sweepGradient); //设置背景颜色
RectF oval = new RectF(circlePoint - radius, circlePoint - radius, circlePoint + radius, circlePoint + radius);
canvas.drawArc(oval, getDegree(direction), (360 *progress / maxProgress),false, mPaint_progress);
//3.头部小圆球
float swipe = (360 *progress / maxProgress);
float radians = (float) (((swipe - 90) / 2) / 180 * 2 * Math.PI);
float endX = circlePoint + radius * (float) Math.cos(radians);//x坐标
float endY = circlePoint + radius * (float) Math.sin(radians);//y坐标
canvas.drawCircle(endX, endY, 12, mPaint_ball);
//4.中间文字
Rect rect = new Rect();
mPaint_text.setTextSize((float) (radius*0.8));
String progressText = (int)(((float)progress / maxProgress) * 100) + "";
Log.d("zwt",maxProgress+":::::progress::"+progress+"progressText::"+progressText);
mPaint_text.getTextBounds(progressText, 0, progressText.length(), rect);
Paint.FontMetricsInt fontMetrics = mPaint_text.getFontMetricsInt();
int baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top)/2 - fontMetrics.top;//获得文字的基准线
canvas.drawText(progressText, (getMeasuredWidth()-rect.width()) / 2, baseline, mPaint_text);
}
private void initPaint(){
int circlePoint = getWidth() / 2;
int radius = (int) (circlePoint-progressWidth); //计算半径
/************************* 圆环背景 ****************************/
mPaint_bg.setColor(bgColor); //背景颜色
mPaint_bg.setStyle(Paint.Style.FILL); //实心
//mPaint_bg.setStrokeWidth(progressWidth); //设置圆的宽度
mPaint_bg.setStrokeCap(Paint.Cap.ROUND); //设置圆角
mPaint_bg.setAntiAlias(true); //消除锯齿
/************************* 外环背景 ****************************/
mPaint_out_progress.setColor(outsideColor); //背景颜色
mPaint_out_progress.setStyle(Paint.Style.STROKE); //空心
mPaint_out_progress.setStrokeWidth(progressWidth); //设置圆的宽度
mPaint_out_progress.setStrokeCap(Paint.Cap.ROUND); //设置圆角
mPaint_out_progress.setAntiAlias(true); //消除锯齿
/************************* 进度圆环 ****************************/
mPaint_progress.setStyle(Paint.Style.STROKE); //空心
//mPaint_out_progress.setColor(insideColor); //设置进度条的颜色,纯色,这里使用了SweepGradient渐变色
mPaint_progress.setStrokeWidth(progressWidth); //设置圆环宽度
mPaint_progress.setStrokeCap(Paint.Cap.ROUND); //设置圆角
mPaint_progress.setAntiAlias(true); //消除锯齿
/************************* 头部小圆球 ****************************/
mPaint_ball.setStyle(Paint.Style.FILL); // 填充
mPaint_ball.setStrokeCap(Paint.Cap.ROUND); // 设置圆角
mPaint_ball.setAntiAlias(true); // 设置抗锯齿
mPaint_ball.setDither(true); // 设置抖动
mPaint_ball.setStrokeWidth((float) (progressWidth*0.9));
mPaint_ball.setColor(Color.WHITE);
/************************* 音量数字 ****************************/
mPaint_text.setColor(progressTextColor);
mPaint_text.setStyle(Paint.Style.FILL);
mPaint_text.setAntiAlias(true);
}
private float getDegree(int direction){
return 90*direction;
}
//设置进度
public void setProces(int proces){
if (proces < 0 ){
return;
}else if (proces >= maxProgress){
this.progress = maxProgress;
}else {
this.progress = proces;
}
postInvalidate();
}
public int getProces(){
return this.progress;
}
}
SweepGradient 设置渐变色背景,Matrix 可设置渐变色起始位置以及每种颜色在进度条中的占比
attrs.xml
<resources>
<declare-styleable name="VolumeRoundProgressBar">
<attr name="background_color" format="color" />
<attr name="outside_color" format="color" />
<attr name="outside_radius" format="dimension" />
<attr name="inside_color" format="color" />
<attr name="progress_text_color" format="color" />
<attr name="progress_width" format="dimension" />
<attr name="max_progress" format="integer" />
<attr name="progress" format="integer" />
<attr name="direction">
<enum name="right" value="0" />
<enum name="bottom" value="1" />
<enum name="left" value="2" />
<enum name="top" value="3" />
attr>
declare-styleable>
resources>
布局activity_main.xml
"1.0" encoding="utf-8"?>
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#AA58c693"
android:orientation="vertical">
<!-- .zwt.myapplication3.view.FlyMusicView-->
<!-- android:id="@+id/FlyMusicView"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- tools:ignore="InvalidId" />-->
.zwt.myapplication3.view.VolumeRoundProgressBar
android:id="@+id/VolumeRoundProgressBar"
android:layout_width="200dp"
android:layout_height="200dp"
app:progress="56"
app:direction="top"
android:background="#aac5d687"/>
调用
public class MainActivity extends AppCompatActivity {
private VolumeRoundProgressBar volumeRoundProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("zwt", "onClick: "+(volumeRoundProgressBar.getProces()+1));
volumeRoundProgressBar.setProces(volumeRoundProgressBar.getProces()+1);
}
});
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
volumeRoundProgressBar.setProces(volumeRoundProgressBar.getProces()-1);
}
});
volumeRoundProgressBar = findViewById(R.id.VolumeRoundProgressBar);
}
}
首先先将上面demo中的自定义类放入到下面的位置
frameworks\base\packages\SystemUI\src\com\android\systemui\volume\VolumeRoundProgressBar.java
更改布局
Z:\git_repository\frameworks\base\packages\SystemUI\res\layout\volume_dialog_row.xml
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:tag="row"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:clipChildren="false"
android:clipToPadding="false"
android:theme="@style/qs_theme">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:gravity="center"
android:layout_gravity="center"
android:orientation="horizontal"
android:visibility="gone">
<com.android.keyguard.AlphaOptimizedImageButton
android:id="@+id/volume_row_icon"
style="@style/VolumeButtons"
android:layout_width="@dimen/volume_dialog_tap_target_size"
android:layout_height="@dimen/volume_dialog_tap_target_size"
android:background="@drawable/ripple_drawable_20dp"
android:tint="@color/accent_tint_color_selector"
android:soundEffectsEnabled="false" />
<TextView
android:id="@+id/volume_row_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLength="10"
android:maxLines="1"
android:visibility="gone"
android:textColor="?android:attr/colorControlNormal"
android:textAppearance="@style/TextAppearance.Volume.Header" />
<TextView
android:id="@+id/volume_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="26dip" />
<FrameLayout
android:id="@+id/volume_row_slider_frame"
android:layout_height="match_parent"
android:layoutDirection="ltr"
android:layout_width="@dimen/volume_dialog_row_width">
<SeekBar
android:id="@+id/volume_row_slider"
android:clickable="false"
android:layout_width="@dimen/volume_dialog_row_width"
android:layout_height="match_parent"
android:layoutDirection="ltr"
android:layout_gravity="center"
android:rotation="0" />
FrameLayout>
LinearLayout>
<com.android.systemui.volume.VolumeRoundProgressBar
android:id="@+id/volume_round_progressBar"
android:layout_width="200dp"
android:layout_height="200dp"
app:progress="0"
app:direction="top"
/>
<include layout="@layout/volume_dnd_icon"/>
FrameLayout>

先将原来的音量条隐藏掉,再将自定义的布局加入上去
音量处理流程可以看这位大神
https://blog.csdn.net/qq_33668392/article/details/118679631
具体就是当phonewindowsmanager 监听到音量键按下的时候,会有一个server通知底层改变音量,同时 通知systemui改变ui
更改音量逻辑
\frameworks\base\packages\SystemUI\src\com\android\systemui\volume\VolumeDialogImpl.java
diff --git a/frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImp
index 7d337f3..bc16788 100755
--- a/frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -95,6 +95,7 @@ import com.android.systemui.plugins.VolumeDialogController.StreamState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.volume.VolumeRoundProgressBar;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -185,6 +186,7 @@ public class VolumeDialogImpl implements VolumeDialog {
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ //mWindow.setBackgroundDrawable(new ColorDrawable(Color.BLUE));
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
@@ -194,26 +196,29 @@ public class VolumeDialogImpl implements VolumeDialog {
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
final WindowManager.LayoutParams lp = mWindow.getAttributes();
lp.format = PixelFormat.TRANSLUCENT;
lp.setTitle(VolumeDialogImpl.class.getSimpleName());
if (mContext.getResources().getBoolean(R.bool.config_tvVolumeBar)) {
lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ lp.y = 400;
+ lp.alpha=1.0f;
}
else
lp.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
lp.windowAnimations = -1;
mWindow.setAttributes(lp);
mDialog.setCanceledOnTouchOutside(true);
mDialog.setContentView(R.layout.volume_dialog);
+ mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
mDialog.setOnShowListener(dialog -> {
if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2);
mDialogView.setAlpha(0);
mDialogView.animate()
.alpha(1)
.translationX(0)
@@ -396,6 +401,7 @@ public class VolumeDialogImpl implements VolumeDialog {
row.volumeValue = (TextView) row.view.findViewById(R.id.volume_value);
+ row.volumeRoundProgressBar = (VolumeRoundProgressBar) row.view.findViewById(R.id.volume_round_progressBar);
row.icon = row.view.findViewById(R.id.volume_row_icon);
row.icon.setImageResource(iconRes);
@@ -979,6 +985,7 @@ public class VolumeDialogImpl implements VolumeDialog {
row.volumeValue.setText(String.valueOf(vlevel));
+ row.volumeRoundProgressBar.setProces(vlevel);
}
private void recheckH(VolumeRow row) {
@@ -1210,6 +1217,7 @@ public class VolumeDialogImpl implements VolumeDialog {
if (mRow.requestedLevel != userLevel) {
mRow.volumeValue.setText(String.valueOf(userLevel));
+ mRow.volumeRoundProgressBar.setProces(userLevel);
mController.setStreamVolume(mRow.stream, userLevel);
mRow.requestedLevel = userLevel;
@@ -1322,5 +1330,6 @@ public class VolumeDialogImpl implements VolumeDialog {
private int lastAudibleLevel = 1;
private FrameLayout dndIcon;
private TextView volumeValue;
+ private VolumeRoundProgressBar volumeRoundProgressBar;
}
}
最后实现效果

Demo地址:https://download.csdn.net/download/weixin_44128558/86338338
百度云:https://pan.baidu.com/s/19ndMC4-IXEgGkow4nyDtog
提取码:fw4u