如何在发现时从蓝牙过滤 ListView 中显示的设备
Posted
技术标签:
【中文标题】如何在发现时从蓝牙过滤 ListView 中显示的设备【英文标题】:How to filter devices shown in ListView from bluetooth on discover 【发布时间】:2021-09-22 05:50:10 【问题描述】:我正在编写一个应用程序,它有一部分在单击按钮时会扫描蓝牙设备并将它们显示在 ListView 中。它工作正常,但我需要它只显示名称中以单词开头的设备,例如:“MyArduino/123”,所以我需要它在设备名称中查找“MyArduino/”部分并只显示那些拥有它。
XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.test.app.MainActivity">
<TextView
android:layout_
android:layout_
android:id="@+id/title"
android:textSize="20sp"
android:textAlignment="center"
android:text="@string/title"
android:gravity="center_horizontal" />
<Button
android:layout_
android:layout_
android:layout_below="@id/title"
android:text="@string/bt_menu"
android:background="@color/button"
android:onClick="showBt"
android:id="@+id/to_bt_menu"
android:layout_margin="10sp"
tools:ignore="UsingOnClickInXml" />
<Button
android:id="@+id/to_vars_menu"
android:layout_
android:layout_
android:layout_below="@id/title"
android:layout_marginLeft="10sp"
android:layout_marginStart="10sp"
android:layout_toEndOf="@id/to_bt_menu"
android:layout_toRightOf="@id/to_bt_menu"
android:onClick="showVars"
android:text="@string/vars_menu"
android:background="@color/button"
android:layout_margin="10sp"
tools:ignore="UsingOnClickInXml" />
<TextView
android:id="@+id/status"
android:layout_
android:layout_
android:layout_below="@id/to_bt_menu"
android:visibility="gone"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/status"
android:textStyle="bold" />
<TextView
android:id="@+id/bluetooth_status"
android:layout_
android:layout_
android:layout_below="@id/to_bt_menu"
android:visibility="gone"
android:layout_toEndOf="@id/status"
android:layout_toRightOf="@id/status"
android:layout_marginLeft="20sp"
android:layout_marginStart="20sp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/bluetooth_status" />
<Button
android:id="@+id/scan"
android:layout_
android:layout_
android:layout_below="@id/bluetooth_status"
android:layout_margin="10sp"
android:visibility="gone"
android:text="@string/bluetooth_on"
android:background="@color/button" />
<Button
android:id="@+id/off"
android:layout_
android:layout_
android:layout_below="@id/bluetooth_status"
android:layout_margin="10sp"
android:visibility="gone"
android:layout_toRightOf="@id/scan"
android:layout_toEndOf="@id/scan"
android:background="@color/button"
android:text="@string/bluetooth_off" />
<Button
android:id="@+id/paired_btn"
android:layout_
android:layout_
android:layout_below="@id/off"
android:layout_margin="10sp"
android:visibility="gone"
android:text="@string/show_paired_devices"
android:background="@color/button" />
<Button
android:id="@+id/discover"
android:layout_
android:layout_
android:layout_below="@id/off"
android:layout_margin="10sp"
android:visibility="gone"
android:layout_toEndOf="@+id/paired_btn"
android:layout_toRightOf="@id/paired_btn"
android:text="@string/discover_new_devices"
android:background="@color/button" />
<ListView
android:id="@+id/devices_list_view"
android:layout_
android:layout_
android:layout_below="@id/discover"
android:visibility="gone"
android:choiceMode="singleChoice" />
<Button
android:layout_
android:layout_
android:layout_marginTop="20sp"
android:onClick="reqVars"
android:text="@string/req_btn"
android:layout_below="@id/user_edit"
android:visibility="gone"
android:id="@+id/req_btn"
android:layout_margin="10sp"
android:background="@color/button" />
<EditText
android:layout_
android:layout_
android:layout_below="@id/to_vars_menu"
android:layout_margin="10sp"
android:visibility="gone"
android:id="@+id/user_edit"
android:hint="@string/hint_edit"
tools:ignore="TextFields"/>
<Button
android:id="@+id/send_edit_btn"
android:layout_margin="10sp"
android:layout_
android:layout_
android:layout_below="@+id/user_edit"
android:visibility="gone"
android:layout_marginStart="20sp"
android:layout_marginLeft="20sp"
android:layout_marginTop="20sp"
android:layout_toEndOf="@+id/req_btn"
android:layout_toRightOf="@+id/req_btn"
android:onClick="editVar"
android:text="@string/send_edit_btn"
android:background="@color/button" />
<ListView
android:layout_below="@id/req_btn"
android:visibility="gone"
android:id="@+id/vars"
android:choiceMode="singleChoice"
android:listSelector="@color/list"
android:layout_
android:layout_ />
</RelativeLayout>
主活动:
package com.test.app;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener
private final String TAG = MainActivity.class.getSimpleName();
private static final UUID BT_MODULE_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // "random" unique identifier
// #defines for identifying shared types between calling functions
private final static int REQUEST_ENABLE_BT = 1; // used to identify adding bluetooth names
public final static int MESSAGE_READ = 2; // used in bluetooth handler to identify message update
private final static int CONNECTING_STATUS = 3; // used in bluetooth handler to identify message status
// GUI Components
private TextView mBluetoothStatus;
private Button mScanBtn;
private Button mOffBtn;
private Button mListPairedDevicesBtn;
private Button mDiscoverBtn;
private ListView mDevicesListView;
private TextView mStatus;
private EditText mUserEdit;
private Button mReqBtn;
private Button mSendEdit;
private ListView mVars;
private String mSelecVar;
private String mMessage;
private BluetoothAdapter mBTAdapter;
private Set<BluetoothDevice> mPairedDevices;
private ArrayList<BluetoothDevice> mBTDevices;
private ArrayAdapter<String> mBTArrayAdapter;
private Handler mHandler; // Our main handler that will receive callback notifications
private ConnectedThread mConnectedThread; // bluetooth background worker thread to send and receive data
private BluetoothSocket mBTSocket = null; // bi-directional client-to-client data path
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
Objects.requireNonNull(getSupportActionBar()).hide();
else
getSupportActionBar().hide();
setContentView(R.layout.activity_main);
mBluetoothStatus = (TextView)findViewById(R.id.bluetooth_status);
mScanBtn = (Button)findViewById(R.id.scan);
mOffBtn = (Button)findViewById(R.id.off);
mDiscoverBtn = (Button)findViewById(R.id.discover);
mListPairedDevicesBtn = (Button)findViewById(R.id.paired_btn);
mStatus = (TextView)findViewById(R.id.status);
mUserEdit = (EditText)findViewById(R.id.user_edit);
mReqBtn = (Button)findViewById(R.id.req_btn);
mSendEdit = (Button)findViewById(R.id.send_edit_btn);
mVars = (ListView)findViewById(R.id.vars);
mBTArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
mBTDevices = new ArrayList<>();
mBTAdapter = BluetoothAdapter.getDefaultAdapter(); // get a handle on the bluetooth radio
mDevicesListView = (ListView)findViewById(R.id.devices_list_view);
assert mDevicesListView != null;
mDevicesListView.setAdapter(mBTArrayAdapter); // assign model to view
mDevicesListView.setOnItemClickListener(mDeviceClickListener);
mVars.setOnItemClickListener(new AdapterView.OnItemClickListener()
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
mSelecVar = mVars.getItemAtPosition(position).toString().split(",")[0];
);
// Ask for location permission if not already allowed
if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(this, new String[]Manifest.permission.ACCESS_COARSE_LOCATION, 1);
mHandler = new Handler(Looper.getMainLooper())
@Override
public void handleMessage(Message msg)
if(msg.what == MESSAGE_READ)
String readMessage = null;
try
readMessage = new String((byte[]) msg.obj, "UTF-8");
catch (UnsupportedEncodingException e)
e.printStackTrace();
mMessage = readMessage;
if (mMessage != null)
for (int i = 0; i < mMessage.length(); i++)
if (mMessage.toCharArray()[i] == ';')
getVars(mMessage);
break;
if(msg.what == CONNECTING_STATUS)
if(msg.arg1 == 1)
mBluetoothStatus.setText("Connected to Device: " + msg.obj);
mConnectedThread.write("list");
else
mBluetoothStatus.setText("Connection Failed");
;
if (mBTArrayAdapter == null)
// Device does not support Bluetooth
mBluetoothStatus.setText("Status: Bluetooth not found");
Toast.makeText(getApplicationContext(),"Bluetooth device not found!",Toast.LENGTH_SHORT).show();
else
mScanBtn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
bluetoothOn();
);
mOffBtn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
bluetoothOff();
);
mListPairedDevicesBtn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
listPairedDevices();
);
mDiscoverBtn.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
discover();
);
private void bluetoothOn()
if (!mBTAdapter.isEnabled())
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
mBluetoothStatus.setText("Bluetooth enabled");
Toast.makeText(getApplicationContext(),"Bluetooth turned on",Toast.LENGTH_SHORT).show();
else
Toast.makeText(getApplicationContext(),"Bluetooth is already on", Toast.LENGTH_SHORT).show();
// Enter here after user selects "yes" or "no" to enabling radio
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent Data)
// Check which request we're responding to
if (requestCode == REQUEST_ENABLE_BT)
// Make sure the request was successful
if (resultCode == RESULT_OK)
// The user picked a contact.
// The Intent's data Uri identifies which contact was selected.
mBluetoothStatus.setText("Enabled");
else
mBluetoothStatus.setText("Disabled");
private void bluetoothOff()
mBTAdapter.disable(); // turn off
mBluetoothStatus.setText("Bluetooth disabled");
Toast.makeText(getApplicationContext(),"Bluetooth turned Off", Toast.LENGTH_SHORT).show();
private void discover()
// Check if the device is already discovering
if(mBTAdapter.isDiscovering())
mBTAdapter.cancelDiscovery();
Toast.makeText(getApplicationContext(),"Discovery stopped",Toast.LENGTH_SHORT).show();
else
if(mBTAdapter.isEnabled())
mBTArrayAdapter.clear(); // clear items
mBTAdapter.startDiscovery();
Toast.makeText(getApplicationContext(), "Discovery started", Toast.LENGTH_SHORT).show();
registerReceiver(blReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
else
Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
final BroadcastReceiver blReceiver = 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);
// add the name to the list
mBTDevices.add(device);
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
mBTArrayAdapter.notifyDataSetChanged();
if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED))
BluetoothDevice mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (mDevice.getBondState() == BluetoothDevice.BOND_BONDED)
Log.d(TAG, "BroadcastReceiver: BOND_BONDED");
if (mDevice.getBondState() == BluetoothDevice.BOND_BONDING)
Log.d(TAG, "BroadcastReceiver: BOND_BONDING");
if (mDevice.getBondState() == BluetoothDevice.BOND_NONE)
Log.d(TAG, "BroadcastReceiver: BOND_NONE");
;
private void listPairedDevices()
mBTArrayAdapter.clear();
mPairedDevices = mBTAdapter.getBondedDevices();
if(mBTAdapter.isEnabled())
// put it's one to the adapter
for (BluetoothDevice device : mPairedDevices)
if (device.getName().equals("MyArduino/"))
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
Toast.makeText(getApplicationContext(), "Show Paired Devices", Toast.LENGTH_SHORT).show();
else
Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
private AdapterView.OnItemClickListener mDeviceClickListener = new AdapterView.OnItemClickListener()
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
if(!mBTAdapter.isEnabled())
Toast.makeText(getBaseContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
return;
mBluetoothStatus.setText("Connecting...");
// Get the device MAC address, which is the last 17 chars in the View
String info = ((TextView) view).getText().toString();
final String address = info.substring(info.length() - 17);
final String name = info.substring(0,info.length() - 17);
// Spawn a new thread to avoid blocking the GUI one
new Thread()
@Override
public void run()
boolean fail = false;
BluetoothDevice device = mBTAdapter.getRemoteDevice(address);
try
mBTSocket = createBluetoothSocket(device);
catch (IOException e)
fail = true;
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
// Establish the Bluetooth socket connection.
try
mBTSocket.connect();
catch (IOException e)
try
fail = true;
mBTSocket.close();
mHandler.obtainMessage(CONNECTING_STATUS, -1, -1)
.sendToTarget();
catch (IOException e2)
//insert code to deal with this
Toast.makeText(getBaseContext(), "Socket creation failed", Toast.LENGTH_SHORT).show();
if(!fail)
mConnectedThread = new ConnectedThread(mBTSocket, mHandler);
mConnectedThread.start();
mHandler.obtainMessage(CONNECTING_STATUS, 1, -1, name)
.sendToTarget();
.start();
;
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException
try
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", UUID.class);
return (BluetoothSocket) m.invoke(device, BT_MODULE_UUID);
catch (Exception e)
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
return device.createRfcommSocketToServiceRecord(BT_MODULE_UUID);
public void showBt(View view)
mBluetoothStatus.setVisibility(View.VISIBLE);
mScanBtn.setVisibility(View.VISIBLE);
mOffBtn.setVisibility(View.VISIBLE);
mDiscoverBtn.setVisibility(View.VISIBLE);
mListPairedDevicesBtn.setVisibility(View.VISIBLE);
mDevicesListView.setVisibility(View.VISIBLE);
mStatus.setVisibility(View.VISIBLE);
mVars.setVisibility(View.GONE);
mUserEdit.setVisibility(View.GONE);
mSendEdit.setVisibility(View.GONE);
mReqBtn.setVisibility(View.GONE);
public void showVars(View view)
mBluetoothStatus.setVisibility(View.GONE);
mScanBtn.setVisibility(View.GONE);
mOffBtn.setVisibility(View.GONE);
mDiscoverBtn.setVisibility(View.GONE);
mListPairedDevicesBtn.setVisibility(View.GONE);
mDevicesListView.setVisibility(View.GONE);
mStatus.setVisibility(View.GONE);
mVars.setVisibility(View.VISIBLE);
mUserEdit.setVisibility(View.VISIBLE);
mSendEdit.setVisibility(View.VISIBLE);
mReqBtn.setVisibility(View.VISIBLE);
public void reqVars(View view)
if (mConnectedThread != null)
mConnectedThread.write("list");
public void getVars(String string)
String[] str = string.split(";");
List<String> al;
al = Arrays.asList(str);
ArrayAdapter<String> mVarsArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, al);
mVars.setAdapter(mVarsArrayAdapter);
public void editVar(View view)
if (mConnectedThread != null)
mConnectedThread.write("edit " + mSelecVar + " " + mUserEdit.getText());
mUserEdit.setText("");
SystemClock.sleep(2000);
mConnectedThread.write("list");
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l)
mBTAdapter.cancelDiscovery();
Log.d(TAG, "onItemClick: You clicked on a device.");
String deviceName = mBTDevices.get(i).getName();
String deviceAddress = mBTDevices.get(i).getAddress();
Log.d(TAG, "onItemClick: deviceName = " + deviceName);
Log.d(TAG, "onItemClick: deviceAddress = " + deviceAddress);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2)
Log.d(TAG, "Trying to pair with " + deviceName);
mBTDevices.get(i).createBond();
连接线程:
package com.test.app;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.SystemClock;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ConnectedThread extends Thread
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private final Handler mHandler;
public ConnectedThread(BluetoothSocket socket, Handler handler)
mmSocket = socket;
mHandler = handler;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
catch (IOException e)
mmInStream = tmpIn;
mmOutStream = tmpOut;
@Override
public void run()
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while (true)
try
// Read from the InputStream
bytes = mmInStream.available();
if(bytes != 0)
buffer = new byte[1024];
SystemClock.sleep(100); //pause and wait for rest of data. Adjust this depending on your sending speed.
bytes = mmInStream.available(); // how many bytes are ready to be read?
bytes = mmInStream.read(buffer, 0, bytes); // record how many bytes we actually read
mHandler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget(); // Send the obtained bytes to the UI activity
catch (IOException e)
e.printStackTrace();
break;
/* Call this from the main activity to send data to the remote device */
public void write(String input)
byte[] bytes = input.getBytes(); //converts entered String into bytes
try
mmOutStream.write(bytes);
catch (IOException e)
/* Call this from the main activity to shutdown the connection */
public void cancel()
try
mmSocket.close();
catch (IOException e)
现在,ListView 会显示蓝牙找到的所有设备,有时甚至会在同一个列表中显示 2 或 3 次。正如您在 MainActivity 中看到的那样,我尝试在第 272 行制作某种过滤器,但它不起作用。谢谢
【问题讨论】:
【参考方案1】:因此,经过数小时的反复试验,问题得到了解决。
我注意到崩溃错误中的这一行:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.startsWith(java.lang.String)' on a null object reference
这意味着该函数正在一个空对象中寻找“MyArduino/”。 所以我修改了这个:
if(device.getName().startsWith("MyArduino/"))
mBTDevices.add(device);
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
mBTArrayAdapter.notifyDataSetChanged();
到这里:
if (device.getName() != null)
if (device.getName().contains("MyAdruino/"))
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
mBTArrayAdapter.notifyDataSetChanged();
问题就解决了。它只显示了带有“MyAdruino/”名称的设备,而且只显示了一次。希望这能帮助遇到同样问题的人
【讨论】:
【参考方案2】:您试图让设备名称等于“MyArduino/”,但实际上您需要的是设备名称如何以“MyArduino/”开头:
只需在 MainActivity 的 listPairedDevices() 中替换这一行:
来自:(等于)
if (device.getName().equals("MyArduino/"))
到:(startsWith)
if (device.getName().startsWith("MyArduino/"))
编辑答案
if(BluetoothDevice.ACTION_FOUND.equals(action))
BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//添加设备前:
if(device.getName().startsWith("MyArduino/"))
// add the name to the list
mBTDevices.add(device);
mBTArrayAdapter.add(device.getName() + "\n" + device.getAddress());
mBTArrayAdapter.notifyDataSetChanged();
【讨论】:
所以,正如你所说,我编辑了它,但它是配对设备的一部分,所以当我配对 arduinos(实际上它们是 esp32)时,我会看到它们,但我需要它在发现,当我向 BroadcastReceiver blReceiver 部分添加相同的 if 语句时,它只会在点击发现按钮时使应用程序崩溃 添加该部分后,应用程序因以下错误而崩溃:pastebin.com/BE54rUyz以上是关于如何在发现时从蓝牙过滤 ListView 中显示的设备的主要内容,如果未能解决你的问题,请参考以下文章
如何在单击android中的listview项目时从数据库中获取id
更新 Firebase 时从 ListView 中删除旧条目
StreamBuilder ListView 在首次加载时从 Firestore 返回空列表
从 ListView 中选择一个蓝牙设备并打开一个新的 Intent (Android)