Android Ble蓝牙App(三)特性和属性

Ble蓝牙App(三)特性使用

  • 前言
  • 正文
    • 一、获取属性列表
    • 二、属性适配器
    • 三、获取特性名称
    • 四、特性适配器
    • 五、加载特性
    • 六、显示特性和属性
    • 七、源码

前言

  在上一篇中我们完成了连接和发现服务两个动作,那么再发现服务之后要做什么呢?发现服务只是让你知道设备有什么服务,可以做什么事情。

在这里插入图片描述

正文

  本篇要做的是显示服务下的特性,首先我们了解一下特性的基本知识。在蓝牙低功耗(BLE)中,特性(Characteristic)是蓝牙设备提供的一种数据单元,用于描述设备的某个属性或功能。特性包含了一系列的属性和值,可以用于读取、写入和通知数据。

BLE特性相关的关键概念和说明:

  1. UUID(Universally Unique Identifier):每个特性都会有一个唯一的UUID,用于标识该特性。
  2. 值(Value):特性包含一个值,可以是字节数组、字符串或其他数据类型。该值代表特性的当前状态或数据内容。
  3. 属性(Properties):特性具有一组属性,包括读、写、通知等。属性决定了可以对特性进行哪些操作。
  4. 读(Read):允许外部设备从特性中读取当前的值。
  5. 写(Write):允许外部设备向特性写入一个新的值。
  6. 通知(Notify):当特性的值发生变化时,可以通过通知方式将新的值发送给订阅该特性的外部设备。
  7. 描述符(Descriptor):特性可以附带一个或多个描述符,用于提供关于特性的额外信息或配置。

  使用BLE特性,可以实现各种功能和数据交互,例如传感器数据的读取、设备状态的监控、远程控制等。特性的读写和通知操作可以通过与蓝牙设备的交互来实现。需要注意的是,BLE特性的操作和功能是由设备的厂商定义的,并在设备的GATT(Generic Attribute Profile)配置文件中进行描述。

  首先理清一下思路,我们现在知道服务下面有特性,特性下面有一些属性值,其中属性(Properties)尤为重要,因为它决定了你的特性可以进行那些操作。用一个图来说明服务,特性,属性之间的关系。

在这里插入图片描述

一、获取属性列表

下面我们先获取最下面的属性,这是一个列表,属性值的处理有一些不同,首先我们在BleUtils中增加一个函数,代码如下所示:

    /*** 获取属性*/fun getProperties(property: Int): List<String> {val properties: MutableList<String> = ArrayList()for (i in 0..7) {when (property and (1 shl i)) {0x01 -> properties.add("Broadcast")0x02 -> properties.add("Read")0x04 -> properties.add("Write No Response")0x08 -> properties.add("Write")0x10 -> properties.add("Notify")0x20 -> properties.add("Indicate")0x40 -> properties.add("Authenticated Signed Writes")0x80 -> properties.add("Extended Properties")}}return properties}

  这里是通过位运算进行计算属性的值,首先是循环遍历,shl 是一种位运算符,用于执行按位左移操作。shl 是 “shift left” 的缩写。and用于执行按位与操作。先左移再按位与,得到最终的值,根据值得到属性描述,这些描述就是具体的功能操作。会返回一个属性列表,有了列表我们就可以写一个适配器了。

二、属性适配器

首先我们在layout下创建一个item_property.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/tv_property"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="8dp"android:text="property"android:textColor="@color/orange" />

  因为是String类型,所以我们就直接用一个TextView显示即可,下面我们写适配器,在adapter包下新建一个PropertyAdapter类,代码如下所示:

class PropertyAdapter(private val properties: List<String>,private val listener: OnItemClickListener
) : RecyclerView.Adapter<PropertyAdapter.ViewHolder>() {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {return ViewHolder(ItemPropertyBinding.inflate(LayoutInflater.from(parent.context), parent, false)).apply {binding.tvProperty.setOnClickListener { v -> listener.onItemClick(v, adapterPosition) }}}override fun onBindViewHolder(holder: ViewHolder, position: Int) {holder.binding.tvProperty.text = properties[position]}override fun getItemCount() = properties.sizeclass ViewHolder(itemView: ItemPropertyBinding) : RecyclerView.ViewHolder(itemView.root) {var binding: ItemPropertyBindinginit {binding = itemView}}
}

这里进行了属性的点击监听,我们可以回调到特性适配器中去处理,下面我们要处理的就是特性了。

三、获取特性名称

  首先是特性名称,同样是根据UUID,同样是那个PDF文档,在BleUtils中增加一个getCharacteristicsName()函数,代码有点多,如下所示:

    /*** 获取特性名称* @param uuid UUID*/fun getCharacteristicsName(uuid: UUID) =when ("0x${uuid.toString().substring(4, 8).uppercase(Locale.getDefault())}") {"0x2A00" -> "Device Name""0x2A01" -> "Appearance""0x2A02" -> "Peripheral Privacy Flag""0x2A03" -> "Reconnection Address""0x2A04" -> "Peripheral Preferred Connection Parameters""0x2A05" -> "Service Changed""0x2A06" -> "Alert Level""0x2A07" -> "Tx Power Level""0x2A08" -> "Date Time""0x2A09" -> "Day of Week""0x2A0A" -> "Day Date Time""0x2A0C" -> "Exact Time 256""0x2A0D" -> "DST Offset""0x2A0E" -> "Time Zone""0x2A0F" -> "Local Time Information""0x2A11" -> "Time with DST""0x2A12" -> "Time Accuracy""0x2A13" -> "Time Source""0x2A14" -> "Reference Time Information""0x2A16" -> "Time Update Control Point""0x2A17" -> "Time Update State""0x2A18" -> "Glucose Measurement""0x2A19" -> "Battery Level""0x2A1C" -> "Temperature Measurement""0x2A1D" -> "Temperature Type""0x2A1E" -> "Intermediate Temperature""0x2A21" -> "Measurement Interval""0x2A22" -> "Boot Keyboard Input Report""0x2A23" -> "System ID""0x2A24" -> "Model Number String""0x2A25" -> "Serial Number String""0x2A26" -> "Firmware Revision String""0x2A27" -> "Hardware Revision String""0x2A28" -> "Software Revision String""0x2A29" -> "Manufacturer Name String""0x2A2A" -> "IEEE 11073-20601 Regulatory Certification Data List""0x2A2B" -> "Current Time""0x2A2C" -> "Magnetic Declination""0x2A31" -> "Scan Refresh""0x2A32" -> "Boot Keyboard Output Report""0x2A33" -> "Boot Mouse Input Report""0x2A34" -> "Glucose Measurement Context""0x2A35" -> "Blood Pressure Measurement""0x2A36" -> "Intermediate Cuff Pressure""0x2A37" -> "Heart Rate Measurement""0x2A38" -> "Body Sensor Location""0x2A39" -> "Heart Rate Control Point""0x2A3F" -> "Alert Status""0x2A40" -> "Ringer Control Point""0x2A41" -> "Ringer Setting""0x2A42" -> "Alert Category ID Bit Mask""0x2A43" -> "Alert Category ID""0x2A44" -> "Alert Notification Control Point""0x2A45" -> "Unread Alert Status""0x2A46" -> "New Alert""0x2A47" -> "Supported New Alert Category""0x2A48" -> "Supported Unread Alert Category""0x2A49" -> "Blood Pressure Feature""0x2A4A" -> "HID Information""0x2A4B" -> "Report Map""0x2A4C" -> "HID Control Point""0x2A4D" -> "Report""0x2A4E" -> "Protocol Mode""0x2A4F" -> "Scan Interval Window""0x2A50" -> "PnP ID""0x2A51" -> "Glucose Feature""0x2A52" -> "Record Access Control Point""0x2A53" -> "RSC Measurement""0x2A54" -> "RSC Feature""0x2A55" -> "SC Control Point""0x2A5A" -> "Aggregate""0x2A5B" -> "CSC Measurement""0x2A5C" -> "CSC Feature""0x2A5D" -> "Sensor Location""0x2A5E" -> "PLX Spot-Check Measurement""0x2A5F" -> "PLX Continuous Measurement""0x2A60" -> "PLX Features""0x2A63" -> "Cycling Power Measurement""0x2A64" -> "Cycling Power Vector""0x2A65" -> "Cycling Power Feature""0x2A66" -> "Cycling Power Control Point""0x2A67" -> "Location and Speed""0x2A68" -> "Navigation""0x2A69" -> "Position Quality""0x2A6A" -> "LN Feature""0x2A6B" -> "LN Control Point""0x2A6C" -> "Elevation""0x2A6D" -> "Pressure""0x2A6E" -> "Temperature""0x2A6F" -> "Humidity""0x2A70" -> "True Wind Speed""0x2A71" -> "True Wind Direction""0x2A72" -> "Apparent Wind Speed""0x2A73" -> "Apparent Wind Direction""0x2A74" -> "Gust Factor""0x2A75" -> "Pollen Concentration""0x2A76" -> "UV Index""0x2A77" -> "Irradiance""0x2A78" -> "Rainfall""0x2A79" -> "Wind Chill""0x2A7A" -> "Heat Index""0x2A7B" -> "Dew Point""0x2A7D" -> "Descriptor Value Changed""0x2A7E" -> "Aerobic Heart Rate Lower Limit""0x2A7F" -> "Aerobic Threshold""0x2A80" -> "Age""0x2A81" -> "Anaerobic Heart Rate Lower Limit""0x2A82" -> "Anaerobic Heart Rate Upper Limit""0x2A83" -> "Anaerobic Threshold""0x2A84" -> "Aerobic Heart Rate Upper Limit""0x2A85" -> "Date of Birth""0x2A86" -> "Date of Threshold Assessment""0x2A87" -> "Email Address""0x2A88" -> "Fat Burn Heart Rate Lower Limit""0x2A89" -> "Fat Burn Heart Rate Upper Limit""0x2A8A" -> "First Name""0x2A8B" -> "Five Zone Heart Rate Limits""0x2A8C" -> "Gender""0x2A8D" -> "Heart Rate Max""0x2A8E" -> "Height""0x2A8F" -> "Hip Circumference""0x2A90" -> "Last Name""0x2A91" -> "Maximum Recommended Heart Rate""0x2A92" -> "Resting Heart Rate""0x2A93" -> "Sport Type for Aerobic and Anaerobic Thresholds""0x2A94" -> "Three Zone Heart Rate Limits""0x2A95" -> "Two Zone Heart Rate Limits""0x2A96" -> "VO2 Max""0x2A97" -> "Waist Circumference""0x2A98" -> "Weight""0x2A99" -> "Database Change Increment""0x2A9A" -> "User Index""0x2A9B" -> "Body Composition Feature""0x2A9C" -> "Body Composition Measurement""0x2A9D" -> "Weight Measurement""0x2A9E" -> "Weight Scale Feature""0x2A9F" -> "User Control Point""0x2AA0" -> "Magnetic Flux Density - 2D""0x2AA1" -> "Magnetic Flux Density - 3D""0x2AA2" -> "Language""0x2AA3" -> "Barometric Pressure Trend""0x2AA4" -> "Bond Management Control Point""0x2AA5" -> "Bond Management Feature""0x2AA6" -> "Central Address Resolution""0x2AA7" -> "CGM Measurement""0x2AA8" -> "CGM Feature""0x2AA9" -> "CGM Status""0x2AAA" -> "CGM Session Start Time""0x2AAB" -> "CGM Session Run Time""0x2AAC" -> "CGM Specific Ops Control Point""0x2AAD" -> "Indoor Positioning Configuration""0x2AAE" -> "Latitude""0x2AAF" -> "Longitude""0x2AB0" -> "Local North Coordinate""0x2AB1" -> "Local East Coordinate""0x2AB2" -> "Floor Number""0x2AB3" -> "Altitude""0x2AB4" -> "Uncertainty""0x2AB5" -> "Location Name""0x2AB6" -> "URI""0x2AB7" -> "HTTP Headers""0x2AB8" -> "HTTP Status Code""0x2AB9" -> "HTTP Entity Body""0x2ABA" -> "HTTP Control Point""0x2ABB" -> "HTTPS Security""0x2ABC" -> "TDS Control Point""0x2ABD" -> "OTS Feature""0x2ABE" -> "Object Name""0x2ABF" -> "Object Type""0x2AC0" -> "Object Size""0x2AC1" -> "Object First -Created""0x2AC2" -> "Object Last - Modified""0x2AC3" -> "Object ID""0x2AC4" -> "Object Properties""0x2AC5" -> "Object Action Control Point""0x2AC6" -> "Object List Control Point""0x2AC7" -> "Object List Filter""0x2AC8" -> "Object Changed""0x2AC9" -> "Resolvable Private Address Only""0x2ACC" -> "Fitness Machine Feature""0x2ACD" -> "Treadmill Data""0x2ACE" -> "Cross Trainer Data""0x2ACF" -> "Step Climber Data""0x2AD0" -> "Stair Climber Data""0x2AD1" -> "Rower Data""0x2AD2" -> "Indoor Bike Data""0x2AD3" -> "Training Status""0x2AD4" -> "Supported Speed Range""0x2AD5" -> "Supported Inclination Range""0x2AD6" -> "Supported Resistance Level Range""0x2AD7" -> "Supported Heart Rate Range""0x2AD8" -> "Supported Power Range""0x2AD9" -> "Fitness Machine Control Point""0x2ADA" -> "Fitness Machine Status""0x2ADB" -> "Mesh Provisioning Data In""0x2ADC" -> "Mesh Provisioning Data Out""0x2ADD" -> "Mesh Proxy Data In""0x2ADE" -> "Mesh Proxy Data Out""0x2AE0" -> "Average Current""0x2AE1" -> "Average Voltage""0x2AE2" -> "Boolean""0x2AE3" -> "Chromatic Distance from Planckian""0x2AE4" -> "Chromaticity Coordinates""0x2AE5" -> "Chromaticity in CCT and Duv Values""0x2AE6" -> "Chromaticity Tolerance""0x2AE7" -> "CIE 13.3 - 1995 Color Rendering Index""0x2AE8" -> "Coefficient""0x2AE9" -> "Correlated Color Temperature""0x2AEA" -> "Count 16""0x2AEB" -> "Count 24""0x2AEC" -> "Country Code""0x2AED" -> "Date UTC""0x2AEE" -> "Electric Current""0x2AEF" -> "Electric Current Range""0x2AF0" -> "Electric Current Specification""0x2AF1" -> "Electric Current Statistics""0x2AF2" -> "Energy""0x2AF3" -> "Energy in a Period of Day""0x2AF4" -> "Event Statistics""0x2AF5" -> "Fixed String 16""0x2AF6" -> "Fixed String 24""0x2AF7" -> "Fixed String 36""0x2AF8" -> "Fixed String 8""0x2AF9" -> "Generic Level""0x2AFA" -> "Global Trade Item Number""0x2AFB" -> "Illuminance""0x2AFC" -> "Luminous Efficacy""0x2AFD" -> "Luminous Energy""0x2AFE" -> "Luminous Exposure""0x2AFF" -> "Luminous Flux""0x2B00" -> "Luminous Flux Range""0x2B01" -> "Luminous Intensity""0x2B02" -> "Mass Flow""0x2B03" -> "Perceived Lightness""0x2B04" -> "Percentage 8""0x2B05" -> "Power""0x2B06" -> "Power Specification""0x2B07" -> "Relative Runtime in a Current Range""0x2B08" -> "Relative Runtime in a Generic Level Range""0x2B09" -> "Relative Value in a Voltage Range""0x2B0A" -> "Relative Value in an Illuminance Range""0x2B0B" -> "Relative Value in a Period of Day""0x2B0C" -> "Relative Value in a Temperature Range""0x2B0D" -> "Temperature 8""0x2B0E" -> "Temperature 8 in a Period of Day""0x2B0F" -> "Temperature 8 Statistics""0x2B10" -> "Temperature Range""0x2B11" -> "Temperature Statistics""0x2B12" -> "Time Decihour 8""0x2B13" -> "Time Exponential 8""0x2B14" -> "Time Hour 24""0x2B15" -> "Time Millisecond 24""0x2B16" -> "Time Second 16""0x2B17" -> "Time Second 8""0x2B18" -> "Voltage""0x2B19" -> "Voltage Specification""0x2B1A" -> "Voltage Statistics""0x2B1B" -> "Volume Flow""0x2B1C" -> "Chromaticity Coordinate""0x2B1D" -> "RC Feature""0x2B1E" -> "RC Settings""0x2B1F" -> "Reconnection Configuration Control Point""0x2B20" -> "IDD Status Changed""0x2B21" -> "IDD Status""0x2B22" -> "IDD Annunciation Status""0x2B23" -> "IDD Features""0x2B24" -> "IDD Status Reader Control Point""0x2B25" -> "IDD Command Control Point""0x2B26" -> "IDD Command Data""0x2B27" -> "IDD Record Access Control Point""0x2B28" -> "IDD History Data""0x2B29" -> "Client Supported Features""0x2B2A" -> "Database Hash""0x2B2B" -> "BSS Control Point""0x2B2C" -> "BSS Response""0x2B2D" -> "Emergency ID""0x2B2E" -> "Emergency Text""0x2B2F" -> "ACS Status""0x2B30" -> "ACS Data In""0x2B31" -> "ACS Data Out Notify""0x2B32" -> "ACS Data Out Indicate""0x2B33" -> "ACS Control Point""0x2B34" -> "Enhanced Blood Pressure Measurement""0x2B35" -> "Enhanced Intermediate Cuff Pressure""0x2B36" -> "Blood Pressure Record""0x2B37" -> "Registered User""0x2B38" -> "BR - EDR Handover Data""0x2B39" -> "Bluetooth SIG Data""0x2B3A" -> "Server Supported Features""0x2B3B" -> "Physical Activity Monitor Features""0x2B3C" -> "General Activity Instantaneous Data""0x2B3D" -> "General Activity Summary Data""0x2B3E" -> "CardioRespiratory Activity Instantaneous Data""0x2B3F" -> "CardioRespiratory Activity Summary Data""0x2B40" -> "Step Counter Activity Summary Data""0x2B41" -> "Sleep Activity Instantaneous Data""0x2B42" -> "Sleep Activity Summary Data""0x2B43" -> "Physical Activity Monitor Control Point""0x2B44" -> "Activity Current Session""0x2B45" -> "Physical Activity Session Descriptor""0x2B46" -> "Preferred Units""0x2B47" -> "High Resolution Height""0x2B48" -> "Middle Name""0x2B49" -> "Stride Length""0x2B4A" -> "Handedness""0x2B4B" -> "Device Wearing Position""0x2B4C" -> "Four Zone Heart Rate Limits""0x2B4D" -> "High Intensity Exercise Threshold""0x2B4E" -> "Activity Goal""0x2B4F" -> "Sedentary Interval Notification""0x2B50" -> "Caloric Intake""0x2B51" -> "TMAP Role""0x2B77" -> "Audio Input State""0x2B78" -> "Gain Settings Attribute""0x2B79" -> "Audio Input Type""0x2B7A" -> "Audio Input Status""0x2B7B" -> "Audio Input Control Point""0x2B7C" -> "Audio Input Description""0x2B7D" -> "Volume State""0x2B7E" -> "Volume Control Point""0x2B7F" -> "Volume Flags""0x2B80" -> "Volume Offset State""0x2B81" -> "Audio Location""0x2B82" -> "Volume Offset Control Point""0x2B83" -> "Audio Output Description""0x2B84" -> "Set Identity Resolving Key""0x2B85" -> "Coordinated Set Size""0x2B86" -> "Set Member Lock""0x2B87" -> "Set Member Rank""0x2B88" -> "Encrypted Data Key Material""0x2B89" -> "Apparent Energy 32""0x2B8A" -> "Apparent Power""0x2B8B" -> "Live Health Observations""0x2B8C" -> "CO \\{} text-subscript { 2 } Concentration""0x2B8D" -> "Cosine of the Angle""0x2B8E" -> "Device Time Feature""0x2B8F" -> "Device Time Parameters""0x2B90" -> "Device Time""0x2B91" -> "Device Time Control Point""0x2B92" -> "Time Change Log Data""0x2B93" -> "Media Player Name""0x2B94" -> "Media Player Icon Object ID""0x2B95" -> "Media Player Icon URL""0x2B96" -> "Track Changed""0x2B97" -> "Track Title""0x2B98" -> "Track Duration""0x2B99" -> "Track Position""0x2B9A" -> "Playback Speed""0x2B9B" -> "Seeking Speed""0x2B9C" -> "Current Track Segments Object ID""0x2B9D" -> "Current Track Object ID""0x2B9E" -> "Next Track Object ID""0x2B9F" -> "Parent Group Object ID""0x2BA0" -> "Current Group Object ID""0x2BA1" -> "Playing Order""0x2BA2" -> "Playing Orders Supported""0x2BA3" -> "Media State""0x2BA4" -> "Media Control Point""0x2BA5" -> "Media Control Point Opcodes Supported""0x2BA6" -> "Search Results Object ID""0x2BA7" -> "Search Control Point""0x2BA8" -> "Energy 32""0x2BA9" -> "Media Player Icon Object Type""0x2BAA" -> "Track Segments Object Type""0x2BAB" -> "Track Object Type""0x2BAC" -> "Group Object Type""0x2BAD" -> "Constant Tone Extension Enable""0x2BAE" -> "Advertising Constant Tone Extension Minimum Length""0x2BAF" -> "Advertising Constant Tone Extension Minimum Transmit Count""0x2BB0" -> "Advertising Constant Tone Extension Transmit Duration""0x2BB1" -> "Advertising Constant Tone Extension Interval""0x2BB2" -> "Advertising Constant Tone Extension PHY""0x2BB3" -> "Bearer Provider Name""0x2BB4" -> "Bearer UCI""0x2BB5" -> "Bearer Technology""0x2BB6" -> "Bearer URI Schemes Supported List""0x2BB7" -> "Bearer Signal Strength""0x2BB8" -> "Bearer Signal Strength Reporting Interval""0x2BB9" -> "Bearer List Current Calls""0x2BBA" -> "Content Control ID""0x2BBB" -> "Status Flags""0x2BBC" -> "Incoming Call Target Bearer URI""0x2BBD" -> "Call State""0x2BBE" -> "Call Control Point""0x2BBF" -> "Call Control Point Optional Opcodes""0x2BC0" -> "Termination Reason""0x2BC1" -> "Incoming Call""0x2BC2" -> "Call Friendly Name""0x2BC3" -> "Mute""0x2BC4" -> "Sink ASE""0x2BC5" -> "Source ASE""0x2BC6" -> "ASE Control Point""0x2BC7" -> "Broadcast Audio Scan Control Point""0x2BC8" -> "Broadcast Receive State""0x2BC9" -> "Sink PAC""0x2BCA" -> "Sink Audio Locations""0x2BCB" -> "Source PAC""0x2BCC" -> "Source Audio Locations""0x2BCD" -> "Available Audio Contexts""0x2BCE" -> "Supported Audio Contexts""0x2BCF" -> "Ammonia Concentration""0x2BD0" -> "Carbon Monoxide Concentration""0x2BD1" -> "Methane Concentration""0x2BD2" -> "Nitrogen Dioxide Concentration""0x2BD3" -> "Non -Methane Volatile Organic Compounds Concentration""0x2BD4" -> "Ozone Concentration""0x2BD5" -> "Particulate Matter - PM1 Concentration""0x2BD6" -> "Particulate Matter - PM2.5 Concentration""0x2BD7" -> "Particulate Matter - PM10 Concentration""0x2BD8" -> "Sulfur Dioxide Concentration""0x2BD9" -> "Sulfur Hexafluoride Concentration""0x2BDA" -> "Hearing Aid Features""0x2BDB" -> "Hearing Aid Preset Control Point""0x2BDC" -> "Active Preset Index""0x2BDD" -> "Stored Health Observations""0x2BDE" -> "Fixed String 64""0x2BDF" -> "High Temperature""0x2BE0" -> "High Voltage""0x2BE1" -> "Light Distribution""0x2BE2" -> "Light Output""0x2BE3" -> "Light Source Type""0x2BE4" -> "Noise""0x2BE5" -> "Relative Runtime in a Correlated Color Temperature Range""0x2BE6" -> "Time Second 32""0x2BE7" -> "VOC Concentration""0x2BE8" -> "Voltage Frequency""0x2BE9" -> "Battery Critical Status""0x2BEA" -> "Battery Health Status""0x2BEB" -> "Battery Health Information""0x2BEC" -> "Battery Information""0x2BED" -> "Battery Level Status""0x2BEE" -> "Battery Time Status""0x2BEF" -> "Estimated Service Date""0x2BF0" -> "Battery Energy Status""0x2BF1" -> "Observation Schedule Changed""0x2BF2" -> "Current Elapsed Time""0x2BF3" -> "Health Sensor Features""0x2BF4" -> "GHS Control Point""0x2BF5" -> "LE GATT Security Levels""0x2BF6" -> "ESL Address""0x2BF7" -> "AP Sync Key Material""0x2BF8" -> "ESL Response Key Material""0x2BF9" -> "ESL Current Absolute Time""0x2BFA" -> "ESL Display Information""0x2BFB" -> "ESL Image Information""0x2BFC" -> "ESL Sensor Information""0x2BFD" -> "ESL LED Information""0x2BFE" -> "ESL Control Point""0x2BFF" -> "UDI for Medical Devices"else -> "Unknown Characteristics"}

同修改一下原有的getServiceUUID()getShortUUID(),只改名字而已,之前命名有点不太严谨。

四、特性适配器

首先我们在layout下创建一个item_characteristic.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="1dp"android:foreground="?attr/selectableItemBackground"android:paddingStart="16dp"><TextViewandroid:id="@+id/tv_character_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="8dp"android:text="特性"android:textColor="@color/black"android:textSize="16sp"android:textStyle="bold"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_uuid_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="UUID:"app:layout_constraintStart_toStartOf="@+id/tv_character_name"app:layout_constraintTop_toBottomOf="@+id/tv_character_name" /><TextViewandroid:id="@+id/tv_character_uuid"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="UUID"android:textColor="@color/black"app:layout_constraintBottom_toBottomOf="@+id/tv_uuid_title"app:layout_constraintStart_toEndOf="@+id/tv_uuid_title"app:layout_constraintTop_toTopOf="@+id/tv_uuid_title" /><TextViewandroid:id="@+id/tv_property_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="8dp"android:text="Properties:"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="@+id/tv_character_name"app:layout_constraintTop_toBottomOf="@+id/tv_uuid_title" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_property"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="@+id/tv_property_title"app:layout_constraintStart_toEndOf="@+id/tv_property_title"app:layout_constraintTop_toTopOf="@+id/tv_property_title" /></androidx.constraintlayout.widget.ConstraintLayout>

  这里显示特性的名称和UUIID,同时加载属性列表,然后写适配器,因为需要操作属性的缘故,这些写一个接口,在adapter包下新建一个OperateCallback接口,代码如下所示:

interface OperateCallback {/*** 属性操作*/fun onPropertyOperate(characteristic: BluetoothGattCharacteristic, operateName: String)
}

通过这个接口可以知道当前操作的是那个特性和属性名称。下面我们写适配器,在adapter包下新建一个CharacteristicAdapter类,代码如下所示:

class CharacteristicAdapter(private val characteristics: List<BluetoothGattCharacteristic>,private val callback: OperateCallback
) : RecyclerView.Adapter<CharacteristicAdapter.ViewHolder>() {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {return ViewHolder(ItemCharacteristicBinding.inflate(LayoutInflater.from(parent.context), parent, false))}override fun onBindViewHolder(holder: ViewHolder, position: Int) {holder.binding.tvCharacterName.text = BleUtils.getCharacteristicsName(characteristics[position].uuid)holder.binding.tvCharacterUuid.text = BleUtils.getShortUUID(characteristics[position].uuid)//加载特性下的属性holder.binding.rvProperty.apply {layoutManager = LinearLayoutManager(context).apply { orientation = LinearLayoutManager.HORIZONTAL }val properties: List<String> = BleUtils.getProperties(characteristics[position].properties)adapter = PropertyAdapter(properties, object : OnItemClickListener {//点击属性override fun onItemClick(view: View?, position: Int) { callback.onPropertyOperate(characteristics[position], properties[position]) }})}}override fun getItemCount() = characteristics.sizeclass ViewHolder(itemView: ItemCharacteristicBinding) : RecyclerView.ViewHolder(itemView.root) {var binding: ItemCharacteristicBindinginit {binding = itemView}}
}

在这里我们就可以处理特性的名称和UUID显示,同时加载属性适配器,显示出来。

五、加载特性

  因为特性是在服务下的,所以我们可以在服务适配器中加载特性适配器。首先我们修改一下item_service.xml,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:orientation="vertical"android:background="@color/white"android:layout_height="wrap_content"><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/item_service"android:layout_width="match_parent"android:foreground="?attr/selectableItemBackground"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:id="@+id/tv_service_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="16dp"android:layout_marginTop="8dp"android:text="服务"android:textColor="@color/black"android:textSize="16sp"android:textStyle="bold"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_uuid_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="UUID:"app:layout_constraintStart_toStartOf="@+id/tv_service_name"app:layout_constraintTop_toBottomOf="@+id/tv_service_name" /><TextViewandroid:id="@+id/tv_service_uuid"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="UUID"android:textColor="@color/black"app:layout_constraintBottom_toBottomOf="@+id/tv_uuid_title"app:layout_constraintStart_toEndOf="@+id/tv_uuid_title"app:layout_constraintTop_toTopOf="@+id/tv_uuid_title" /><TextViewandroid:id="@+id/tv_service_info"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="8dp"android:text="PRIMARY SERVICE"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="@+id/tv_service_name"app:layout_constraintTop_toBottomOf="@+id/tv_uuid_title" /><ImageViewandroid:id="@+id/iv_state"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="16dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent"app:srcCompat="@drawable/ic_right_24" /></androidx.constraintlayout.widget.ConstraintLayout><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_characteristics"android:visibility="gone"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingStart="16dp" />
</LinearLayout>

  整体上变化不大,只是加了一个RecyclerView,同时增加了一个ImageView,用于显示当前的服务是否展开,可以通过当前的服务item的方式控制是否显示特性列表,这里用到两个图标,在drawable下创建ic_right_24.xml,代码如下所示:

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="24dp"android:height="24dp"android:tint="#000000"android:viewportWidth="24"android:viewportHeight="24"><pathandroid:fillColor="@android:color/white"android:pathData="M10,17l5,-5 -5,-5v10z" />
</vector>

还有一个ic_down_24.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="24dp"android:height="24dp"android:tint="#000000"android:viewportWidth="24"android:viewportHeight="24"><pathandroid:fillColor="@android:color/white"android:pathData="M7,10l5,5 5,-5z" />
</vector>

下面修改一下ServiceAdapter,代码如下所示:

class ServiceAdapter(private val services: List<BluetoothGattService>,private val callback: OperateCallback
) : RecyclerView.Adapter<ServiceAdapter.ViewHolder>() {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val viewHolder = ViewHolder(ItemServiceBinding.inflate(LayoutInflater.from(parent.context), parent, false))viewHolder.binding.itemService.setOnClickListener {//显示特性列表viewHolder.binding.rvCharacteristics.visibility = if (viewHolder.binding.rvCharacteristics.visibility == View.VISIBLE) View.GONE else View.VISIBLE//更换图标viewHolder.binding.ivState.setImageDrawable(if (viewHolder.binding.rvCharacteristics.visibility == View.VISIBLE) ContextCompat.getDrawable(parent.context, R.drawable.ic_down_24)else ContextCompat.getDrawable(parent.context, R.drawable.ic_right_24))}return viewHolder}override fun onBindViewHolder(holder: ViewHolder, position: Int) {holder.binding.tvServiceName.text = BleUtils.getServiceName(services[position].uuid)holder.binding.tvServiceUuid.text = BleUtils.getShortUUID(services[position].uuid)//加载服务下的特性holder.binding.rvCharacteristics.apply {layoutManager = LinearLayoutManager(context)adapter = CharacteristicAdapter(services[position].characteristics, callback)}}override fun getItemCount() = services.sizeclass ViewHolder(itemView: ItemServiceBinding) : RecyclerView.ViewHolder(itemView.root) {var binding: ItemServiceBindinginit {binding = itemView}}
}

  和之前的区别就在于构造的时候增加了一个回调,并且在onCreateViewHolder()函数中就处理了服务Item的点击事件,而不是像之前一样回调到Activity中,在服务Item的点击事件中判断是否显示特性列表同时修改图标资源。最后再将接口回调到Activity中。

六、显示特性和属性

  现在要做的就是修改MainActivity中的代码,首先修改activity_main.xml中的代码,主要是修改之前的rv_service中的属性值,修改后如下所示:

    <androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_service"android:layout_width="match_parent"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/tv_device_info" />

主要就是让RecyclerView占满剩下的空间,避免内容过多导致无法滑动的情况,最后我们修改一下MainActivity中代码,如下所示:

class MainActivity : BaseActivity(), BleCallback, OperateCallback {...override fun onServicesDiscovered(services: List<BluetoothGattService>) {runOnUiThread {mServiceList.clear()mServiceList.addAll(services)mServiceAdapter ?: run {mServiceAdapter = ServiceAdapter(mServiceList, this@MainActivity)binding.rvService.apply {(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = falselayoutManager = LinearLayoutManager(this@MainActivity)adapter = mServiceAdapter//增加分隔线addItemDecoration(DividerItemDecoration(this@MainActivity, DividerItemDecoration.VERTICAL))}mServiceAdapter}mServiceAdapter!!.notifyDataSetChanged()}}/*** 属性操作*/override fun onPropertyOperate(characteristic: BluetoothGattCharacteristic, operateName: String) {showMsg(operateName)}
}

  修改的地方有三个,第一个就是MainActivity实现OnItemClickListener改成OperateCallback ,第二个是onServicesDiscovered()函数中,构建ServiceAdapter适配器中实现接口,然后添加分隔线,最后一个就是去掉之前onItemClick()函数改成onPropertyOperate()函数,运行一下看看效果。

在这里插入图片描述

七、源码

如果对你有所帮助的话,不妨 StarFork,山高水长,后会有期~

源码地址:GoodBle

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/79807.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在centos7上使用非编译方式安装ffmpeg

很多在centos7上安装ffmpeg的教程都需要使用编译方式的安装&#xff1b;编译时间较长而且需要配置; 后来搜索到可以通过加载rpm 源的方式实现快速便捷操作 第一种方式&#xff1a; 首先需要安装yum源&#xff1a; yum install epel-release yum install -y https://mirrors.…

GPU版PyTorch对应安装教程

一、正确安装符合自己电脑的对应GPU版本的PyTorch之前需要了解三个基本概念 算力、CUDA driver version、CUDA runtime version ①算力&#xff1a;需要先知道你的显卡&#xff0c;之后根据官网表格进行对应&#xff0c;得到算力 ②CUDA driver version&#xff1a;电脑上显卡…

python编写小程序有界面,python编写小程序的运行

大家好&#xff0c;小编为大家解答python编写小程序怎么看代码的的问题。很多人还不知道python编写小程序的运行&#xff0c;现在让我们一起来看看吧&#xff01; Python第一个简单的小游戏 temp input("请猜一猜姐姐的幸运数字是&#xff1a; ") guess int(temp) …

99%的人做效果图都会忽略的问题!为什么你的效果图没有亚洲面孔?

不知道各位设计师有没有发现一个问题&#xff0c;我们做了不少效果图&#xff0c;也积攒了很多素材&#xff0c;但是出现在我们效果图的人物几乎都是外国人&#xff01; 可能你会说是亚洲人的素材实在太少&#xff0c;但本质是对“人”不够重视&#xff0c;觉得随便“复制粘贴”…

数据结构 | 利用二叉堆实现优先级队列

目录 一、二叉堆的操作 二、二叉堆的实现 2.1 结构属性 2.2 堆的有序性 2.3 堆操作 队列有一个重要的变体&#xff0c;叫作优先级队列。和队列一样&#xff0c;优先级队列从头部移除元素&#xff0c;不过元素的逻辑顺序是由优先级决定的。优先级最高的元素在最前&#xff…

火爆全网,Python自动化测试Allure测试报告生成,最强总结...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Allure测试报告框…

JavaWeb(9)——前端综合案例3(悬停显示下拉列表)

一、实例需求 ⌛ 实现类似百度首页的“一个简单的鼠标悬停显示的下拉列表效果”。 二、代码实现 ☕ <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>.dropdown-cont…

外部链接跳转到vue项目传递参数实现单点登录

1、问题背景描述&#xff1a; 我有一个困扰了很久项目需求&#xff0c;前台门户用的MVC&#xff0c;前台登录之后需要能点击某个按钮就能进入后台vue开发的前端项目&#xff0c;不需要重新登录。这个需求中mvc项目相对于vue项目来说是外部链接&#xff0c;他要跳转到vue项目&a…

9、Kubernetes核心技术 - Volume

目录 一、概述 二、卷的类型 三、emptyDir 四、hostPath 五、NFS 5.1、master服务器上搭建nfs服务器 5.2、各个slave节点上安装nfs客户端 5.3、创建Pod 六、PV和PVC 6.1、PV 6.1.1、PV资源清单文件示例 6.1.2、PV属性说明 6.1.3、PV的状态 6.2、PVC 6.2.1、PVC资…

git开发过程中的使用

1、先创建本地分支&#xff0c;然后修改代码 2、本地提交 push 3、合并为主分支 回到master分支

Bigemap如何添加谷歌地图?

工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP 打开软件&#xff0c;要提示需要授权和添加地图&#xff0c;然后去点击选择地图这个按钮&#xff0c;列表中有个添加按钮点进去选择添加地图的方式。 第一种方式&#x…

UML—用例图的那些事

目录 背景: 1.用例图的发展史 过程: 1.用例图中的元素和关系 2.应用中的例子 总结&#xff1a; 背景: 1.用例图的发展史 用例图是一种常用的软件工程工具&#xff0c;用于描述系统的功能需求和用户与系统的交互。它在软件开发过程中起到了重要的作用&#xff0c;并且经历了…

【开源项目--稻草】Day06

【开源项目--稻草】Day06 1. 学生提问与解答功能2. 显示create.html2.1 HomeController中代码2.2 复用网页的标签导航条 3. 创建问题发布界面3.1 富文本编辑器 4.多选下列框5.动态加载所有标签和老师6. 发布问题的业务处理 1. 学生提问与解答功能 学生提问: 提问时指定标签和回…

VBA遍历Wrod所有表格每个单元格,单元格未尾两个回车替换

一、遍历 word中遍历所有表格的每个单元格。因为在单元格时会常出错。浪费了不少时间。 Sub a()Dim doc As Document, tb As Table, ce As cellDim rng As Range, p As ParagraphSet doc ActiveDocumentFor Each tb In doc.TablesFor Each ce In tb.Range.Cells 关键处就是这里…

Java中的Unsafe类详解

Java中的Unsafe类详解 1. Unsafe 概念2. Unsafe 构造及获取3. 功能和应用3.1 内存管理3.1.1 普通读写3.1.2 volatile 读写3.1.3 有序读写3.1.4 直接操作内存 3.2 CAS3.3 偏移量3.4 线程调度3.5 类加载3.6 内存屏障3.7 其他操作 4. 潜在风险和挑战5. 最佳实践5.1 使用案例&#…

QtAV for ubuntu16.04

下载ubuntu https://releases.ubuntu.com/16.04/ubuntu-16.04.7-desktop-amd64.iso 下载ffmpeg https://ffmpeg.org/download.html 下载QtAV https://github.com/wang-bin/QtAV/releases 更新 sudo apt update 安装库 sudo apt-get install libglu1-mesa-dev freeglut3-dev…

【算法系列 | 7】深入解析查找算法之—布隆过滤器

序言 心若有阳光&#xff0c;你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏&#xff0c;希望能帮助大家很好的了解算法。主要深入解析每个算法&#xff0c;从概念到示例。 我们一起努力&#xff0c;成为更好的自己&#xff01; 今天第3讲&#xff0c;讲一…

【数据结构】链表(一)

链表&#xff08;一&#xff09; 文章目录 链表&#xff08;一&#xff09;01 引入02 概念及结构03 单向不带头不循环链表实现3.1 创建节点类型3.2 简易创建一个链表3.3 遍历链表每个节点3.4 获取链表长度3.5 查找是否包含关键字key是否在单链表当中3.6 头插法3.7 尾插法3.8 任…

MySQL 重置root 密码

5.7 版本 首先要把服务mysql57 关闭 net stop MySQL57 在安装的mysql57的程序的bin中 运行cmd&#xff08;管理员运行&#xff09; mysqld --defaults-file‘mysql存放数据的位置\my.ini’ --skip-grant-tables 上图 错误 注意&#xff1a;如果遇到mysqld: Can’t change dir…

【从零学习python 】02. 开发工具介绍

文章目录 编写Python代码一、常见的代码编辑工具二、运行Python程序三、Pycharm的下载和安装PyCharm的主要功能区域进阶案例 编写Python代码 根据我们之前介绍的知识&#xff0c;我们知道&#xff0c;所谓代码其实就是将一段普通文本按照一定的规范编写&#xff0c;然后交给电…