想要繪製一個矩形,繼承View,並重寫onDraw方法便可。複雜一點還能夠重寫onMeasure方法和onLayout方法進行大小測量和位置測量。但本文不打算寫那麼複雜的view,故只須要重寫一個onDraw方法便可:java
private RectF rectF = new RectF();//繪製矩形
private float lineWidth = 50;
private Paint paint = new Paint();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//設置顏色
paint.setColor(context.getColor(R.color.colorPrimary));
//填充內部
paint.setStyle(Paint.Style.FILL);
//設置抗鋸齒
paint.setAntiAlias(true);
//繪製矩形的四邊
int widthCentre = getWidth() / 2;
int heightCentre = getHeight() / 2;
rectF.left = widthCentre - lineWidth / 2;
rectF.right = widthCentre + lineWidth / 2;
rectF.top = heightCentre - lineWidth * 2;
rectF.bottom = heightCentre + lineWidth * 2;
//繪製圓角矩形,rx:x方向上的圓角半徑。ry:y方向上的圓角半徑。
canvas.drawRoundRect(rectF, 6, 6, paint);
}
複製代碼
1.咱們須要初始化一個RectF來繪製矩形,這個類經過一個邊的來繪製矩形。並初始化一個畫筆,和矩形的寬度。android
2.在onDraw方法中,設置畫筆paint,包括顏色,填充方式,是否抗拒性。還有更多的設置,讀者能夠自行查閱APIcanvas
3.獲取該View的實際寬高的一半,而後設置矩形的四邊,熟悉Android的view的繪製都知道,view的寬爲right - left,高度爲bottom - top。因此讓right比left多一個lineWidth便可讓矩形的寬爲lineWidth,bottom比top多4數組
4.在畫布上使用drawRoundRect方法繪製一個圓角的矩形性能優化
5.而後在xml文件中引用這個view就可使用了:架構
<com.ycm.customview.LineWaveVoiceView
android:id="@+id/line1"
android:layout_width="300dp"
android:layout_height="100dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
/>
複製代碼
這樣就能夠在view中繪製一個矩形,如圖所示:dom
如今咱們能夠繪製多個矩形在畫布上。直接採用for循環是不行的,這樣會讓矩形重疊在一塊兒,致使只顯示一個矩形,因此應該控制讓矩形錯開顯示,咱們可讓矩形之間間隔一個lineWidth。如圖所示:ide
咱們以第一個矩形的左邊爲參照,標爲0,則其右邊爲1,第二個矩形的左邊爲2,右邊爲3,以此類推,它們的距離都是lineWidth。因此咱們能夠得出:佈局
for (int i = 1; i <= 4; i++) { rectF.left = widthCentre - lineWidth / 2 + 2 * (i - 1) * lineWidth; rectF.right = widthCentre + lineWidth / 2 + (2 * i - 1) * lineWidth; rectF.top = heightCentre - lineWidth * 2; rectF.bottom = heightCentre + lineWidth * 2; //繪製圓角矩形,rx:x方向上的圓角半徑。ry:y方向上的圓角半徑。 canvas.drawRoundRect(rectF, 6, 6, paint); } 複製代碼
固然注意要在循環裏繪製圓角矩形,由於繪製多個矩形,固然要有一個繪製一個,否則放到循壞外只能繪製最後一個。效果如圖:post
咱們要繪製音頻抖動的效果,矩形的高度確定不能同樣,而是要根據聲音的大小來顯示,這裏咱們沒有聲音,簡單模擬一下給高度乘上for循環裏的i效果如圖:
至此咱們已經知道了如何繪製多個矩形,並控制不一樣的高度,那咱們要如何動態的控制高度呢?好比咱們點擊開始錄音的時候,就會動態的傳入聲音的大小,這個分貝值控制着矩形的抖動。要實現這個動態的效果,咱們須要不斷的設置分貝,並不斷的刷新。因此咱們能夠開啓一個線程,不斷設置音量的分貝,並不斷的刷新。爲了讓矩形抖動有錯落感,就須要讓每一個矩形抖動的值不同,因此咱們設置一個list存儲音量值,並依次改變裏面的值便可。
private static final int MIN_WAVE_HEIGHT = 2;//矩形線最小高
private static final int MAX_WAVE_HEIGHT = 12;//矩形線最大高
private static final int[] DEFAULT_WAVE_HEIGHT = {2, 2, 2, 2};
private static final int UPDATE_INTERVAL_TIME = 100;//100ms更新一次
private LinkedList<Integer> mWaveList = new LinkedList<>();
private float maxDb;
private void resetView(List<Integer> list, int[] array) {
list.clear();
for (int anArray : array) {
list.add(anArray);
}
}
private synchronized void refreshElement() {
Random random = new Random();
maxDb = random.nextInt(5) + 2;
int waveH = MIN_WAVE_HEIGHT + Math.round(maxDb * (MAX_WAVE_HEIGHT - MIN_WAVE_HEIGHT));
mWaveList.add(0, waveH);
mWaveList.removeLast();
}
public boolean isStart = false;
private class LineJitterTask implements Runnable {
@Override
public void run() {
while (isStart) {
refreshElement();
try {
Thread.sleep(updateSpeed);
} catch (Exception e) {
e.printStackTrace();
}
postInvalidate();
}
}
}
public synchronized void startRecord() {
isStart = true;
executorService.execute(task);
}
public synchronized void stopRecord() {
isStart = false;
mWaveList.clear();
resetView(mWaveList, DEFAULT_WAVE_HEIGHT);
postInvalidate();
}
複製代碼
1.爲了控制矩形抖動的範圍,咱們須要設置一個最大值和最小值。
2.並利用數組設置矩形的默認值,由於有四個矩形,因此數組大小爲4
3.定義一個分貝值,控制矩形的高度
4.重置View的時候把默認的數組傳進去,就能夠達到View的重置,好比View的初始化,和中止錄音的時候
5.刷新元素方法,用於不停的刷新矩陣的高度,讓矩陣抖起來。這裏用隨機數模擬聲音大小,傳給數組,每次都添加到第一個,而後每次都移除最後一個,這樣能讓矩陣按順序抖動。
6.在線程中調用這個刷新矩陣的方法,當開始錄音的時候,在while中刷新矩陣,並睡眠100ms,這樣就實現了沒100ms刷新一次view,開始錄音的時候設置isStart爲true。
7.在中止錄音的時候設置isStart爲false,並初始化矩形爲原始高度。因爲在線程中刷新View,應該使用postInvalidate()方法。
至此這個邏輯已經實現了,稍微潤色一下便可實現錄音時的音頻抖動
效果如圖:
/** * 語音錄製的動畫效果 */
public class LineWaveVoiceView extends View {
private static final String DEFAULT_TEXT = " 請錄音 ";
private static final int LINE_WIDTH = 9;//默認矩形波紋的寬度,9像素, 原則上從layout的attr得到
private Paint paint = new Paint();
private Runnable task;
private ExecutorService executorService = Executors.newCachedThreadPool();
private RectF rectRight = new RectF();//右邊波紋矩形的數據,10個矩形複用一個rectF
private RectF rectLeft = new RectF();//左邊波紋矩形的數據
private String text = DEFAULT_TEXT;
private int updateSpeed;
private int lineColor;
private int textColor;
private float lineWidth;
private float textSize;
public LineWaveVoiceView(Context context) {
super(context);
}
public LineWaveVoiceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LineWaveVoiceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(attrs, context);
resetView(mWaveList, DEFAULT_WAVE_HEIGHT);
task = new LineJitterTask();
}
private void initView(AttributeSet attrs, Context context) {
//獲取佈局屬性裏的值
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.LineWaveVoiceView);
lineColor = mTypedArray.getColor(R.styleable.LineWaveVoiceView_voiceLineColor, context.getColor(R.color.defaultLineColor));
lineWidth = mTypedArray.getDimension(R.styleable.LineWaveVoiceView_voiceLineWidth, LINE_WIDTH);
textSize = mTypedArray.getDimension(R.styleable.LineWaveVoiceView_voiceTextSize, 42);
textColor = mTypedArray.getColor(R.styleable.LineWaveVoiceView_voiceTextColor, context.getColor(R.color.defaultTextColor));
updateSpeed = mTypedArray.getColor(R.styleable.LineWaveVoiceView_updateSpeed, UPDATE_INTERVAL_TIME);
mTypedArray.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//獲取實際寬高的一半
int widthCentre = getWidth() / 2;
int heightCentre = getHeight() / 2;
paint.setStrokeWidth(0);
paint.setColor(textColor);
paint.setTextSize(textSize);
float textWidth = paint.measureText(text);
canvas.drawText(text, widthCentre - textWidth / 2, heightCentre - (paint.ascent() + paint.descent()) / 2, paint);
//設置顏色
paint.setColor(lineColor);
//填充內部
paint.setStyle(Paint.Style.FILL);
//設置抗鋸齒
paint.setAntiAlias(true);
for (int i = 0; i < 10; i++) {
rectRight.left = widthCentre + textWidth / 2 + (1 + 2 * i) * lineWidth;
rectRight.top = heightCentre - lineWidth * mWaveList.get(i) / 2;
rectRight.right = widthCentre + textWidth / 2 + (2 + 2 * i) * lineWidth;
rectRight.bottom = heightCentre + lineWidth * mWaveList.get(i) / 2;
//左邊矩形
rectLeft.left = widthCentre - textWidth / 2 - (2 + 2 * i) * lineWidth;
rectLeft.top = heightCentre - mWaveList.get(i) * lineWidth / 2;
rectLeft.right = widthCentre - textWidth / 2 - (1 + 2 * i) * lineWidth;
rectLeft.bottom = heightCentre + mWaveList.get(i) * lineWidth / 2;
canvas.drawRoundRect(rectRight, 6, 6, paint);
canvas.drawRoundRect(rectLeft, 6, 6, paint);
}
}
private static final int MIN_WAVE_HEIGHT = 2;//矩形線最小高
private static final int MAX_WAVE_HEIGHT = 12;//矩形線最大高
private static final int[] DEFAULT_WAVE_HEIGHT = {2,2, 2, 2,2, 2, 2, 2,2,2};
private static final int UPDATE_INTERVAL_TIME = 100;//100ms更新一次
private LinkedList<Integer> mWaveList = new LinkedList<>();
private float maxDb;
private void resetView(List<Integer> list, int[] array) {
list.clear();
for (int anArray : array) {
list.add(anArray);
}
}
private synchronized void refreshElement() {
Random random = new Random();
maxDb = random.nextInt(5) + 2;
int waveH = MIN_WAVE_HEIGHT + Math.round(maxDb * (MAX_WAVE_HEIGHT - MIN_WAVE_HEIGHT));
mWaveList.add(0, waveH);
mWaveList.removeLast();
}
public boolean isStart = false;
private class LineJitterTask implements Runnable {
@Override
public void run() {
while (isStart) {
refreshElement();
try {
Thread.sleep(updateSpeed);
} catch (Exception e) {
e.printStackTrace();
}
postInvalidate();
}
}
}
public synchronized void startRecord() {
isStart = true;
executorService.execute(task);
}
public synchronized void stopRecord() {
isStart = false;
mWaveList.clear();
resetView(mWaveList, DEFAULT_WAVE_HEIGHT);
postInvalidate();
}
public synchronized void setText(String text) {
this.text = text;
postInvalidate();
}
public void setUpdateSpeed(int updateSpeed) {
this.updateSpeed = updateSpeed;
}
複製代碼
若是喜歡個人文章,點個贊
若是喜歡個人文章,想與一羣資深開發者一塊兒交流學習的話,歡迎加入個人合做羣Android Senior Engineer技術交流羣。有flutter—性能優化—移動架構—資深UI工程師 —NDK相關專業人員和視頻教學資料
羣號:925019412