刚学习了外设连接蓝牙,需要实现一个搜索蓝牙对话框,可以获取已连接的蓝牙和附近发现的蓝牙
效果如下
activity_custom_dialog.xml:主视图布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/search_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#2196F3"
android:text="@string/search_bluetooth"/>
<TextView
android:id="@+id/mac_address"
android:layout_width="match_parent"
android:layout_height="@dimen/item_layout_height"
android:background="#E6EFEE"
android:textColor="@color/black"/>
</LinearLayout>
对话框布局文件:bluetooth_dialog.xml
<?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="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/title_layout_height"
android:background="#3F51B5"
android:gravity="center"
android:padding="10dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/bluetooth"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_vertical"
android:src="@drawable/bluetooth_conn" />
<TextView
android:id="@+id/tv_bluetooth"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:gravity="center"
android:textColor="@color/white"
android:textSize="@dimen/title_font_size"
android:text="@string/bluetoothtitle" />
</LinearLayout>
<Button
android:id="@+id/close_dialog"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:background="@drawable/close" />
</RelativeLayout>
<ListView
android:id="@+id/device_list"
android:layout_width="match_parent"
android:layout_height="500dp"/>
</LinearLayout>
list item 布局文件 bluetooth_device_item.xml
<!-- res/layout/item_bluetooth_device.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/title_layout_height"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_gravity="center"
android:layout_height="24dp"
android:contentDescription="@null"
android:src="@drawable/baseline_bluetooth_audio_24" />
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="10dp">
<TextView
android:id="@+id/bluetooth_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#1E1D1D"
android:textSize="@dimen/common_font_size"
android:text="Bluetooth Device Name" />
<TextView
android:id="@+id/mac_address"
android:layout_width="wrap_content"
android:layout_marginTop="5dp"
android:textColor="#6C6B6B"
android:layout_height="wrap_content"
android:text="00:11:22:33:AA:BB" />
</LinearLayout>
<TextView
android:id="@+id/isConn_tv"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textColor="#aaaaaa"
android:textSize="15sp"/>
</LinearLayout>
主Activity代码
package com.example.drawable;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.drawable.utils.BluetoothAlertDialog;
public class BluetoothDialogActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_dialog);
Button button = findViewById(R.id.search_btn);
TextView macAddress = findViewById(R.id.mac_address);
button.setOnClickListener(v -> {
BluetoothAlertDialog customDialog = new BluetoothAlertDialog(BluetoothDialogActivity.this, new BluetoothAlertDialog.DataBackListener() {
@Override
public void getData(String data) {
macAddress.setText(data);
}
});
customDialog.show();
});
}
}
自定义对话框:BluetoothAlertDialog
package com.example.drawable.utils;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import com.example.drawable.R;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@SuppressLint("MissingPermission")
public class BluetoothAlertDialog extends Dialog implements AdapterView.OnItemClickListener {
private final Context context;
private List<BluetoothDevice> bluetoothDevices;
private BluetoothDeviceAdapter adapter;
//创建监听对象
private final DataBackListener listener;
//广播开启标志
private Boolean isReceiverRegistered = false;
//定义回调接口
public interface DataBackListener{
void getData(String data);
}
//定义广播接收器
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device != null && !bluetoothDevices.contains(device)) {
// 将设备添加到数据源中
bluetoothDevices.add(device);
//通知适配器更新列表
adapter.notifyDataSetChanged();
}
}
}
};
public BluetoothAlertDialog(Context context, final DataBackListener listener) {
super(context);
this.context = context;
this.listener = listener;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.bluetooth_dialog, null);
setContentView(view);
ListView listView = findViewById(R.id.device_list);
//获取默认蓝牙适配器
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(bluetoothAdapter==null){
Toast.makeText(context, R.string.unsupported_bluetooth, Toast.LENGTH_SHORT).show();
}else {
//判断适配器是否可用,如果可用
if (bluetoothAdapter.isEnabled()) {
searchBluetooth(bluetoothAdapter, listView);
}else {
Toast.makeText(context, R.string.request_open_bluetooth, Toast.LENGTH_SHORT).show();
}
}
//给list设置item点击监听事件
listView.setOnItemClickListener(this);
Button closeBtn = findViewById(R.id.close_dialog);
closeBtn.setOnClickListener(v -> dismiss());
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
BluetoothDevice selectedDevice = bluetoothDevices.get(position);
if(selectedDevice!=null){
String address = selectedDevice.getAddress();
listener.getData(address);
dismiss();
}
}
/**
* 搜索蓝牙
* @param bluetoothAdapter 蓝牙列表适配器
* @param listView 蓝牙列表
*/
private void searchBluetooth(BluetoothAdapter bluetoothAdapter, ListView listView) {
//获取已配对的蓝牙
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
bluetoothDevices = new ArrayList<>(pairedDevices);
//创建自定义视图适配器
adapter = new BluetoothDeviceAdapter(getContext(), bluetoothDevices);
//给list组件设置适配器
listView.setAdapter(adapter);
//创建蓝牙广播意图对象
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
//注册广播接收器,传一个广播对象,一个意图对象
context.registerReceiver(mReceiver, filter);
isReceiverRegistered = true;
//开启搜索广播
bluetoothAdapter.startDiscovery();
}
@Override
protected void onStop() {
super.onStop();
if (isReceiverRegistered) { // 添加一个标志位来检查是否已注册
try {
context.unregisterReceiver(mReceiver);
isReceiverRegistered = false;
} catch (IllegalArgumentException e) {
Log.e("BluetoothAlertDialog", "An error occurred", e);
}
}
}
}
列表适配器:BluetoothDeviceAdapter
package com.example.drawable.utils;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.example.drawable.R;
import java.util.List;
@SuppressLint("MissingPermission")
public class BluetoothDeviceAdapter extends ArrayAdapter<BluetoothDevice> {
private final Context context;
public BluetoothDeviceAdapter(Context context, List<BluetoothDevice> devices) {
super(context, 0, devices);
this.context = context;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.bluetooth_device_item, parent, false);
}
ImageView icon = convertView.findViewById(R.id.icon);
TextView bluetoothName = convertView.findViewById(R.id.bluetooth_name);
TextView macAddress = convertView.findViewById(R.id.mac_address);
TextView isConn = convertView.findViewById(R.id.isConn_tv);
BluetoothDevice device = getItem(position);
if (device != null) {
bluetoothName.setText(device.getName());
macAddress.setText(device.getAddress());
int bondState = device.getBondState();
if (BluetoothDevice.BOND_BONDED == bondState) {
isConn.setText("已配对");
}
BluetoothClass bluetoothClass = device.getBluetoothClass();
if (bluetoothClass != null) {
int deviceClass = bluetoothClass.getDeviceClass();
switch (deviceClass) {
case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
icon.setImageResource(R.drawable.wearable);
break;
case BluetoothClass.Device.COMPUTER_DESKTOP:
case BluetoothClass.Device.COMPUTER_LAPTOP:
icon.setImageResource(R.drawable.pc);
break;
case BluetoothClass.Device.PHONE_SMART:
case BluetoothClass.Device.PHONE_CELLULAR:
icon.setImageResource(R.drawable.phone);
break;
default:
icon.setImageResource(R.drawable.bluetooth);
break;
}
} else {
icon.setImageResource(R.drawable.bluetooth);
}
}
return convertView;
}
}
需要开启的权限,有时可能或需要手动申请权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
字符串可以自己添加,下面是图标资源
bluetooth.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
<path android:fillColor="#231815" android:pathData="M756.7,679.8l0.1,-0.1L561.2,512l195.6,-167.7 -0.1,-0.1c6.8,-5.9 11.3,-14.5 11.3,-24.2a31.8,31.8 0,0 0,-11.3 -24.2l0.1,-0.1 -224,-192 -0.1,0.1C527.1,99 520,96 512,96a32,32 0,0 0,-32 32v314.4l-171.2,-146.7 -0.1,0.1c-5.6,-4.8 -12.8,-7.8 -20.7,-7.8a32,32 0,0 0,-32 32c0,9.7 4.4,18.3 11.3,24.2l-0.1,0.1L462.8,512 267.2,679.7l0.1,0.1A31.8,31.8 0,0 0,256 704a32,32 0,0 0,32 32c8,0 15.1,-3 20.7,-7.8l0.1,0.1L480,581.6V896a32,32 0,0 0,32 32c8,0 15.1,-3 20.7,-7.8l0.1,0.1 224,-192 -0.1,-0.1A31.8,31.8 0,0 0,768 704a31.8,31.8 0,0 0,-11.3 -24.2zM686.8,704L544,826.4V581.6L686.8,704zM544,442.4V197.6L686.8,320 544,442.4z"/>
</vector>
bluetooth_conn.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
<path android:fillColor="#FFFFFF" android:pathData="M397.1,493.7l215.2,-229.7a35.4,35.4 0,0 0,-3 -51.3L365.8,8.5A35.4,35.4 0,0 0,307.4 35.5L307.4,405L115.9,214.8a35.4,35.4 0,0 0,-49.8 50.2l232.3,230.3L65.3,744.1a35.4,35.4 0,0 0,51.6 48.5l190.7,-203.3v399.3a35.4,35.4 0,0 0,61 24.3l243.5,-256.7a35.4,35.4 0,0 0,-0.8 -49.2l-214.4,-213.3zM378.5,111.5L535.2,243.1 378.5,410.5zM378.5,899.2L378.5,574.6l158.6,157.4zM718.3,307.3a35.4,35.4 0,1 0,-58.8 39.4,228.3 228.3,0 0,1 0,253 35.4,35.4 0,0 0,58.7 39.4,298.7 298.7,0 0,0 0,-331.8zM930.2,290.1a482.3,482.3 0,0 0,-45.5 -85.2,35.4 35.4,0 1,0 -58.8,39.3 421.9,421.9 0,0 1,0 468.3,35.4 35.4,0 0,0 58.8,39.4A492.3,492.3 0,0 0,930.2 290.1z"/>
</vector>
bluetooth_title_logo.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M14.24,12.01l2.32,2.32c0.28,-0.72 0.44,-1.51 0.44,-2.33 0,-0.82 -0.16,-1.59 -0.43,-2.31l-2.33,2.32zM19.53,6.71l-1.26,1.26c0.63,1.21 0.98,2.57 0.98,4.02s-0.36,2.82 -0.98,4.02l1.2,1.2c0.97,-1.54 1.54,-3.36 1.54,-5.31 -0.01,-1.89 -0.55,-3.67 -1.48,-5.19zM15.71,7.71L10,2L9,2v7.59L4.41,5 3,6.41 8.59,12 3,17.59 4.41,19 9,14.41L9,22h1l5.71,-5.71 -4.3,-4.29 4.3,-4.29zM11,5.83l1.88,1.88L11,9.59L11,5.83zM12.88,16.29L11,18.17v-3.76l1.88,1.88z"/>
</vector>
close.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
<path android:fillColor="#C9D1E1" android:pathData="M512,0c282.5,0 512,229.5 512,512s-229.5,512 -512,512S0,794.5 0,512 229.5,0 512,0zM667.1,304.7L512,459.9l-155.1,-155.1 -51.7,51.7 155.1,155.2 -155.1,155.1 51.7,51.7 155.1,-155.1 155.1,155.1 51.7,-51.7 -155.1,-155.1 155.1,-155.1 -51.7,-51.7z"/>
</vector>
pc.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
<path android:fillColor="#FF000000" android:pathData="M900.1,904.6"/>
<path android:fillColor="#FF000000" android:pathData="M948.1,811.2l-45.1,0L121,811.2 75.9,811.2c-24.9,0 -45.1,-20.1 -45.1,-44.9l0,-74.8 90.2,0L121,257.7c0,-24.8 20.2,-44.9 45.1,-44.9l691.7,0c24.9,0 45.1,20.1 45.1,44.9l0,433.9 90.2,0 0,74.8C993.2,791.2 973,811.2 948.1,811.2zM406.7,766.4l210.5,0 0,-29.9L406.7,736.4 406.7,766.4zM842.8,272.6 L181.2,272.6l0,389 661.6,0L842.8,272.6z"/>
</vector>
phone.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
<path android:fillColor="#FF000000" android:pathData="M776.7,1010.1h-479.9c-28.3,0 -51.3,-23 -51.3,-51.3v-891.1c0,-28.3 23,-51.3 51.3,-51.3h479.9c28.3,0 51.3,23 51.3,51.3v891.1c0,28.3 -23,51.3 -51.3,51.3v0zM536.6,958.8c19.1,0 34.2,-15.4 34.2,-34.2s-15.4,-34.2 -34.2,-34.2 -34.2,15.4 -34.2,34.2 15.4,34.2 34.2,34.2v0zM776.7,119h-479.9v719.7h479.6v-719.7h0.3z"/>
</vector>
wearable.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
<path android:fillColor="#44454A" android:pathData="M866.5,445.1L866.5,402.9c0,-190.9 -155.3,-346.2 -346.2,-346.2S174.1,212 174.1,402.9v38.6c-50.5,11.7 -88.2,56.9 -88.2,110.9v94.5c0,62.8 51,113.8 113.8,113.8s113.8,-51 113.8,-113.8v-94.5c0,-54 -37.7,-99.2 -88.2,-110.9v-38.6c0,-162.7 132.4,-295 295,-295 162.7,0 295,132.4 295,295v36.4c-56.6,6.5 -100.8,54.7 -100.8,113v94.5c0,53.6 37.2,98.6 87.2,110.6 -4.7,28 -28.2,90.6 -143.9,112.1 -13.9,2.6 -23.1,15.9 -20.5,29.8a25.6,25.6 0,0 0,25.1 20.9c1.5,0 3.1,-0.2 4.7,-0.4 152.9,-28.4 180.8,-122.4 185.8,-162 51,-11.3 89.2,-56.8 89.2,-111.2v-94.5c-0.1,-49.4 -31.7,-91.5 -75.7,-107.2zM262.3,552.3v94.5c0,34.6 -28.1,62.6 -62.6,62.6 -34.6,0 -62.6,-28.1 -62.6,-62.6v-94.5c0,-34.6 28.1,-62.6 62.6,-62.6s62.6,28.1 62.6,62.6zM891,646.9c0,34.6 -28.1,62.6 -62.6,62.6s-62.6,-28.1 -62.6,-62.6v-94.5c0,-34.6 28.1,-62.6 62.6,-62.6s62.6,28.1 62.6,62.6v94.5z"/>
<path android:fillColor="#44454A" android:pathData="M531.4,902.7m-61.4,0a61.4,61.4 0,1 0,122.9 0,61.4 61.4,0 1,0 -122.9,0Z"/>
</vector>