先上图
比较简单,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
1.2调用
布局activity_main.xml
调用
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); } }二.合并到systemui中
首先先将上面demo中的自定义类放入到下面的位置
frameworksbasepackagesSystemUIsrccomandroidsystemuivolumeVolumeRoundProgressBar.java
更改布局
Z:git_repositoryframeworksbasepackagesSystemUIreslayoutvolume_dialog_row.xml
先将原来的音量条隐藏掉,再将自定义的布局加入上去
音量处理流程可以看这位大神
https://blog.csdn.net/qq_33668392/article/details/118679631
具体就是当phonewindowsmanager 监听到音量键按下的时候,会有一个server通知底层改变音量,同时 通知systemui改变ui
更改音量逻辑
frameworksbasepackagesSystemUIsrccomandroidsystemuivolumeVolumeDialogImpl.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