自定义View实现仿微信语音竖线动画
package com.sample.utils;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
public class AudioRecorder {
private int bufferSize;
private AudioRecord audioRecord;
private boolean isRecording;
private Object mLock;
public AudioRecorder() {
mLock = new Object();
int sampleRate = 44100; // 采样率
int channelConfig = AudioFormat.CHANNEL_IN_MONO; // 单声道
//channelConfig = AudioFormat.CHANNEL_IN_STEREO;//立体声
int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 格式
bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, bufferSize);
}
public void startRecording() {
isRecording = true;
new Thread(new Runnable() {
@Override
public void run() {
audioRecord.startRecording();
byte[] buffer = new byte[bufferSize];
while (isRecording) {
int read = audioRecord.read(buffer, 0, bufferSize);
long v = 0;
// 将 buffer 内容取出,进行平方和运算
for (int i = 0; i < buffer.length; i++) {
v += buffer[i] * buffer[i];
}
// 平方和除以数据总长度,得到音量大小。
double mean = v / (double) read;
double volume = 10 * Math.log10(mean);
System.out.println("分贝值:" + volume);
listener.getAudioVolume(volume);
listener.getAudioVolume(buffer);
// 大概一秒十次
synchronized (mLock) {
try {
mLock.wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
public void stopRecording() {
isRecording = false;
if (audioRecord!=null)
audioRecord.stop();
//audioRecord.release();
}
AudioListener listener;
public interface AudioListener{
void getAudioVolume(double volume);
void getAudioVolume(byte[] buffer);
}
public void setListener(AudioListener listener){
this.listener = listener;
}
}
package com.sample.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.sample.R;
import java.util.Random;
public class TestDraw extends View {
private Context context;
private Paint linePaint;
private int width,height;//屏幕的宽、高
Matrix matrix;
float downX,downY,lastX,lastY;
private boolean pointer;// 是否多个手指
private float[] matrixValue= new float[9];// matrix对应的值
public TestDraw(Context context) {
super(context);
this.context = context;
initPaint();
}
public TestDraw(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
initPaint();
}
public TestDraw(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initPaint();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public TestDraw(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.context = context;
initPaint();
}
private void initPaint() {
linePaint = new Paint();
//画笔抗锯齿
linePaint.setAntiAlias(true);
initScaleGestureDetector();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//视图的宽高
width = w;
height = h;
System.out.println("宽:"+w+"高:"+h);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.concat(matrix);
canvas.drawColor(getResources().getColor(R.color.hint_color));
// 去除锯齿毛边
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG| Paint.FILTER_BITMAP_FLAG));
testDraw2(canvas);
ViewConfiguration.get(context).getScaledTouchSlop();
}
float minHeight = 0;
float maxHeight = 0;
float startY = 40;
float stopY = 0;
Random random = new Random();
private void testDraw2(Canvas canvas){
stopY = height-40;
maxHeight = stopY-startY;
minHeight = maxHeight/6;
float startX = (width-10*40)/2;
for (int a=0;a<40;a++){
if (volume<=voiceMin){
num = 0;
//最小
drawLine(canvas,startX+a*10,(height-minHeight)/2,startX+a*10,(height-minHeight)/2+minHeight);
}else if (voiceMin<volume&&volume<voiceMax){
if (a<a1||a>a2){
//最小
drawLine(canvas,startX+a*10,(height-minHeight)/2,startX+a*10,(height-minHeight)/2+minHeight);
}else{
float x = startX+a*10;
int i = random.nextInt(a2-a1)+a1;
float h = 5*i/2;
if (h>maxHeight){
if (volume>voice)
h = maxHeight;
else
h = maxHeight-10;
}
float y1 = (height-h)/2;
float y2 = (height-h)/2+h;
drawLine(canvas,x,y1,x,y2);
}
}else{
//最大
drawLine(canvas,startX+a*10,(height-maxHeight)/2,startX+a*10,(height-maxHeight)/2+maxHeight);
}
}
}
private void drawLine(Canvas canvas,
float startX,float startY,float stopX,float stopY){
linePaint.setStyle(Paint.Style.STROKE);//不加这个不显示
linePaint.setColor(getResources().getColor(R.color.mywhite));
linePaint.setStrokeWidth(6);
Path mPath = new Path();
float sX = startX;
float sY = startY;
float sX2 = stopX;
float sY2 = stopY;
mPath.moveTo(sX, sY);
mPath.lineTo(sX2, sY2);
mPath.close();
canvas.drawPath(mPath, linePaint);
}
private void initScaleGestureDetector() {
matrix = new Matrix();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//拖动监听
int x = (int) event.getX();
int y = (int) event.getY();
if (event.getPointerCount() > 1) {
pointer = true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN://第一个手指初次碰到屏幕触发
downX = x;
downY = y;
System.out.println("ACTION_DOWN...");
pointer = false;
listener.audioStart();
break;
case MotionEvent.ACTION_UP://最后一个手指离开屏幕触发
float upX = (float) Math.abs(x - downX);
float upY = (float) Math.abs(y - downY);
if (y<downY&&upY > 100 && !pointer) {
System.out.println("ACTION_UP...松手取消");
listener.audioStop();
}
break;
case MotionEvent.ACTION_MOVE://手指在屏幕上滑动
float dx = (float) Math.abs(x - downX);
float dy = (float) Math.abs(y - downY);
if (y<downY&&dy > 100 && !pointer) {
System.out.println("ACTION_MOVE...");
}
break;
case MotionEvent.ACTION_POINTER_DOWN://有非主要的手指按下(按下之前已经有手指在屏幕上)
System.out.println("ACTION_POINTER_DOWN...");
break;
case MotionEvent.ACTION_POINTER_UP://有非主要手指抬起(抬起之后还有手指在屏幕上)
System.out.println("ACTION_POINTER_UP...");
break;
}
lastX = x;
lastY = y;
return true;
}
double volume;
int num = 0;//次数
int a1 = 10,a2 = 30;
//30、40是根据手机自带录音获取到的分贝范围
double voiceMin = 30,voice = 34.5,voiceMax = 40;
public void getVolume(double volume){
this.volume = volume;
if (voiceMin<volume&&volume<voiceMax)
num++;
int i = num>0?random.nextInt(num):0;
if (20<i&&i<50){
a1 = 5;a2 = 35;
}else if (i>50){
num = 0;
a1 = 3;a2 = 37;
}else{
a1 = 10;a2 = 30;
}
invalidate();
}
AudioListener listener;
public interface AudioListener{
void audioStart();
void audioStop();
}
public void setListener(AudioListener listener){
this.listener = listener;
}
}
本文介绍了如何在Android应用中使用AudioRecord录制音频并计算音量,然后在自定义View中通过竖线动画模拟微信语音消息的音量变化。开发者创建了AudioRecorder类来处理音频记录和AudioListener接口,以及TestDraw类负责绘制动画。
2066

被折叠的 条评论
为什么被折叠?



