安卓自定义View之进度麦克风
前言
工作需要一个能显示进度的麦克风,即要体现出现在的声音分贝是变化的,所以设计一个带双层贝塞尔曲线的麦克风;
在哪个位置来展现现在声音分贝是变化的呢?思来想去,就用麦克风上面的圆角矩形区域,通过改变一个路径(path)的纵坐标,再画贝塞尔曲线是可以实现的,说干就干!
一、效果图
麦克风进度变化效果
二、自定义属性
先在项目的 \src\main\res\values 路径下新建一个attrs.xml 文件,如果已经存在则直接打开;
新建一个declare-styleable 标签,名字就叫MicView
首先需要画一个麦克风,所以可以先定义 画麦克风 的 画笔颜色和粗细:
<declare-styleable name="MicView">
<!-- 麦克风画笔的颜色-->
<attr name="mic_view_paint_color" format="reference|color" />
<!-- 麦克风画笔的粗细-->
<attr name="mic_view_paint_stroke_width" format="reference|float" />
</declare-styleable>
为了让头部矩形区域的颜色可以单独控制,所以给头部矩形区域的画笔 也定义颜色和粗细:
<declare-styleable name="MicView">
<!-- 省略代码-->
<!-- 麦克风头部矩形区域画笔的颜色-->
<attr name="mic_view_round_rect_paint_color" format="reference|color" />
<!-- 麦克风头部矩形区域画笔的粗细-->
<attr name="mic_view_round_rect_paint_stroke_width" format="reference|float" />
</declare-styleable>
接下来还要能控制头部矩形区域的圆角到底有圆,所以也定义圆角半径x和y;
波浪的宽高;
第一条波浪的颜色,粗细;
第二条波浪的颜色,粗细;
是否显示第二条波浪?
现在的进度?即曲线的高度占头部矩形区域的百分比是多少?
还要显示文字,比如 50%;
文字颜色,尺寸;
这些都定义好属性,这样方便在xml设计时,随时更改参数来看预览效果;
不断的调整,最终用得较多的属性定义如下:
<declare-styleable name="MicView">
<!-- 麦克风画笔的颜色-->
<attr name="mic_view_paint_color" format="reference|color" />
<!-- 麦克风画笔的粗细-->
<attr name="mic_view_paint_stroke_width" format="reference|float" />
<!-- 麦克风头部矩形区域画笔的颜色-->
<attr name="mic_view_round_rect_paint_color" format="reference|color" />
<!-- 麦克风头部矩形区域画笔的粗细-->
<attr name="mic_view_round_rect_paint_stroke_width" format="reference|float" />
<!-- 麦克风头部矩形圆角半径x-->
<attr name="mic_view_round_rect_corner_radius_x" format="reference|float" />
<!-- 麦克风头部矩形圆角半径y-->
<attr name="mic_view_round_rect_corner_radius_y" format="reference|float" />
<!-- 波浪的宽度-->
<attr name="mic_view_wave_width" format="reference|dimension" />
<!-- 波浪的高度-->
<attr name="mic_view_wave_height" format="reference|dimension" />
<!-- 第一条曲线画笔的颜色-->
<attr name="mic_view_wave_paint_color" format="reference|color" />
<!-- 第一条曲线画笔的粗细-->
<attr name="mic_view_wave_paint_stroke_width" format="reference|float" />
<!-- 是否绘制第二条曲线-->
<attr name="mic_view_is_show_second_wave" format="reference|boolean" />
<!-- 第二条曲线的颜色-->
<attr name="mic_view_second_wave_paint_color" format="reference|color" />
<!-- 第二条曲线画笔的粗细-->
<attr name="mic_view_second_wave_paint_stroke_width" format="reference|float" />
<!-- 进度-->
<attr name="mic_view_progress_mic" format="reference|integer" />
<!-- 文字画笔的颜色-->
<attr name="mic_view_text_paint_color" format="reference|color" />
<!-- 文字内容-->
<attr name="mic_view_text_str" format="reference|string" />
<!-- 文字尺寸-->
<attr name="mic_view_text_size" format="reference|integer|float" />
<!-- 动画的更新间隔 快慢-->
<attr name="mic_view_anim_interval" format="reference|integer" />
</declare-styleable>
三、关键设计
1.新建MicView继承View
1.1新增必要的构造函数
package com.linkpoon.mixed.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class MicView extends View {
public MicView(Context context) {
this(context, null);
}
public MicView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MicView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
}
1.2读取自定义属性并初始化
private RectF roundRectF; /*麦克风上部的圆角矩形区域*/
private float cornerRadiusX = 150;// 圆角矩形的 圆角半径
private float cornerRadiusY = 150;// 圆角矩形的 圆角半径
private RectF arcRectF; /*麦克风外围的U形区域*/
private Paint roundPaint; /*麦克风上部的圆角矩形画笔*/
private Paint micPaint; /*麦克风画笔*/
private Path roundPath;/*麦克风上部的圆角矩形路径*/
private int defaultSize = 200;//自定义View默认的宽高
/*波浪曲线画笔*/
private Paint wavePaint;
/*波浪曲线的路径*/
private Path wavePath;
private float waveWidth;//波浪宽度
private float waveHeight;//波浪高度
private int waveNum;//波浪组的数量(一次起伏为一组)
private float waveMovingDistance;//波浪平移的距离
/*第二层波浪曲线画笔*/
private Paint secondWavePaint;
private Path secondWavePath;// 第二层波浪路径
private boolean showSecondWave;//是否绘制第二层波浪
/*文本画笔*/
private Paint textPaint;
/*要显示的文字*/
private String textStr;
/*当前进度*/
private int progress = 0;
public static final int MAX_PROGRESS = 100;//先不考虑可以动态设置 进度最大值的问题,默认最大值为 100
public static final int MIN_PROGRESS = 0;//先不考虑可以动态设置 进度最小值的问题,默认最小值为 0
/*是否正在动画*/
private boolean isAnimating = false;
private double steep = 0;
private final Random random = new Random();
public String getTextStr() {
return textStr;
}
public void setTextStr(String textStr) {
if (textStr == null) {
textStr = "";
}
this.textStr = textStr;
}
public boolean isShowSecondWave() {
return showSecondWave;
}
public void setShowSecondWave(boolean showSecondWave) {
this.showSecondWave = showSecondWave;
invalidate();
}
public void setAnimating(boolean animating) {
isAnimating = animating;
}
public boolean isAnimating() {
return isAnimating;
}
/**
* 将 dp 转换成 px
*/
private int dipToPx(float dip) {
float density = getResources().getDisplayMetrics().density;
return (int) ((dip * density) + 0.5f);
}
private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
roundPath = new Path();
arcRectF = new RectF();
roundRectF = new RectF();
try (TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MicView)) {
//波浪的宽度
waveWidth = typedArray.getDimension(R.styleable.MicView_mic_view_wave_width, dipToPx(25));
//波浪的高度
waveHeight = typedArray.getDimension(R.styleable.MicView_mic_view_wave_height, dipToPx(5));
//240 255 255 #F0FFFF 灰白色
int roundPaintColorDefault = Color.argb(255, 240, 255, 255);
int roundPaintColor = typedArray.getColor(R.styleable.MicView_mic_view_round_rect_paint_color, roundPaintColorDefault);
float roundStrokeWidth = typedArray.getFloat(R.styleable.MicView_mic_view_round_rect_paint_stroke_width, dipToPx(1));
roundPaint = new Paint();
roundPaint.setAntiAlias(true);
roundPaint.setColor(roundPaintColor);
roundPaint.setStrokeWidth(roundStrokeWidth);
roundPaint.setStyle(Paint.Style.FILL);
cornerRadiusX = typedArray.getFloat(R.styleable.MicView_mic_view_round_rect_corner_radius_x, 150);
cornerRadiusY = typedArray.getFloat(R.styleable.MicView_mic_view_round_rect_corner_radius_y, 150);
//176 224 230 #B0E0E6 力量蓝
int micPaintColorDefault = Color.argb(255, 176, 224, 230);
int micPaintColor = typedArray.getColor(R.styleable.MicView_mic_view_paint_color, micPaintColorDefault);
float micStrokeWidth = typedArray.getFloat(R.styleable.MicView_mic_view_paint_stroke_width, dipToPx(5));
micPaint = new Paint();
micPaint.setAntiAlias(true);
micPaint.setColor(micPaintColor);
micPaint.setStrokeWidth(micStrokeWidth);
micPaint.setStyle(Paint.Style.STROKE);
int wavePaintColorDefault = getResources().getColor(R.color.colorPrimaryHalf);
int wavePaintColor = typedArray.getColor(R.styleable.MicView_mic_view_wave_paint_color, wavePaintColorDefault);
float waveStrokeWidth = typedArray.getFloat(R.styleable.MicView_mic_view_wave_paint_stroke_width, dipToPx(5));
wavePath = new Path();
wavePaint = new Paint();
wavePaint.setAntiAlias(true);
wavePaint.setColor(wavePaintColor);
wavePaint.setStrokeWidth(waveStrokeWidth);
wavePaint.setStyle(Paint.Style.FILL);
//154 255 154 #9AFF9A 绿色
int secondWavePaintColorDefault = Color.argb(255, 154, 255, 154);
int secondWaveColor = typedArray.getColor(R.styleable.MicView_mic_view_second_wave_paint_color, secondWavePaintColorDefault);
float secondWaveStrokeWidth = typedArray.getFloat(R.styleable.MicView_mic_view_second_wave_paint_stroke_width, dipToPx(5));
secondWavePath = new Path();
secondWavePaint = new Paint();
secondWavePaint.setAntiAlias(true);
secondWavePaint.