首页 > 技术文章 > Android 基于蓝牙的方向控制器

cv-pr 2015-08-06 21:11 原文

最近开发了一个蓝牙控制器App,用手机远程控制小球的运动。

包含了一些基础知识:多线程使用,页面UI数据更新,按钮事件,选择项功能,蓝牙功能(蓝牙打开,蓝牙搜索,蓝牙连接,蓝牙命令发送,蓝牙命令接收)。

开发环境为:Android Studio+Java,蓝牙模块HC-06(自发自收模式)。

 

实现代码

主Activity代码:

  1 package com.example.cxk.lightballcontroller;
  2 
  3 import android.app.Activity;
  4 import android.bluetooth.BluetoothAdapter;
  5 import android.bluetooth.BluetoothDevice;
  6 import android.bluetooth.BluetoothSocket;
  7 import android.content.BroadcastReceiver;
  8 import android.content.Context;
  9 import android.content.Intent;
 10 import android.content.IntentFilter;
 11 import android.os.Bundle;
 12 import android.os.Handler;
 13 import android.os.Message;
 14 import android.view.Menu;
 15 import android.view.MenuItem;
 16 import android.view.View;
 17 import android.widget.AdapterView;
 18 import android.widget.ArrayAdapter;
 19 import android.widget.Button;
 20 import android.widget.CompoundButton;
 21 import android.widget.EditText;
 22 import android.widget.Spinner;
 23 import android.widget.Switch;
 24 import android.widget.TextView;
 25 import android.widget.Toast;
 26 import java.io.IOException;
 27 import java.io.InputStream;
 28 import java.io.OutputStream;
 29 import java.lang.ref.WeakReference;
 30 import java.util.ArrayList;
 31 import java.util.List;
 32 import java.util.Set;
 33 import java.util.UUID;
 34 
 35 public class MainActivity extends Activity {
 36 
 37     private Switch bluetoothSwitch;
 38     private Button bluetoothSearch;
 39     private Spinner bluetoothList;
 40     private Button bluetoothConnect;
 41     private TextView recvData;
 42     private EditText sendData;
 43     private Button bluetoothSend;
 44 
 45     private Button upSend;
 46     private Button leftSend;
 47     private Button downSend;
 48     private Button rightSend;
 49 
 50     private BluetoothAdapter bluetoothAdapter;
 51     private List<String> list = new ArrayList<String>();
 52     private ArrayAdapter<String> adapter;
 53     private String strMacAddress;
 54     private boolean booleanConnect = false;
 55     private ConnectedThread connectedThread;
 56     private MyHandler myHandler = new MyHandler(this);
 57 
 58     @Override
 59     protected void onCreate(Bundle savedInstanceState) {
 60         super.onCreate(savedInstanceState);
 61         setContentView(R.layout.activity_main);
 62 
 63         bluetoothSwitch = (Switch) findViewById(R.id.swtch);
 64         bluetoothSearch = (Button) findViewById(R.id.search);
 65         bluetoothList = (Spinner) findViewById(R.id.list);
 66         bluetoothConnect = (Button) findViewById(R.id.connect);
 67         recvData = (TextView) findViewById(R.id.recvdata);
 68         sendData = (EditText) findViewById(R.id.senddata);
 69         bluetoothSend = (Button) findViewById(R.id.send);
 70 
 71         upSend = (Button)findViewById(R.id.BtnUp);
 72         leftSend = (Button)findViewById(R.id.BtnLeft);
 73         downSend = (Button)findViewById(R.id.BtnDown);
 74         rightSend = (Button)findViewById(R.id.BtnRight);
 75 
 76         bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 77         if(bluetoothAdapter == null){
 78             //表明此手机不支持蓝牙
 79             Toast.makeText(MainActivity.this, "未发现蓝牙设备", Toast.LENGTH_SHORT).show();
 80             return;
 81         }
 82 
 83         if (bluetoothAdapter.isEnabled()) {
 84             bluetoothSwitch.setChecked(true);
 85         }
 86 
 87         adapter = new ArrayAdapter<String>(this,android.R.layout.simple_spinner_item, list);
 88         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 89         bluetoothList.setAdapter(adapter);
 90 
 91         bluetoothList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
 92             @Override
 93             public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
 94                 strMacAddress = adapter.getItem(i);
 95                 adapterView.setVisibility(View.VISIBLE);
 96             }
 97 
 98             @Override
 99             public void onNothingSelected(AdapterView<?> adapterView) {
100 
101             }
102         });
103 
104         bluetoothSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
105             @Override
106             public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
107                 if (b) {
108                     if (!bluetoothAdapter.isEnabled()) { //蓝牙未开启,则开启蓝牙
109                         Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
110                         startActivity(enableIntent);
111                     } else {
112                         Toast.makeText(MainActivity.this, "蓝牙已开启", Toast.LENGTH_SHORT).show();
113                     }
114                 } else {
115                     bluetoothAdapter.disable();
116                     Toast.makeText(MainActivity.this, "蓝牙已关闭", Toast.LENGTH_SHORT).show();
117                 }
118             }
119         });
120 
121 
122         bluetoothSearch.setOnClickListener(new View.OnClickListener() {
123             @Override
124             public void onClick(View view) {
125                 if (bluetoothAdapter == null) {
126                     Toast.makeText(MainActivity.this, "未发现蓝牙设备", Toast.LENGTH_SHORT).show();
127                     return;
128                 } else if (!bluetoothAdapter.isEnabled()) {
129                     Toast.makeText(MainActivity.this, "蓝牙设备未开启", Toast.LENGTH_SHORT).show();
130                 }
131 
132                 Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
133                 if (pairedDevices.size() > 0) {
134                     for (BluetoothDevice device : pairedDevices) {
135                         adapter.remove(device.getAddress());
136                         adapter.add(device.getAddress());
137                     }
138                 } else {
139                     //注册,当一个设备被发现时调用mReceive
140                     IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
141                     registerReceiver(mReceiver, filter);
142                 }
143             }
144         });
145 
146 
147         bluetoothConnect.setOnClickListener(new View.OnClickListener() {
148             @Override
149             public void onClick(View view) {
150                 if (strMacAddress == null) {
151                     Toast.makeText(MainActivity.this, "请先搜索设备", Toast.LENGTH_SHORT).show();
152                 } else {
153                     BluetoothDevice device = bluetoothAdapter.getRemoteDevice(strMacAddress);
154                     ConnectThread connectThread = new ConnectThread(device);
155                     connectThread.start();
156                 }
157             }
158         });
159 
160 
161         bluetoothSend.setOnClickListener(new View.OnClickListener() {
162             @Override
163             public void onClick(View view) {
164                 if (booleanConnect == false) {
165                     Toast.makeText(MainActivity.this, "请先连接设备", Toast.LENGTH_SHORT).show();
166                 } else {
167                     String strSendData = new String(sendData.getText().toString());
168                     connectedThread.write(strSendData.getBytes());
169                     sendData.setText("");
170                 }
171             }
172         });
173 
174         upSend.setOnClickListener(new View.OnClickListener() {
175             @Override
176             public void onClick(View view) {
177                 if (booleanConnect == false) {
178                     Toast.makeText(MainActivity.this, "请先连接设备", Toast.LENGTH_SHORT).show();
179                 } else {
180                     String str = "ONA\n";
181                     String strSendData = new String(str);
182                     connectedThread.write(strSendData.getBytes());
183                     sendData.setText("");
184                 }
185             }
186         });
187 
188         downSend.setOnClickListener(new View.OnClickListener() {
189             @Override
190             public void onClick(View view) {
191                 if (booleanConnect == false) {
192                     Toast.makeText(MainActivity.this, "请先连接设备", Toast.LENGTH_SHORT).show();
193                 } else {
194                     String str = "ONB\n";
195                     String strSendData = new String(str);
196                     connectedThread.write(strSendData.getBytes());
197                     sendData.setText("");
198                 }
199             }
200         });
201 
202         leftSend.setOnClickListener(new View.OnClickListener() {
203             @Override
204             public void onClick(View view) {
205                 if (booleanConnect == false) {
206                     Toast.makeText(MainActivity.this, "请先连接设备", Toast.LENGTH_SHORT).show();
207                 } else {
208                     String str = "ONC\n";
209                     String strSendData = new String(str);
210                     connectedThread.write(strSendData.getBytes());
211                     sendData.setText("");
212                 }
213             }
214         });
215 
216         rightSend.setOnClickListener(new View.OnClickListener() {
217             @Override
218             public void onClick(View view) {
219                 if (booleanConnect == false) {
220                     Toast.makeText(MainActivity.this, "请先连接设备", Toast.LENGTH_SHORT).show();
221                 } else {
222                     String str = "OND\n";
223                     String strSendData = new String(str);
224                     connectedThread.write(strSendData.getBytes());
225                     sendData.setText("");
226                 }
227             }
228         });
229     }
230 /////////////////////////////////////////////////////////////////////////////////////////////////////////////
231 
232     private static class MyHandler extends Handler {
233         WeakReference<MainActivity> mActivity;
234 
235         MyHandler(MainActivity activity) {
236             mActivity = new WeakReference<MainActivity>(activity);
237         }
238 
239         @Override
240         public void handleMessage(Message msg) {
241             MainActivity theActivity = mActivity.get();
242 
243             theActivity.recvData.append(msg.obj.toString());
244         }
245     }
246 
247     @Override
248     protected void onDestroy() {
249         if (connectedThread != null) {
250             connectedThread.cancel();
251         }
252         super.onDestroy();
253     }
254 
255 
256     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
257         @Override
258         public void onReceive(Context context, Intent intent) {
259             String action = intent.getAction();
260             if(BluetoothDevice.ACTION_FOUND.equals(action)){
261                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
262                 // 已经配对的则跳过
263                 if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
264                     adapter.add(device.getAddress());
265                 }
266             }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {  //搜索结束
267                 if (adapter.getCount() == 0) {
268                     Toast.makeText(MainActivity.this, "没有搜索到设备", Toast.LENGTH_SHORT).show();
269                 }
270             }
271         }
272     };
273 
274 
275 
276     private class ConnectThread extends Thread{
277         private BluetoothSocket mmsocket;
278         private BluetoothDevice mmdevice;
279 
280         public ConnectThread(BluetoothDevice device){
281             mmdevice = device;
282             BluetoothSocket tmp = null;
283             try {
284                 tmp = mmdevice.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
285             } catch (IOException e) {
286 
287             }
288             mmsocket = tmp;
289         }
290 
291         public void run(){
292             bluetoothAdapter.cancelDiscovery();    //取消设备查找
293             try {
294                 mmsocket.connect();
295             } catch (IOException e) {
296                 try {
297                     mmsocket.close();
298                 } catch (IOException e1) {
299 
300                 }
301                 //连接失败
302                 return;
303             }
304 
305 
306             booleanConnect = true;
307             //新建一个数据交换线程
308             connectedThread = new ConnectedThread(mmsocket);
309             connectedThread.start();
310         }
311 
312         public void cancel() {
313             try {
314                 mmsocket.close();
315             } catch (IOException e) {
316 
317             }
318         }
319     }
320 
321 
322     private class ConnectedThread extends Thread{
323         private BluetoothSocket mmsocket;
324         private InputStream inStream;
325         private OutputStream outStream;
326 
327         public ConnectedThread(BluetoothSocket socket){
328 
329             mmsocket = socket;
330             try {
331                 //获得输入输出流
332                 inStream = mmsocket.getInputStream();
333                 outStream = mmsocket.getOutputStream();
334             } catch (IOException e) {
335 
336             }
337         }
338 
339         public void run(){
340             byte[] buff = new byte[1];
341             int len = 0;
342             //读数据需不断监听,写不需要
343             while(true){
344                 try {
345                     len = inStream.read(buff);
346                     //把读取到的数据发送给UI进行显示
347                     String strBuffer = new String(buff);
348 
349                     Message toMain = myHandler.obtainMessage();
350                     toMain.obj = strBuffer;
351                     myHandler.sendMessage(toMain);
352                 } catch (IOException e) {
353 
354                     break;
355                 }
356             }
357         }
358 
359         public void write(byte[] buffer) {
360             try {
361                 outStream.write(buffer);
362             } catch (IOException e) {
363 
364             }
365         }
366 
367         public void cancel() {
368             try {
369                 mmsocket.close();
370             } catch (IOException e) {
371 
372             }
373         }
374     }
375 
376 
377     @Override
378     public boolean onCreateOptionsMenu(Menu menu) {
379         // Inflate the menu; this adds items to the action bar if it is present.
380         getMenuInflater().inflate(R.menu.menu_main, menu);
381         return true;
382     }
383 
384     @Override
385     public boolean onOptionsItemSelected(MenuItem item) {
386         // Handle action bar item clicks here. The action bar will
387         // automatically handle clicks on the Home/Up button, so long
388         // as you specify a parent activity in AndroidManifest.xml.
389         int id = item.getItemId();
390 
391         //noinspection SimplifiableIfStatement
392         if (id == R.id.action_settings) {
393             super.finish();
394             return true;
395         }
396 
397         return super.onOptionsItemSelected(item);
398     }
399 }

 

界面代码:

  1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2     xmlns:tools="http://schemas.android.com/tools"
  3     android:layout_width="match_parent"
  4     android:layout_height="match_parent"
  5     android:orientation="vertical"
  6     android:paddingLeft="@dimen/activity_horizontal_margin"
  7     android:paddingRight="@dimen/activity_horizontal_margin"
  8     android:paddingTop="@dimen/activity_vertical_margin"
  9     android:paddingBottom="@dimen/activity_vertical_margin"
 10     tools:context=".MainActivity"
 11     android:weightSum="1">
 12 
 13     <Switch
 14         android:id="@+id/swtch"
 15         android:textOn="@string/open"
 16         android:textOff="@string/close"
 17         android:layout_width="wrap_content"
 18         android:layout_height="48dp"
 19         android:layout_gravity="center_horizontal" />
 20 
 21     <LinearLayout
 22         android:layout_width="fill_parent"
 23         android:layout_height="wrap_content"
 24         android:orientation="horizontal"
 25         android:weightSum="1">
 26 
 27         <Button
 28             android:id="@+id/search"
 29             android:text="@string/search"
 30             android:layout_width="wrap_content"
 31             android:layout_height="37dp"
 32             android:background="#ff9ed7ff" />
 33 
 34         <Spinner
 35             android:id="@+id/list"
 36             android:layout_width="162dp"
 37             android:layout_height="48dp"
 38             android:layout_weight="1.17" />
 39 
 40         <Button
 41             android:id="@+id/connect"
 42             android:text="@string/connect"
 43             android:layout_width="93dp"
 44             android:layout_height="38dp"
 45             android:background="#ff9ed7ff" />
 46     </LinearLayout>
 47 
 48     <LinearLayout
 49         android:layout_width="fill_parent"
 50         android:layout_height="wrap_content"
 51         android:orientation="horizontal">
 52 
 53     </LinearLayout>
 54 
 55     <RelativeLayout
 56         android:layout_width="350dp"
 57         android:layout_height="112dp"
 58         android:layout_gravity="center_horizontal"
 59         android:id="@+id/DirectionPanel">
 60 
 61         <Button
 62             android:layout_width="wrap_content"
 63             android:layout_height="wrap_content"
 64             android:text="@string/up"
 65             android:id="@+id/BtnUp"
 66             android:layout_above="@+id/BtnRight"
 67             android:layout_centerHorizontal="true"
 68             android:background="#ffd0e2ff" />
 69 
 70         <Button
 71             android:layout_width="wrap_content"
 72             android:layout_height="wrap_content"
 73             android:text="@string/left"
 74             android:id="@+id/BtnLeft"
 75             android:layout_alignParentBottom="true"
 76             android:layout_toLeftOf="@+id/BtnUp"
 77             android:background="#ffd0e2ff" />
 78 
 79         <Button
 80             android:layout_width="wrap_content"
 81             android:layout_height="wrap_content"
 82             android:text="@string/right"
 83             android:id="@+id/BtnRight"
 84             android:layout_alignTop="@+id/BtnLeft"
 85             android:layout_toRightOf="@+id/BtnUp"
 86             android:background="#ffd0e2ff" />
 87     </RelativeLayout>
 88 
 89     <Button
 90         android:layout_width="wrap_content"
 91         android:layout_height="wrap_content"
 92         android:text="@string/down"
 93         android:id="@+id/BtnDown"
 94         android:layout_alignParentBottom="true"
 95         android:layout_centerHorizontal="true"
 96         android:layout_gravity="center_horizontal"
 97         android:background="#ffd0e2ff" />
 98 
 99     <EditText
100         android:id="@+id/senddata"
101         android:layout_width="356dp"
102         android:layout_height="wrap_content"
103         android:layout_weight="0.18" />
104 
105     <Button
106         android:id="@+id/send"
107         android:text="@string/send"
108         android:layout_width="84dp"
109         android:layout_height="33dp"
110         android:background="#ff9ed7ff"
111         android:layout_gravity="right" />
112 
113     <TextView
114         android:text="@string/recv"
115         android:layout_width="wrap_content"
116         android:layout_height="wrap_content" />
117 
118     <ScrollView
119         android:layout_width="match_parent"
120         android:layout_height="66dp"
121         android:id="@+id/scrollView"
122         android:layout_weight="0.82"
123         android:background="#ffc8c8c8">
124 
125         <TextView
126             android:id="@+id/recvdata"
127             android:layout_width="fill_parent"
128             android:layout_height="wrap_content"
129             android:layout_weight="0.99"
130             android:background="#ffc8c8c8" />
131     </ScrollView>
132 
133 
134 </LinearLayout>

 

 实现的效果

 

实现工程代码蓝牙控制器

 

参考资料:

http://www.pudn.com/downloads691/sourcecode/comm/android/detail2784484.html

推荐阅读