這邊來記錄一下這個盲人指示紅綠燈 藍牙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。那就第二個音頻也進行播放。