前言
在上一篇博文中,針對Android的藍牙基礎知識作了一個簡單的梳理。在正式的APP開發過程中,知識轉換成成用戶能夠看得見、摸得著的才能生產力最大化。本章主要針對藍牙設備的搜索界面作簡要的設計、開發。
一、藍牙搜索界面
通常在APP內的藍牙搜索中,選擇Dialog+listview的方式來顯示藍牙設備的搜索即簡潔又美觀。常用的Dialog、Listview無法滿足需求,因此需要客制化自行封裝,以后也可以將這些方法作為一個簡易的封裝類供其他的項目或者代碼參考。
藍牙搜索界面
1.1 MyDialog封裝
Dialog的封裝主要包含兩個方面,style和尺寸的封裝。
- style
style的封裝主要是對Dialog的顯示作簡要的修改,在res\values\styles.xml中添加以下代碼:
<style name="my_dialog" parent="android:Theme.Dialog">
<item name="android:windowFrame">@null</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowBackground">@color/white</item>
</style>
- list item
btDevice_list_item.xml主要用于Dialog中的Listview的item布局,每一個item共有三種屬性:name,address,bonded,即藍牙設備名,藍牙地址,是否綁定。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/bt_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="藍牙名稱"
android:layout_marginStart="43dp"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true" />
<TextView
android:id="@+id/bt_addr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="藍牙地址"
android:layout_alignParentTop="true"
android:layout_alignStart="@+id/bt_name"
android:layout_marginTop="20dp"
android:layout_marginBottom="5dp"/>
<TextView
android:id="@+id/bt_bonded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已綁定"
android:layout_marginEnd="23dp"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true" />
</RelativeLayout>
- MyDialog封裝
此自定義的Dialog可以作為后續的其他項目或者其他類型的Dialog類的封裝,根據不同的需求,設定其theme即可。
public class MyDialog extends Dialog{
// 這種適合通用
public MyDialog(Context context) {
super(context);
}
// 自定義提醒框樣式
public MyDialog(Context context, int themeResId) {
super(context, themeResId);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
- MyDialog的尺寸大小
自定義一個合適的尺寸,滿足需求即可,用戶或者愛好者可以根據自己的需求及喜好自定義類似的Dialog框圖大小及樣式。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@color/white"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textColor="@color/aliceblue"
android:text="藍牙設備"
android:textSize="20sp"
android:textStyle="bold"
android:background="@color/purple"/>
<ListView
android:id="@+id/bt_device_list"
android:divider="@color/blue"
android:dividerHeight="0.5dp"
android:padding="10dp"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/bt_dialog_cancel"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="取消"
android:textColor="@color/aliceblue"
android:background="@color/purple"
android:textStyle="bold"
android:textSize="20sp"
android:gravity="center"/>
</LinearLayout>
至此,顯示界面已經完成。
二、藍牙設備搜索
2.1 藍牙設備的適配器
因添加Listview類,對listview的添加、點擊、構建、移除等數據都需要綁定適配器,因此首要的任務根據業務需求自定義適配器。
public class BTDeviceAdapter extends BaseAdapter{
private static final String TAG = "first";
private Context context;
private LayoutInflater mInflater;
private List<BluetoothDevice> list;
private BluetoothDevice device;
private static class ViewHolder{
TextView btNameView;
TextView btAddrView;
TextView btBondedView;
}
//構造函數
public BTDeviceAdapter(final Context context, final List<BluetoothDevice> list){
super();
this.context = context;
this.list = list;
mInflater = LayoutInflater.from(context);
}
//device list大小
@Override
public int getCount() {
return list.size();
}
//device在list中的位置
@Override
public Object getItem(int position) {
return list.get(position);
}
//device在list中的id
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
//設備是否已經綁定
device = list.get(position);
//如果緩存convertView為空,則需要創建View
if (convertView == null) {
// view創建
convertView = mInflater.inflate(R.layout.btdevice_list_item, null);
holder = new ViewHolder();
holder.btNameView = (TextView) convertView.findViewById(R.id.bt_name);
holder.btAddrView = (TextView) convertView.findViewById(R.id.bt_addr);
holder.btBondedView = (TextView) convertView.findViewById(R.id.bt_bonded);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
//view屬性設置
holder.btNameView.setText(device.getName());
holder.btAddrView.setText(device.getAddress());
if (device.getBondState() == BluetoothDevice.BOND_BONDED){
Log.i(TAG, "設備:" + device.getName() + "已綁定");
holder.btBondedView.setVisibility(View.VISIBLE);
holder.btBondedView.setTextColor(Color.RED);
}else {
holder.btBondedView.setVisibility(View.INVISIBLE);
}
return convertView;
}
}
2.2 藍牙設備搜索
在所有的工作準備完成后,進入Activity或者fragment中進行藍牙button的點擊搜索工作。
手機藍牙因為會保存之前已經配對的藍牙設備,因此可以選擇對這些已配對的設備添加到list中,下方附上主要部分的代碼。
2.2.1 添加已配對的藍牙設備
//將已經存在的配對設備先加入列表
Set<BluetoothDevice> bondedDevices = adapter.getBondedDevices();
Log.e(TAG, "已綁定設備數為:" + bondedDevices.size());
if (bondedDevices.size() != 0) {
Iterator<BluetoothDevice> iterator = bondedDevices.iterator();
while (iterator.hasNext()) {
mlist.add((BluetoothDevice) iterator.next());
}
}
Log.e(TAG,"mlist:" + mlist);//搜索前先將手機以前配對的藍牙設備列表打印
2.2.2 藍牙設備搜索并添加
首先將dialog中的listview初始化,并關聯適配器。
deviceAdapter = new BTDeviceAdapter(mActivity, mlist);
bt_device_list = (ListView) dialog.findViewById(R.id.bt_device_list);
bt_device_list.setAdapter(deviceAdapter);
adapter.startDiscovery();
2.2.3 藍牙設備開啟廣播并添加藍牙設備到listview
此過程是一個動態添加的過程,在此代碼中包括搜索和完成的intent判斷。
/**
* 設置靜態廣播,主要用于接收藍牙搜索的結果
*/
@Override
public void onResume() {
super.onResume();
//廣播過濾
filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
filter.setPriority(Integer.MAX_VALUE);//設置廣播的優先級最大
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
try{
String action = intent.getAction();
Log.e(TAG,"接收到藍牙廣播!" + "action = " + action);
switch (action) {
case BluetoothDevice.ACTION_FOUND:
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getName() != null && device != null) {
Log.i(TAG, "device Name: " + device.getName());
Log.i(TAG, "device Addr: " + device.getAddress());
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "開始添加設備!");
addDevice(device);
}
});
}
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
System.out.println("ACTION_DISCOVERY_FINISHED");
Log.e(TAG, "掃描完成!");
if (mActivity instanceof Activity) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "finished 開始添加設備!");
Set<BluetoothDevice> paired = adapter.getBondedDevices();
Log.e(TAG, "paired = " + paired);
if (paired != null && paired.size() > 0) {
for (BluetoothDevice bonded : paired) {
if (mlist.indexOf(bonded) == -1) {
mlist.add(bonded);
deviceAdapter.notifyDataSetChanged();
}
}
}
Log.e(TAG, "掃描完成后的list: " + mlist);
}
});
}
Log.e(TAG, "掃描完成后的list: " + mlist);
break;
}
}catch (Exception e){
e.printStackTrace();
}
}
};
//注冊藍牙搜索結果的receiver
mActivity.registerReceiver(receiver, filter);
}
添加設備的關鍵代碼
private void addDevice(final BluetoothDevice device) {
boolean deviceFound = false;
for (final BluetoothDevice listDev : mlist) {
if (listDev.getAddress().equals(device.getAddress())) {
deviceFound = true;
break;
}
}
if (!deviceFound) {
if (mlist.indexOf(device) == -1) { //判斷設備是否存在list中,不存返回-1
mlist.add(device);
deviceAdapter.notifyDataSetChanged();
}
}
}
2.3 藍牙搜索結果
通過以上關鍵部分的代碼及搜索過程,可以完成對于藍牙設備的搜索及添加,當然可以根據個人的需求及愛好針對搜索過程中的一些客制化工作,如將搜索的結果放置在Activity中,dialog加入搜索狀態,如正在搜索,搜索完成等,具體添加的位置在btdevice_list_item.xml和BTDeviceAdapter.class中添加狀態的判斷。
下圖為藍牙設備的搜索結果
藍牙設備搜索結果