Android 模拟吹气实现吹风车效果

把最近写的一个吹气demo分享一下,实现效果:吹气风车转动,持续吹气风车加速,停止吹气风车减速,补气继续加速。
demo地址:https://github.com/SiKang123/BlowWindMill

目录:

这里写图片描述

MySensor只提供start(),shutDown(),destory()(之前有其他传感器的使用,这里去掉了,只演示吹气,接口可以忽略)

下面看AduioSensor类,主要功能是打开麦克风录音,每当分贝达到吹气的标准就给UI线程一个通知

package com.blow.main.sensor;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;
import android.util.Log;

import com.blow.main.ActivityBlow;


/**
 * Created by SiKang on 2016/1/5.
 */
public class AudioSensor extends MySensor {
    private static final String TAG = "AudioSensor";
    private static AudioSensor mMicSensor = null;
    static final int SAMPLE_RATE_IN_HZ = 8000;
    static final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE_IN_HZ,
            AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT);
    AudioRecord mAudioRecord;
    boolean isGetVoiceRun;//是否正在录音
    private Handler mHandler;//UI线程Handler
    private AudioSensor(Handler handler) {
        super();
        this.mHandler = handler;
        isGetVoiceRun = false;
        //初始化麦克风
        mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
                SAMPLE_RATE_IN_HZ, AudioFormat.CHANNEL_IN_DEFAULT,
                AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE);
    }

    //获取实例
    public static AudioSensor getInstance(Handler handler) {
        if (mMicSensor == null) {
            synchronized (AudioSensor.class) {
                if (mMicSensor == null) {
                    mMicSensor = new AudioSensor(handler);
                }
            }
        }
        return mMicSensor;
    }

    //开始录音
    @Override
    public void start() {
        if (isGetVoiceRun) {
            Log.e(TAG, "正在录音");
            return;
        }
        if (mAudioRecord == null) {
            throw new NullPointerException("mAudioRecord初始化失败!");
        }
        isGetVoiceRun = true;
        new Thread(new Runnable() {
            int index = 0;
            @Override
            public void run() {
                mAudioRecord.startRecording();
                short[] buffer = new short[BUFFER_SIZE];
                double volumeSum = 0;
                while (isGetVoiceRun) {
                    //(计算分贝是在网上找的算法)
                    //r是实际读取的数据长度,一般而言r会小于buffersize
                    int r = mAudioRecord.read(buffer, 0, BUFFER_SIZE);
                    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);
//                    if (needVolume > 0) {
                    //采集平均值测试不太稳定,上下浮动较大(用iPhone测试麦克风得到的数据非常稳定)这里用定值75,试了华为、魅族、三星、HTC感觉比取均值要稳定
                        if (volume > 75) {
                        //通知UI线程
                            Log.d(TAG, volume + "");
                            mHandler.sendEmptyMessage(ActivityBlow.BLOW_START);
                        }
//                    }
//                  采集前10次最大最小分贝差 计算吹气标准
//                    if (!gotNeedVolume) {
//                        if (index++ == 0) {
//                            //初始化最大和最小的采集分别
//                            maxVolume = volume;
//                            minVolume = volume;
//                        } else if (volume > maxVolume) {
//                            maxVolume = volume;
//                        } else if (volume < minVolume) {
//                            minVolume = volume;
//                        }
//                        //计算出吹气标准分贝
//                        if (index >= 10) {
//                            double avg = (maxVolume + minVolume) / 2;
//                            needVolume = avg * 2;
//                            Log.d(TAG, needVolume + "");
//                            gotNeedVolume = true;
//                        }
//
//                    }

                    //采集前10次分贝平均值计算吹气标准
//                    if (!gotNeedVolume) {
//                        //采集10前10次录入
//                        if (index >= 10) {
//                            //将吹气判定标准设为平均值的1.5倍
//                            needVolume = (volumeSum / 10) * 1.5;
//                            Log.d(TAG, needVolume + "");
//                            gotNeedVolume = true;
//                        }else{
//                            volumeSum+=volume;
//                            index++;
//                        }
//                    }

                    synchronized (mLock) {
                        try {
                            mLock.wait(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                if (mAudioRecord != null) {
                    mAudioRecord.stop();

                }
            }
        }).start();
    }

    @Override
    public void shutDown() {
        isGetVoiceRun = false;
    }

    @Override
    public void destory() {
        if (mAudioRecord != null) {
            mAudioRecord.release();
            mAudioRecord = null;
        }
        mMicSensor = null;
    }
}

然后是ActivityBlow,主要思路是,当收到AudioSensor发来的通知,先判断风车是不是正在转动,不是则开启一个匀速转动风车的线程,同时开启一个加速线程和一个等待线程,等待线程会计时,计时到300ms便停止加速开始减速,减速中又开始吹气的话停止减速,开启加速,并重新计时,Handler每次收到AudioSensor的消息都会重置 记时变量。
所以持续吹气会一直加速,一旦停止吹气,就会开始减速

下面是代码:

package com.blow.main;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import com.blow.main.sensor.AudioSensor;
import com.blow.main.sensor.MySensor;


/**
 * Created by SiKang on 2016/1/14.
 */
public class ActivityBlow extends Activity implements View.OnTouchListener {
    private final String TAG = "ActivityBlowTest";
    private ImageView mWindmillImg;//风车需要转动的部位
    private Button mBlowBtn;//吹气按钮
    private MySensor blowSenser;
    private boolean isBlowing;//正在吹起标识
    private boolean isBegin;//是否按下按钮(按下才接受吹起)
    private float blowAngle;
    private int blowTime;
    public static final int BLOW_START = 0;
    public static final int BLOWING = 1;
    public static final int BLOW_END = 2;
    private boolean isReset;
    private boolean isPause;
    DisplayMetrics dm;

    @Override
    protected void onCreate(Bundle arg0) {
        super.onCreate(arg0);
        setContentView(R.layout.activity_blow);
        dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        initLayout();
        initData();
        blowSenser = AudioSensor.getInstance(handler);
    }

    public void initData() {
        isPause = false;//是否进入onPause
        isBlowing = false;//转动线程是否已开启
        isBegin = false;//是否按住吹起按钮
        blowAngle = 0;//风车当前角度
        isReset = false;//是否补气
    }

    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case BLOW_START:
                //收到吹气通知,先判断是否按住按钮
                    if (isBegin) {
                        //重置计时和补气标识
                        if (!isReset)
                            isReset = true;
                        if (blowTime > 0)
                            blowTime = 0;
                        if (!isBlowing) {
                            //如果风车没有转动,转动线程开启
                            isBlowing = true;
                            new Thread(new blowRun()).start();
                        }
                    }
                    break;
                case BLOWING:
                    //更新风车角度
                    mWindmillImg.setRotation(blowAngle);
                    break;
                case BLOW_END:
                    //转动停止
                    isBlowing = false;
                    break;
            }
            return false;
        }
    });

    protected void initLayout() {
        //获得控件
        mWindmillImg = (ImageView) this.findViewById(R.id.activity_blow_windmill);
        mBlowBtn = (Button) this.findViewById(R.id.activity_blow_btn);
        mBlowBtn.setOnTouchListener(this);
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        int btnWidth = dm.heightPixels / 6;
        layoutParams.width = btnWidth;//设置宽高
        layoutParams.height = btnWidth;
        layoutParams.setMargins((dm.widthPixels - btnWidth) / 2, dm.heightPixels / 6 * 4, 0, 0);
        mBlowBtn.setLayoutParams(layoutParams);
    }

    //开启麦克风
    @Override
    protected void onStart() {
        super.onStart();
        isPause = false;
        blowSenser.start();
    }
    //注销麦克风
    @Override
    protected void onPause() {
        super.onPause();
        blowSenser.shutDown();
        isPause = true;
    }

    //销毁
    @Override
    protected void onDestroy() {
        blowSenser.destory();
        super.onDestroy();

    }

    //风车每一帧转动的角度
    private float rotationAngle = 1f;

    //转动风车
    class blowRun implements Runnable {
        @Override
        public void run() {
        //开始转动的同时开启计时和加速线程
            new Thread(new stopRun()).start();
            new Thread(new rotationRun()).start();
            //开始转动,每次转动1度,5ms一次
            while (isBlowing && !isPause) {
                blowAngle += rotationAngle;
                if (blowAngle > 360) {
                    blowAngle = 1;
                }
                handler.sendEmptyMessage(BLOWING);
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //控制加速
    class rotationRun implements Runnable {
        @Override
        public void run() {
        //持续加速,直到isReset为false(计时线程达到300ms会将其设为false)
            while (isReset && !isPause) {
            //最快速度10
                if (rotationAngle <= 10) {
                    rotationAngle += 0.4;
                }

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //控制减速
    class stopRun implements Runnable {
        @Override
        public void run() {
        //每100ms计一次,blowTime在吹气中会被置0,达到3停止加速,并结束循环
            while (blowTime <= 3 && !isPause) {
                blowTime++;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //停止加速
            isReset = false;
            //开始减速(如果重新吹气,isReset会被赋值true,循环停止),否则一直将速度减至0
            while (rotationAngle > 0 && !isReset && !isPause) {
                rotationAngle -= 0.4;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //如果while停止是因为重新吹气,则开始加速,并重新开始计时
            if (isReset) {
                new Thread(new stopRun()).start();
                new Thread(new rotationRun()).start();
            } else {
            //如果停止是因为速度已为0,通知UI线程,转动结束
                handler.sendEmptyMessage(BLOW_END);
            }
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            isBegin = false;
        }
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            isBegin = true;
        }
        return false;
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值