Andrioid 通过话筒实现吹气功能

xiaoxiao2021-02-28  19

今晚完成了模仿魔镜demo的最后一个模块就是使用话筒实例来吹气,让画面形成雾,再用手势擦除,相当于总结一下话筒类的使用。 在工作之前先声明权限

要先构造一个AudioRecordManger类 首先声明变量以及构造方法

private static final String TAG = "AudioRecord";//标记 public static final int SAMPLE_RATE_IN_HZ = 8000;//通道配置 public static final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ, AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT);//用于写入声音的缓存 private AudioRecord mAudioRecord;//话筒类 public boolean isGetVoiceRun;//是否录音运行 private Handler mHandler;//句柄 private int mWhat;//动作 public Object mLock;//对象

记下来是构造方法,通过传入handler和what来赋值,mLock是一个同步锁,做延时用

public AudioRecordManger(Handler handler,int what) { mLock = new Object();//同步锁 this.mHandler = handler;//获得句柄 this.mWhat = what;//动作ID }

实现监听吹气功能,用线程的方式开启话筒录音后,一秒钟10次接收话筒音量,然后将接收的音量转化为float的分贝值,并作为message发送。

public void getNoiseLevel() { if (isGetVoiceRun) { Log.e(TAG, "还在录着呢"); return; } //创建录音对象,并初始化对象属性 mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE_IN_HZ, AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE); //判断话筒对象是否为空 if (mAudioRecord == null) { Log.e("sound", "mAudioRecord初始化失败"); } isGetVoiceRun = true;//开启录音 //使用新线程 new Thread(new Runnable() { @Override public void run() { mAudioRecord.startRecording();//录音启动 short[] buffer = new short[BUFFER_SIZE];//设置缓存数组 while (isGetVoiceRun) { int r = mAudioRecord.read(buffer, 0, BUFFER_SIZE);//r是实际读取的数据长度,一般而言r会小于buffersize long v = 0; // 将 buffer 内容取出,进行平方和运算 for (int i = 0; i < buffer.length; i++) { v += buffer[i] * buffer[i]; } double mean = v / (double) r; // 平方和除以数据总长度,得到音量大小。 double volume = 10 * Math.log10(mean); // 大概一秒十次,锁 synchronized (mLock) { try { mLock.wait(500); } catch (InterruptedException e) { e.printStackTrace(); } } //声明消息类,句柄发送消息到主窗体函数 Message message = Message.obtain(); message.what = mWhat; message.obj = volume; mHandler.sendMessage(message); } //话筒对象释放 if (null !=mAudioRecord) { mAudioRecord.stop(); mAudioRecord.release(); mAudioRecord = null; } } }).start();//启动线程 }

mainactivity中接收message,先声明刚刚创建好的类

private AudioRecordManger audioRecordManger; //调用话筒实现类 private static final int RECORD = 2; //监听话筒

再在onCreate方法中调用实例的方法

audioRecordManger = new AudioRecordManger(handler,RECORD); //实例化话筒实现类 audioRecordManger.getNoiseLevel(); //打开话筒监听声音

我们在xml文件中放置drawview控件用来作为生成雾的画板,并在这上面去擦除雾,这是drawview的类

public class DrawView extends View { private Canvas mCanvas;// 画布 private Path mPath;// 路径 private Paint mPaint;// 画笔 private float moveX, moveY;//移动坐标 private Bitmap mBitmap;//图片变量 private Bitmap bitmap;//图片变量 private volatile boolean mComplete = false;// 判断遮盖层区域是否消除达到阈值 /** * 构造函数 */ public DrawView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init();//初始化 } public DrawView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DrawView(Context context) { this(context, null); } private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.glasses).copy(Bitmap.Config.ARGB_8888,true);//初始化图片加载 mPaint = new Paint(); //新建画笔 mPaint.setColor(Color.RED); //设置画笔颜色 mPaint.setStyle(Paint.Style.STROKE);//设置画笔样式 mPaint.setStrokeJoin(Paint.Join.ROUND);//设置结合处样子 mPaint.setStrokeCap(Paint.Cap.ROUND);//设置画笔笔触风格 mPaint.setDither(true);// 设置递色 mPaint.setAntiAlias(true);//设置抗锯齿 mPaint.setStrokeWidth(100);//设置空心线宽 mPath = new Path();//创建新路径 } //画布绘制 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.TRANSPARENT); if (!mComplete) //如果还未擦除完成 { mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); //设定目标图模式 canvas.drawBitmap(mBitmap, 0, 0, null);//画图 mCanvas.drawPath(mPath, mPaint); //画布 路径 canvas.drawBitmap(mBitmap, 0, 0, null); //画布图片 } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = MeasureSpec.getSize(widthMeasureSpec);//宽度 int height = MeasureSpec.getSize(heightMeasureSpec);//高度 mBitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);//图片 bitmap = Bitmap.createScaledBitmap(bitmap,width,height,true);//基于原bitmap,创建一个新的宽、高的bitmap mCanvas = new Canvas(mBitmap); //画布实例化 mCanvas.drawColor(Color.TRANSPARENT);//设置颜色 mCanvas.drawBitmap(bitmap,0,0,null);//画布重绘bitmap } }

接下来是mainactivity开始通过有aduiorecordmanger发送过来的message做处理,如果音量分贝大于50,则产生动画效果形成雾

private void getSoundValues(double values){ //话筒分贝大于50,屏幕起雾 if (values >50){ hideView();//隐藏无关控件 drawView.setVisibility(View.VISIBLE); //显示控件 Animation animation = AnimationUtils.loadAnimation(this, R.anim.in_window);//设置透明度 drawView.setAnimation(animation); audioRecordManger.isGetVoiceRun = false;//设置话筒停止运行 Log.e("玻璃显示","执行"); } } private Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case RECORD://监测话筒 double soundValues = (double) msg.obj; getSoundValues(soundValues);//获得话筒声音后,屏幕重绘起雾 break; default: break; } return false; } });

这里的anim中的in_window是在res下建立的动画xml文件,用Alpha的值来表示灰的程度,0是完全没有,1是全灰

<set xmlns:android="http://schemas.android.com/apk/res/android"> <alpha android:fromAlpha="0" android:toAlpha="1" android:duration="3000"/> </set>

这个时候当向手机话筒吹气的时候,就会产生雾气。

这时候加入手势擦除功能。 在drawView中加入写一个内部接口让mainactivity来实现

public interface OnCaYiCaCompleteListener{ void complete(); } private OnCaYiCaCompleteListener mListener; public void setOnCaYiCaCompleteListener(OnCaYiCaCompleteListener mListener){ this.mListener = mListener; } //变量重置 public void setEndValues(){ moveX = 0; moveY = 0; mPath.reset(); //路径重置 mComplete =false; //恢复未擦除状态 } private Runnable mRunnable = new Runnable() { @Override public void run() { int w = getWidth(); int h = getHeight(); float wipeArea = 0; float totalArea = w * h; Bitmap bitmap = mBitmap; int[] mPixels = new int[w * h]; // 获得Bitmap上所有的像素信息 bitmap.getPixels(mPixels, 0, w, 0, 0, w, h); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int index = i + j * w; if (mPixels[index] == 0) { wipeArea++; } } } if (wipeArea > 0 && totalArea > 0) { int percent = (int) (wipeArea * 100 / totalArea); Log.e("TAG", percent + ""); if (percent > 50) { // 清除掉图层区域 mComplete = true; postInvalidate(); } } } }; @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: moveX = x; moveY = y; mPath.moveTo(moveX,moveY); break; case MotionEvent.ACTION_MOVE: int dx = (int)Math.abs(moveX - x); int dy = (int)Math.abs(moveY - y); if(dx > 1 || dy > 1){ mPath.quadTo(x,y,(moveX + x) / 2,(moveY + y) / 2); } moveX = x; moveY = y; break; case MotionEvent.ACTION_UP: if(!mComplete){ //如果擦除未完成,则新线程开始 new Thread(mRunnable).start(); } break; default: break; } if(!mComplete){ invalidate(); } return true; }

再在ondraw中加入,当还未擦除干净的时候则进行释放操作

if(mComplete){ if (mListener != null){ mListener.complete(); //监听结束 setEndValues(); //变量重置 } }

最后在mainactivity中实现接口

@Override public void complete() { showView(); audioRecordManger.getNoiseLevel(); drawView.setVisibility(View.GONE); } drawView.setOnCaYiCaCompleteListener(this);

到这里完成吹气起雾和手势擦除功能

转载请注明原文地址: https://www.6miu.com/read-1700192.html

最新回复(0)