前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Harmony Ble蓝牙App(三)特性和属性

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

作者头像
晨曦_LLW
发布2023-12-05 14:25:51
3390
发布2023-12-05 14:25:51
举报
文章被收录于专栏:Android、鸿蒙开发

前言

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

正文

  本篇要做的是显示服务下的特性,首先我们了解一下特性的基本知识。在蓝牙低功耗(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中增加一个函数,代码如下所示:

代码语言:javascript
复制
	/**
     * 获取属性
     */
    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,代码如下所示:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<Text
    xmlns: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类,代码如下所示:

代码语言:javascript
复制
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;
    }

    @Override
    public int getCount() {
        return propertyList == null ? 0 : propertyList.size();
    }

    @Override
    public Object getItem(int position) {
        if (propertyList != null && position >= 0 && position < propertyList.size()) {
            return propertyList.get(position);
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public 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()函数,代码有点多,如下所示:

代码语言:javascript
复制
    /**
     * 获取特性名称
     * @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,代码如下所示:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<DependentLayout
    xmlns: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">

    <Text
        ohos:id="$+id:tx_character_name"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:background_element="$color:black"
        ohos:text="服务"
        ohos:text_size="16fp"/>

    <Text
        ohos: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"/>

    <Text
        ohos: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"/>

    <Text
        ohos: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"/>

    <ListContainer
        ohos: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接口,代码如下所示:

代码语言:javascript
复制
public interface OperateCallback {

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

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

代码语言:javascript
复制
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;
    }

    @Override
    public int getCount() {
        return characteristicList == null ? 0 : characteristicList.size();
    }

    @Override
    public Object getItem(int position) {
        if (characteristicList != null && position >= 0 && position < characteristicList.size()) {
            return characteristicList.get(position);
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public 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,代码如下所示:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns: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">

    <DependentLayout
        xmlns: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">

        <Text
            ohos:id="$+id:tx_service_name"
            ohos:height="match_content"
            ohos:width="match_content"
            ohos:background_element="$color:black"
            ohos:text="服务"
            ohos:text_size="16fp"/>

        <Text
            ohos: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"/>

        <Text
            ohos: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"/>

        <Text
            ohos: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"/>

        <Image
            ohos: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>

    <ListContainer
        ohos: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,代码如下所示:

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<vector
    xmlns:ohos="http://schemas.android.com/apk/res/android"
    ohos:height="24vp"
    ohos:tint="#000000"
    ohos:viewportHeight="24"
    ohos:viewportWidth="24"
    ohos:width="24vp">

    <path
        ohos:fillColor="#000000"
        ohos:pathData="M10,17l5,-5 -5,-5v10z"/>
</vector>

还有一个ic_down_24.xml

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<vector
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="24vp"
    ohos:width="24vp"
    ohos:tint="#000000"
    ohos:viewportHeight="24"
    ohos:viewportWidth="24">

    <path
        ohos:fillColor="#000000"
        ohos:pathData="M7,10l5,5 5,-5z"/>
</vector>

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

代码语言:javascript
复制
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;
    }

    @Override
    public int getCount() {
        return serviceList == null ? 0 : serviceList.size();
    }

    @Override
    public Object getItem(int position) {
        if (serviceList != null && position >= 0 && position < serviceList.size()) {
            return serviceList.get(position);
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public 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()方法。这个方法在下一篇文章中会用到。

代码语言:javascript
复制
    @Override
    public void onPropertyOperate(GattCharacteristic characteristic, String operateName) {

    }

运行一下看看效果。

六、源码

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

源码地址:HarmonyBle-Java

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • 一、获取属性列表
      • 二、属性提供者
        • 三、获取特性名称
          • 四、特性提供者
            • 五、加载特性
              • 六、源码
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档