Harmony 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中增加一个函数,代码如下所示:

	/*** 获取属性*/public static List<String> getProperties(int property) {List<String> properties = new ArrayList<>();for (int i = 0; i < 8; i++) {switch (property & (1 << i)) {case 0x01:properties.add("Broadcast");break;case 0x02:properties.add("Read");break;case 0x04:properties.add("Write No Response");break;case 0x08:properties.add("Write");break;case 0x10:properties.add("Notify");break;case 0x20:properties.add("Indicate");break;case 0x40:properties.add("Authenticated Signed Writes");break;case 0x80:properties.add("Extended Properties");break;}}return properties;}

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

二、属性提供者

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

<?xml version="1.0" encoding="utf-8"?>
<Textxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:id="$+id:tx_property"ohos:height="match_content"ohos:width="match_content"ohos:end_margin="8vp"ohos:text="property"ohos:text_color="$color:blue"ohos:text_size="14fp"/>

  因为是String类型,所以我们就直接用一个Text显示即可,下面我们写提供者,在provider包下新建一个PropertyProvider类,代码如下所示:

public class PropertyProvider extends BaseItemProvider {private final List<String> propertyList;private final AbilitySlice slice;public PropertyProvider(List<String> list, AbilitySlice slice) {this.propertyList = list;this.slice = slice;}@Overridepublic int getCount() {return propertyList == null ? 0 : propertyList.size();}@Overridepublic Object getItem(int position) {if (propertyList != null && position >= 0 && position < propertyList.size()) {return propertyList.get(position);}return null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic Component getComponent(int position, Component component, ComponentContainer componentContainer) {final Component cpt;ServiceHolder holder;if (component == null) {cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_property, null, false);holder = new ServiceHolder(cpt);//将获取到的子组件信息绑定到列表项的实例中cpt.setTag(holder);} else {cpt = component;// 从缓存中获取到列表项实例后,直接使用绑定的子组件信息进行数据填充。holder = (ServiceHolder) cpt.getTag();}holder.txProperty.setText(propertyList.get(position));return cpt;}/*** 用于保存列表项的子组件信息*/public static class ServiceHolder {Text txProperty;public ServiceHolder(Component component) {txProperty = (Text) component.findComponentById(ResourceTable.Id_tx_property);}}
}

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

三、获取特性名称

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

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

  同修改一下原有的getServiceUUID()getShortUUID(),只改名字而已,之前命名有点不太严谨,记得改一下使用的地方,如下图所示,在ServiceProvider

在这里插入图片描述

四、特性提供者

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

<?xml version="1.0" encoding="utf-8"?>
<DependentLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_content"ohos:width="match_parent"ohos:background_element="#FFFFFF"ohos:bottom_margin="2vp"ohos:bottom_padding="8vp"ohos:end_padding="16vp"ohos:start_padding="16vp"ohos:top_padding="8vp"><Textohos:id="$+id:tx_character_name"ohos:height="match_content"ohos:width="match_content"ohos:background_element="$color:black"ohos:text="服务"ohos:text_size="16fp"/><Textohos:id="$+id:tx_uuid_title"ohos:height="match_content"ohos:width="match_content"ohos:below="$id:tx_character_name"ohos:text="UUID:"ohos:text_color="$color:gray"ohos:text_size="16fp"ohos:top_margin="2vp"/><Textohos:id="$+id:tx_uuid"ohos:height="match_content"ohos:width="match_content"ohos:background_element="$color:black"ohos:below="$id:tx_character_name"ohos:end_of="$id:tx_uuid_title"ohos:text="UUID"ohos:text_size="16fp"ohos:top_margin="2vp"/><Textohos:id="$+id:tx_property_title"ohos:height="match_content"ohos:width="match_content"ohos:below="$id:tx_uuid_title"ohos:text="Properties:"ohos:text_color="$color:gray"ohos:text_size="16fp"ohos:top_margin="2vp"/><ListContainerohos:id="$+id:lc_property"ohos:height="match_content"ohos:width="match_parent"ohos:orientation="horizontal"ohos:align_bottom="$id:tx_property_title"ohos:align_top="$id:tx_property_title"ohos:end_of="$id:tx_property_title"/></DependentLayout>

  这个布局中唯一说明的一点就是ListContainerorientation="horizontal"属性,这表示列表是横向,默认是纵向的。这里显示特性的名称和UUIID,同时加载属性列表,然后写适配器,因为需要操作属性的缘故,这些写一个接口,在provider包下新建一个OperateCallback接口,代码如下所示:

public interface OperateCallback {/*** 属性操作*/void onPropertyOperate(GattCharacteristic characteristic, String operateName);
}

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

public class CharacteristicProvider extends BaseItemProvider {private final List<GattCharacteristic> characteristicList;private final AbilitySlice slice;private final OperateCallback operateCallback;public CharacteristicProvider(List<GattCharacteristic> list, AbilitySlice slice, OperateCallback operateCallback) {this.characteristicList = list;this.slice = slice;this.operateCallback = operateCallback;}@Overridepublic int getCount() {return characteristicList == null ? 0 : characteristicList.size();}@Overridepublic Object getItem(int position) {if (characteristicList != null && position >= 0 && position < characteristicList.size()) {return characteristicList.get(position);}return null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic Component getComponent(int position, Component component, ComponentContainer componentContainer) {final Component cpt;ServiceHolder holder;GattCharacteristic characteristic = characteristicList.get(position);if (component == null) {cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_characteristic, null, false);holder = new ServiceHolder(cpt);//将获取到的子组件信息绑定到列表项的实例中cpt.setTag(holder);} else {cpt = component;// 从缓存中获取到列表项实例后,直接使用绑定的子组件信息进行数据填充。holder = (ServiceHolder) cpt.getTag();}holder.txCharacterName.setText(BleUtils.getCharacteristicsName(characteristic.getUuid()));holder.txUuid.setText(BleUtils.getShortUUID(characteristic.getUuid()));List<String> properties = BleUtils.getProperties(characteristic.getProperties());//加载属性holder.lcProperty.setItemProvider(new PropertyProvider(properties, slice));//属性列表点击holder.lcProperty.setItemClickedListener((listContainer, component1, propertyPosition, l) -> {if (operateCallback != null) {//属性操作回调operateCallback.onPropertyOperate(characteristic, properties.get(propertyPosition));}});return cpt;}/*** 用于保存列表项的子组件信息*/public static class ServiceHolder {Text txCharacterName;Text txUuid;ListContainer lcProperty;public ServiceHolder(Component component) {txCharacterName = (Text) component.findComponentById(ResourceTable.Id_tx_character_name);txUuid = (Text) component.findComponentById(ResourceTable.Id_tx_uuid);lcProperty = (ListContainer) component.findComponentById(ResourceTable.Id_lc_property);}}
}

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

五、加载特性

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

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="match_content"ohos:width="match_parent"ohos:background_element="#FFFFFF"ohos:bottom_margin="2vp"ohos:orientation="vertical"><DependentLayoutxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:id="$+id:item_service"ohos:height="match_content"ohos:width="match_parent"ohos:background_element="#FFFFFF"ohos:bottom_padding="8vp"ohos:end_padding="16vp"ohos:start_padding="16vp"ohos:top_padding="8vp"><Textohos:id="$+id:tx_service_name"ohos:height="match_content"ohos:width="match_content"ohos:background_element="$color:black"ohos:text="服务"ohos:text_size="16fp"/><Textohos:id="$+id:tx_uuid_title"ohos:height="match_content"ohos:width="match_content"ohos:below="$id:tx_service_name"ohos:text="UUID:"ohos:text_color="$color:gray"ohos:text_size="16fp"ohos:top_margin="2vp"/><Textohos:id="$+id:tx_uuid"ohos:height="match_content"ohos:width="match_content"ohos:background_element="$color:black"ohos:below="$id:tx_service_name"ohos:end_of="$id:tx_uuid_title"ohos:text="UUID"ohos:text_size="16fp"ohos:top_margin="2vp"/><Textohos:id="$+id:tx_service_info"ohos:height="match_content"ohos:width="match_content"ohos:below="$id:tx_uuid_title"ohos:text="PRIMARY SERVICE"ohos:text_color="$color:gray"ohos:text_size="16fp"ohos:top_margin="2vp"/><Imageohos:id="$+id:iv_state"ohos:height="24vp"ohos:width="24vp"ohos:align_parent_end="true"ohos:end_margin="16vp"ohos:background_element="$graphic:ic_right_24"ohos:vertical_center="true"/></DependentLayout><ListContainerohos:id="$+id:lc_characteristics"ohos:height="match_content"ohos:width="match_parent"ohos:start_padding="16vp"ohos:visibility="hide"/>
</DirectionalLayout>

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

<?xml version="1.0" encoding="UTF-8"?>
<vectorxmlns:ohos="http://schemas.android.com/apk/res/android"ohos:height="24vp"ohos:tint="#000000"ohos:viewportHeight="24"ohos:viewportWidth="24"ohos:width="24vp"><pathohos:fillColor="#000000"ohos:pathData="M10,17l5,-5 -5,-5v10z"/>
</vector>

还有一个ic_down_24.xml

<?xml version="1.0" encoding="utf-8"?>
<vectorxmlns:ohos="http://schemas.huawei.com/res/ohos"ohos:height="24vp"ohos:width="24vp"ohos:tint="#000000"ohos:viewportHeight="24"ohos:viewportWidth="24"><pathohos:fillColor="#000000"ohos:pathData="M7,10l5,5 5,-5z"/>
</vector>

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

public class ServiceProvider extends BaseItemProvider {private final List<GattService> serviceList;private final AbilitySlice slice;private OperateCallback operateCallback;public void setOperateCallback(OperateCallback operateCallback) {this.operateCallback = operateCallback;}public ServiceProvider(List<GattService> list, AbilitySlice slice) {this.serviceList = list;this.slice = slice;}@Overridepublic int getCount() {return serviceList == null ? 0 : serviceList.size();}@Overridepublic Object getItem(int position) {if (serviceList != null && position >= 0 && position < serviceList.size()) {return serviceList.get(position);}return null;}@Overridepublic long getItemId(int position) {return position;}@Overridepublic Component getComponent(int position, Component component, ComponentContainer componentContainer) {final Component cpt;ServiceHolder holder;GattService service = serviceList.get(position);if (component == null) {cpt = LayoutScatter.getInstance(slice).parse(ResourceTable.Layout_item_service, null, false);holder = new ServiceHolder(cpt);//将获取到的子组件信息绑定到列表项的实例中cpt.setTag(holder);} else {cpt = component;// 从缓存中获取到列表项实例后,直接使用绑定的子组件信息进行数据填充。holder = (ServiceHolder) cpt.getTag();}holder.itemService.setClickedListener(component1 -> {boolean isShow = holder.lcCharacteristics.getVisibility() == Component.VISIBLE;//显示特性列表holder.lcCharacteristics.setVisibility(isShow ? Component.HIDE : Component.VISIBLE);//更换图标VectorElement vectorElement = new VectorElement(slice.getContext(), isShow ? ResourceTable.Graphic_ic_right_24 : ResourceTable.Graphic_ic_down_24);holder.ivState.setBackground(vectorElement);//刷新Item高度,这个很重要,不加会造成内容覆盖。notifyDataSetItemChanged(position);});//加载特性 设置属性回调holder.lcCharacteristics.setItemProvider(new CharacteristicProvider(service.getCharacteristics(), slice, operateCallback));holder.txServiceName.setText(BleUtils.getServiceName(service.getUuid()));holder.txUuid.setText(BleUtils.getShortUUID(service.getUuid()));return cpt;}/*** 用于保存列表项的子组件信息*/public static class ServiceHolder {DependentLayout itemService;Text txServiceName;Text txUuid;Image ivState;ListContainer lcCharacteristics;public ServiceHolder(Component component) {itemService = (DependentLayout) component.findComponentById(ResourceTable.Id_item_service);txServiceName = (Text) component.findComponentById(ResourceTable.Id_tx_service_name);txUuid = (Text) component.findComponentById(ResourceTable.Id_tx_uuid);ivState = (Image) component.findComponentById(ResourceTable.Id_iv_state);lcCharacteristics = (ListContainer) component.findComponentById(ResourceTable.Id_lc_characteristics);}}
}

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

  另外还需要注意一点,那就是Image我在xml中是设置的背景,而不是图片资源,因为在java代码中无法设置矢量图的资源,所以我就改成使用背景资源,需要注意的是背景资源要设置Image具体的大小,否则不会显示,在Java代码中通过VectorElement来加载矢量图资源,然后设置背景。同时notifyDataSetItemChanged(position)这样代码也很重要,因为我们的服务Item实际上有两部分内容,服务本身内容和特性列表内容,默认情况下显示服务内容,当点击服务Item时显示特性列表内容,此时如果你按照Android的习惯去搞,你就会发现,Item展开的内容会被其他Item遮挡,所以我们需要加上这样一行代码,让Item进行刷新,这样就不会被其他Item遮挡了。

  最后我们在MainAbilitySlice中添加serviceProvider.setOperateCallback(this);,然后再实现接口

在这里插入图片描述

重写里面的onPropertyOperate()方法。这个方法在下一篇文章中会用到。

    @Overridepublic void onPropertyOperate(GattCharacteristic characteristic, String operateName) {}

运行一下看看效果。

在这里插入图片描述

六、源码

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

源码地址:HarmonyBle-Java

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

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

相关文章

汽车电子 -- 毫米波雷达

参看&#xff1a;自动驾驶感知——毫米波雷达 一、雷达分类 按信号形式&#xff1a;脉冲体制、连续波体制等按测量参数&#xff1a;测速雷达、成像雷达等按扫描方式&#xff1a;机械扫描、电子扫描&#xff08;数字波束形成、相控阵&#xff09;等按工作方式&#xff1a;多普…

容器与集群——通过deployment 创建pod以及Java Web应用的容器化发布

## 一、通过deployment 创建pod 1.1 编写yaml文件 1.2 安装pod 创建kubectl create -f dp-nginx.yaml 查看Deployment信息 1.3 查看相关信息 查看pod信息kubecel get pods 查看rs信息 二、Java Web应用的容器化发布 1. 环境准备 部署K8s集群并启动。 为了与其他pod…

Educational Codeforces Round 159 (Rated for Div. 2)(B 二分贪心 Cgcd D二分+前缀和 E字典树)

A - Binary Imbalance 有只要在01之间插入就能制造无限个0&#xff0c;没有0就统计0 1个数即可 #include<bits/stdc.h> using namespace std; const int N 110010,mod998244353; #define int long long typedef long long LL; typedef pair<int, int> PII; const…

阿里内部自动化测试教程:python+pytest接口自动化-HTTP协议基础

HTTP协议简介 HTTP 即 HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09;&#xff0c;是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。 设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。HTTP 协议在 OSI 模型…

Java中线程池相关的七个参数

在Java中&#xff0c;线程池的七个参数是指线程池的相关配置参数&#xff0c;用来控制线程池的行为和性能。这些参数包括&#xff1a; 1. 核心线程数&#xff08;corePoolSize&#xff09;&#xff1a;线程池中保持的最小线程数&#xff0c;即使线程处于空闲状态&#xff0c;也…

Kubernetes学习笔记-Part.07 Harbor搭建

目录 Part.01 Kubernets与docker Part.02 Docker版本 Part.03 Kubernetes原理 Part.04 资源规划 Part.05 基础环境准备 Part.06 Docker安装 Part.07 Harbor搭建 Part.08 K8s环境安装 Part.09 K8s集群构建 Part.10 容器回退 第七章 Harbor搭建 Docker-Compose是用来管理容器的…

Python+OpenCV实现最强自动扫雷

文章目录 准备实现思路窗体截取雷块分割雷块识别扫雷算法实现关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 用…

Linux 基本语句_14_信号灯实验

原理&#xff1a; Send进程通过建立共享内存区域&#xff0c;并向其中写入数据&#xff0c;Recive通过与共享内存连接读取其中的数据。 但是如果进程进行读取操作的时候其他进程再次写入会产生数据丢失&#xff0c;产生竞态&#xff0c;为了确保在某段时间内只有一个操作&…

用python写一个简单的爬虫

爬虫是一种自动化程序&#xff0c;用于从互联网上获取数据。它能够模拟人类浏览网页的行为&#xff0c;访问网页并提取所需的信息。爬虫在很多领域都有广泛的应用&#xff0c;例如数据采集、信息监控、搜索引擎索引等。 下面是一个使用Python编写的简单爬虫示例&#xff1a; …

新手零基础学习彩铅画,彩铅快速入门教程合集

一、教程描述 画画是很美好的一件事情&#xff0c;你可以把你想到的&#xff0c;或者看到的都画下来&#xff0c;照相机可以拍下任何你看到的&#xff0c;但是你想到的任何事物&#xff0c;只能通过绘画的方式来表达。本套教程是非常不错的&#xff0c;彩铅的小视频教程&#…

[Mac软件]HitPaw Video Converter 功能强大的视频格式转换编辑软件激活版

软件介绍&#xff1a; 以令人难以置信的速度将无损视频和音乐转换为1000多种格式&#xff1a;MP4、MOV、AVI、VOB、MKV等。不仅适用于普通编解码器&#xff0c;也适用于高级VP9、ProRes和Opus编码器。这解决了您不支持格式的所有问题&#xff0c;并允许您在任何平台和设备上播…

仅 CSS 阅读进度条

为了构建一个阅读进度条&#xff0c;即显示用户向下滚动时阅读文章的进度&#xff0c;很难不考虑 JavaScript。但是&#xff0c;事实证明&#xff0c;您也可以使用纯 CSS 构建阅读进度条。 从本质上讲&#xff0c;一个名为 animation-timeline 的新实验性 CSS 属性可以让你指定…

Pytest 使用及调用方法

使用python -m pytest调用pytest 2.0版本新增 你可以在命令行中通过Python编译器来调用Pytest执行测试: python -m pytest [...] 通过python调用会将当前目录也添加到sys.path中,除此之外,这几乎等同于命令行直接调用pytest [...]。 可能出现的执行退出code 执行pytest可能…

S32K116新建工程Debug可以运行,冷启动无法运行问题分析

S32K116使用IAR建立工程后&#xff0c;软件debug可以运行&#xff0c;断电冷启动无法运行。 这种现象基本上都是RAM未初始化导致&#xff0c;由于Debug时&#xff0c;调试器会自动初始化芯片&#xff0c;很多问题都不会暴露处理。 大家可以开一下Startup的汇编文件&#xff0c;…

Rpg游戏地形生成

rpg游戏中的地形一般使用高度图的形式来绘制。写了几个随机生成高度图的算法。 最常见的是基于分形算法生成高度图&#xff0c;网上有很多资料&#xff0c;这里不再介绍。 一种生成断层效果高度图的算法 //!生成断层效果的高度图 void TerrainData::FillFaultSurface(float …

全网最新最全的自动化测试教程:python+pytest接口自动化-requests发送post请求

简介 在HTTP协议中&#xff0c;与get请求把请求参数直接放在url中不同&#xff0c;post请求的请求数据需通过消息主体(request body)中传递。 且协议中并没有规定post请求的请求数据必须使用什么样的编码方式&#xff0c;所以其请求数据可以有不同的编码方式&#xff0c;服务…

初试占比7成!只考一门数据结构+学硕复录比1:1的神仙学校,大连交通大学考情分析

大连工业大学 考研难度&#xff08;☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、24专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1014字&#xff0c;预计阅读&#xff1a;3分钟 2023考情概况 大连工业…

SpringCloud笔记

一、SpringCloud初阶篇 1、从面试题开始 1.1什么是微服务&#xff1f; 1.2微服务之间是如何独立通讯的&#xff1f; 1.3SpringCloud和Dubbo有哪些区别&#xff1f; 1.4通信机制&#xff1a;Dubbo是通过RPC远程过程调用&#xff0c;微服务Cloud是基于rest调用 1.5SpringBo…

【vue】vue-slick-carousel插件,实现横向滚动列表手动左右滚动(也可设置为自动滚动)

需求&#xff1a;图片列表横向滚动的时候&#xff0c;隐藏原始滚动条&#xff0c;通过左右箭头控制滚动条往左右按一定的步长移动。 el-carousel走马灯一滚动就是一屏&#xff0c;不适合我的需求 在npm官网搜vue-slick-carousel&#xff0c;查看更详细的配置 vue-slick-caro…

GO基础之运算符

运算符 Go 语言内置的运算符有&#xff1a; 1.算术运算符 2.关系运算符 3.逻辑运算符 4.位运算符 5.赋值运算符 算术运算符 注意&#xff1a; &#xff08;自增&#xff09;和–&#xff08;自减&#xff09;在Go语言中是单独的语句&#xff0c;并不是运算符。 关系运算符 …