这边来记录一下这个盲人指示红绿灯 蓝牙app的制作过程。
第一步:安装一个android studio 具体过程不写了。直接下载安装就行
第二步: 新建项目 file -> new -> newProject,选择Empty Activity
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EC6anZFL-1586166531716)(https://i.loli.net/2020/04/06/U9waJ2F3zHdXMGf.png)]
language选择java 最小api选择23 一般现在手机都比6.0高吧
第三步:添加两个textview 用来显示一定的信息。id 分别取为tip1,tip2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IWzjbjjk-1586166531718)(https://i.loli.net/2020/04/06/WjExD1Xd54LPCNc.png)]
第四步 初始化界面
在oncreate 函数中 添加.意思是代码获取到那两个text控件。方便后面动态的改变信息。
tip=findViewById(R.id.tip);
tip2=findViewById(R.id.tip2);
tip.setText("蓝牙已断开");
也需要在类中声明变量
TextView tip,tip2;
第五步 构建蓝牙连接层。
声明变量
BluetoothDevice device;
BluetoothSocket mSocket;
BluetoothAdapter mBluetoothAdapter;
构建线程函数,是一个跑在单独线程里的死循环。
安卓新版本都要动态申请权限。所以首先申请权限
下一步 循环检测设备有没有绑定。没绑定得先去绑定。
然后是 进行一个链接和通讯。每次循环前判断一次连接状态。如果正常就读取蓝牙数据。不正常就进行重连
public void bluetoothinit(){
Log.e("myTip123","开始线程");
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
// Device does not support Bluetooth
}
if (!mBluetoothAdapter.isEnabled()) {//申请权限 ask for permission
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
while (true){
Set<BluetoothDevice> tmp = mBluetoothAdapter.getBondedDevices();
for (BluetoothDevice d : tmp) {
Log.e("add123",d.getAddress());
if(d.getAddress().equals("00:19:10:08:A5:3C")){
device=d;
}
}
if(device==null){
Log.e("myTip123","设备未绑定。请绑定后再试。");
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this.getApplicationContext(), "设备未绑定。请绑定后再试。", Toast.LENGTH_LONG).show();
Log.e("myTip123","设备未绑定。请绑定后再试。");
}
});
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
// 所有已绑定设备,一个Set集合
try {
mSocket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
} catch (IOException e) {
e.printStackTrace();
}
mBluetoothAdapter.cancelDiscovery();
while(true){
Log.e("myTip123","检查");
if(mSocket.isConnected()){
try {
String hhh="";
while(mSocket.getInputStream().available()>0){
hhh+=(char)mSocket.getInputStream().read();
}
Log.e("myTip123",hhh);
int lastDirectionFlag=DirectionFlag;
DirectionFlag=(hhh.contains("NSpass")?1:2);
if(lastDirectionFlag!=DirectionFlag){//方向改变
Log.e("方向改变",""+DirectionFlag);
tip.setText("蓝牙已链接 "+(DirectionFlag==1?"南北通":"东西通"));
if(DirectionFlag==2){//东西
if(dir==3){//南
play(ns,gono);
}else if(dir==2){
play(ew,goyes);
}
}
else if(DirectionFlag==1){
if(dir==3){//南
play(ns,goyes);
}else if(dir==2){
play(ew,gono);
}
}
}
} catch (IOException e) {
Log.e("myTip123","检测available 出错");
e.printStackTrace();
}
try {
mSocket.getOutputStream().write(" ".getBytes());
} catch (IOException e) {
e.printStackTrace();
try {
mSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this.getApplicationContext(), "蓝牙已断开", Toast.LENGTH_LONG).show();
tip.setText("蓝牙已断开");
DirectionFlag=0;
play(bno,null);
}
});
}
}
if(!mSocket.isConnected()){
try {
mSocket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
mSocket.connect();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this.getApplicationContext(), "蓝牙已连接", Toast.LENGTH_LONG).show();
tip.setText("蓝牙已链接");
play(byes,null);
}
});
} catch (IOException e) {
try {
mSocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this.getApplicationContext(), "链接失败。未找到设备。5秒后重新链接", Toast.LENGTH_LONG).show();
}
});
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
第六步。在oncreate中加入蓝牙线程的启动
new Thread (new Runnable(){
@Override
public void run(){
bluetoothinit();
}
}).start();
第七步。读取数据
上面的蓝牙代码里已经有内容了。这边拿出来看一下
String hhh="";
while(mSocket.getInputStream().available()>0){
hhh+=(char)mSocket.getInputStream().read();
}
第八步。处理数据。
我们要做的就是判断红绿灯方向是否变化。并且做一个提醒。
变化这种就需要记录这一次和上一次的状态。
所以先创建flag变量
初始化
int DirectionFlag=0;int lastDirectionFlag=DirectionFlag;
然后在上面读取完数据后面对当前方向状态进行赋值,通过判断消息中有没有关键字
DirectionFlag=(hhh.contains("NSpass")?1:2);
如果发生了方向改变。我们进行提示。
显示设置文本 flag为1就是南北通。为2就是东西通
然后后面的是根据指南针数据进行对应的音频提示。这部分我们接下来讲
if(lastDirectionFlag!=DirectionFlag){//方向改变
Log.e("方向改变",""+DirectionFlag);
tip.setText("蓝牙已链接 "+(DirectionFlag==1?"南北通":"东西通"));
if(DirectionFlag==2){//东西
if(dir==3){//南
play(ns,gono);
}else if(dir==2){
play(ew,goyes);
}
}
else if(DirectionFlag==1){
if(dir==3){//南
play(ns,goyes);
}else if(dir==2){
play(ew,gono);
}
}
}
第八步。获取指南针数据
添加变量
private Sensor mAccelerometer;
private Sensor mMagneticField;
private float[] values = new float[3]; //orientation values
private float[] accelerometerValues = new float[3]; //data of acclerometer sensor
private float[] magneticFieldValues = new float[3] ; //data of magnetic field sensor
private float[] r = new float[9];
在oncreate中初始化
SensorManager mSensorManager;
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if(mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null){
//Success!存在磁力计
}else{
//Failure!不存在磁力计
}
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mMagneticField = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
mSensorManager.registerListener((SensorEventListener) this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
mSensorManager.registerListener((SensorEventListener) this, mMagneticField, SensorManager.SENSOR_DELAY_NORMAL);
给当前类添加接口SensorEventListener,即修改为
public class MainActivity extends AppCompatActivity implements SensorEventListener{
添加override函数
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
accelerometerValues = sensorEvent.values;
}
if (sensorEvent.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
magneticFieldValues = sensorEvent.values;
}
SensorManager.getRotationMatrix(r, null, accelerometerValues, magneticFieldValues);
SensorManager.getOrientation(r, values);
values[0] = (float) Math.toDegrees(values[0]);
dirnum=(int)values[0];
lastdir=dir;
if(dirnum>160||dirnum<-160||(dirnum<20&&dirnum>-20)){
dir=3;
}else if((dirnum<-70&&dirnum>-110)||(dirnum>70&&dirnum<110)){
dir=2;
}else{
dir=1;
}
if(dir!=lastdir){
MediaPlayer tar = null;
if (dir== 1){
play(dirno,null);
}else{
if(DirectionFlag==2){//东西
if(dir==3){//南
play(ns,gono);
}else if(dir==2){
play(ew,goyes);
}
}
else if(DirectionFlag==1){
if(dir==3){//南
play(ns,goyes);
}else if(dir==2){
play(ew,gono);
}
}
}
}
switch (dir){
case 1:
tip2.setText("方向偏离过大");
break;
case 2:
tip2.setText("东西 "+dirnum);
break;
case 3:
tip2.setText("南北 "+dirnum);
break;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
在以上函数中。对不同角度进行分类。即东西方向,南北方向,还有一个是偏离较大。就是介于东西和南北之间的。比较模糊的方向、
并且给全局的放前方向flag进行设置,同时更新text显示,并做语音提示。
第九步。语音提示
用到的是MediaPlayer。
实现准备的音频文件有。
蓝牙已连接。蓝牙已断开。可以前行。不可前行。方向偏离大。当前方向为南北。当前方向为东西。
MediaPlayer goyes;//
MediaPlayer gono;
private MediaPlayer dirno;
private MediaPlayer byes;
private MediaPlayer bno;
private MediaPlayer ns;
private MediaPlayer ew;
然后我们进入到oncreate中添加对以上几个mediaplayer的加载
goyes = MediaPlayer.create(MainActivity.this.getApplicationContext(), R.raw.goyes);
gono = MediaPlayer.create(MainActivity.this.getApplicationContext(), R.raw.gono);
bno = MediaPlayer.create(MainActivity.this.getApplicationContext(), R.raw.bno);
byes = MediaPlayer.create(MainActivity.this.getApplicationContext(), R.raw.byes);
dirno = MediaPlayer.create(MainActivity.this.getApplicationContext(), R.raw.dirno);
ns = MediaPlayer.create(MainActivity.this.getApplicationContext(), R.raw.ns);
ew = MediaPlayer.create(MainActivity.this.getApplicationContext(), R.raw.ew);
采用的方案是安卓里的raw 资源,需要在项目下的app/src/main/res下新建一个raw 文件夹。然后把音频文件拖放进去。
由于我们的音频可能是两个要连续播放所以我构造了一下函数
MediaPlayer first,second;
public void play(MediaPlayer tar1, final MediaPlayer tar2){
if (first!=null){
first.pause();
first.seekTo(0);
}
if(second!=null){
second.pause();
second.seekTo(0);
}
first=tar1;
second=tar2;
first.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
if(tar2!=null)
tar2.start();
}
});
first.start();
}
前两个判断是为了防止开始放当前音频的时候。之前的音频还没播放完。所以要先停止。
然后给第一个音频添加一个播放完毕的事件。如果有第二个音频不为null。那就第二个音频也进行播放。