先上图

android P 系统音量条 安卓音量条修改_android P 系统音量条

一.先写个demo,再将demo合并到systemUi中

1.1自定义布局

比较简单,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

<?xml version="1.0" encoding="utf-8"?>
<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>

1.2调用

布局activity_main.xml

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="#AA58c693"
    android:orientation="vertical">


    <!--    <com.zwt.myapplication3.view.FlyMusicView-->
    <!--        android:id="@+id/FlyMusicView"-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="match_parent"-->
    <!--        tools:ignore="InvalidId" />-->
    <com.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"/>

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="add 1" />
    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="remove 1"/>
</LinearLayout>

调用

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中的自定义类放入到下面的位置

frameworks\base\packages\SystemUI\src\com\android\systemui\volume\VolumeRoundProgressBar.java

更改布局

Z:\git_repository\frameworks\base\packages\SystemUI\res\layout\volume_dialog_row.xml

<!--
     Copyright (C) 2015 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<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>

android P 系统音量条 安卓音量条修改_android_02


先将原来的音量条隐藏掉,再将自定义的布局加入上去

音量处理流程可以看这位大神

具体就是当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;
     }
 }

最后实现效果

android P 系统音量条 安卓音量条修改_java_03


百度云:https://pan.baidu.com/s/19ndMC4-IXEgGkow4nyDtog
提取码:fw4u