DevicePicker集成HarmonyOS APP

举报
Jack20 发表于 2026/06/19 20:51:19 2026/06/19
【摘要】 在分布式应用中,经常需要让用户选择目标设备——比如视频通话选择哪个摄像头、音乐播放选择哪个音箱、文件传输选择哪个设备……如果每次都要自己实现设备列表UI、设备过滤、设备选择逻辑,开发成本会很高。HarmonyOS提供了DevicePicker组件,一行代码就能实现专业的设备选择功能。这篇文章带你深入理解DevicePicker的使用与定制。 一、背景与动机:为什么需要DevicePicker...

在分布式应用中,经常需要让用户选择目标设备——比如视频通话选择哪个摄像头、音乐播放选择哪个音箱、文件传输选择哪个设备……如果每次都要自己实现设备列表UI、设备过滤、设备选择逻辑,开发成本会很高。HarmonyOS提供了DevicePicker组件,一行代码就能实现专业的设备选择功能。这篇文章带你深入理解DevicePicker的使用与定制。

一、背景与动机:为什么需要DevicePicker?

1.1 设备选择的复杂性

在分布式场景下,设备选择面临诸多挑战:

设备多样性:不同设备类型、不同能力、不同状态,需要根据场景过滤合适的设备。

UI一致性:每个应用都需要设备选择功能,如果各自实现,UI风格不统一,用户体验差。

交互复杂性:设备选择不仅是点击选中,还涉及设备状态显示、能力展示、搜索过滤等复杂交互。

开发成本高:从设备发现、设备过滤、UI渲染、状态更新到选择回调,完整实现需要大量代码。

1.2 DevicePicker的价值

DevicePicker是HarmonyOS提供的系统级设备选择组件,它封装了设备选择的完整流程:
图片.png

DevicePicker的核心价值

特性 说明 开发价值
系统级UI 统一的设备选择界面 无需自己实现UI
智能过滤 根据场景自动过滤设备 简化过滤逻辑
状态展示 实时显示设备状态 无需自己监听状态
能力展示 展示设备能力信息 无需自己查询能力
回调封装 封装选择结果回调 简化交互逻辑

二、核心原理:DevicePicker工作机制

2.1 组件架构

2.1.1 组件结构

// DevicePicker组件配置
interface DevicePickerConfig {
  // 基础配置
  title?: string;                    // 标题
  subtitle?: string;                 // 副标题
  mode?: DevicePickerMode;           // 选择模式
  
  // 设备过滤
  filter?: DeviceFilter;             // 设备过滤器
  excludeDevices?: string[];         // 排除的设备列表
  
  // UI配置
  showDeviceType?: boolean;          // 是否显示设备类型
  showDeviceState?: boolean;         // 是否显示设备状态
  showDeviceCapability?: boolean;    // 是否显示设备能力
  showSearchBar?: boolean;           // 是否显示搜索栏
  
  // 交互配置
  multiSelect?: boolean;             // 是否多选
  maxSelectCount?: number;           // 最大选择数量
  confirmText?: string;              // 确认按钮文本
  cancelText?: string;               // 取消按钮文本
  
  // 回调配置
  onDeviceSelected?: (device: DeviceInfo) => void;      // 单选回调
  onDevicesSelected?: (devices: DeviceInfo[]) => void;  // 多选回调
  onCancelled?: () => void;                             // 取消回调
}

// 选择模式
enum DevicePickerMode {
  SINGLE = 'single',         // 单选模式
  MULTI = 'multi',           // 多选模式
  CASCADE = 'cascade'        // 级联选择模式
}

// 设备过滤器
interface DeviceFilter {
  deviceTypes?: DeviceType[];        // 设备类型过滤
  capabilities?: string[];           // 能力过滤
  states?: DeviceState[];            // 状态过滤
  trustLevels?: TrustLevel[];        // 信任等级过滤
  customFilter?: (device: DeviceInfo) => boolean;  // 自定义过滤
}

2.1.2 组件流程

sequenceDiagram
    participant App as 应用
    participant Picker as DevicePicker
    participant Filter as 过滤器
    participant UI as UI组件
    participant User as 用户
    
    Note over App: 1. 创建DevicePicker
    App->>Picker: create(config)
    
    Note over Picker: 2. 初始化
    Picker->>Picker: loadDevices()
    Picker->>Filter: applyFilter(devices)
    Filter-->>Picker: filteredDevices
    
    Note over Picker: 3. 渲染UI
    Picker->>UI: render(filteredDevices)
    
    Note over User: 4. 用户交互
    User->>UI: 选择设备
    UI->>Picker: handleSelect(device)
    
    alt 单选模式
        Picker->>Picker: validateSelection(device)
        Picker->>App: onDeviceSelected(device)
    else 多选模式
        User->>UI: 继续选择
        UI->>Picker: handleSelect(device2)
        User->>UI: 点击确认
        UI->>Picker: confirmSelection()
        Picker->>App: onDevicesSelected(devices)
    end
    
    classDef primary fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
    classDef warning fill:#F5A623,stroke:#C17A00,stroke-width:2px,color:#fff
    classDef info fill:#7ED321,stroke:#5BA315,stroke-width:2px,color:#fff
    
    class App,User primary
    class Picker,Filter warning
    class UI info

2.2 设备过滤机制

2.2.1 过滤流程

graph TD
    Start[开始过滤] --> Load[加载设备列表]
    Load --> Type{设备类型过滤?}
    
    Type -->|| FilterType[按类型过滤]
    Type -->|| Cap
    FilterType --> Cap
    
    Cap{能力过滤?}
    Cap -->|| FilterCap[按能力过滤]
    Cap -->|| State
    FilterCap --> State
    
    State{状态过滤?}
    State -->|| FilterState[按状态过滤]
    State -->|| Trust
    FilterState --> Trust
    
    Trust{信任等级过滤?}
    Trust -->|| FilterTrust[按信任等级过滤]
    Trust -->|| Custom
    FilterTrust --> Custom
    
    Custom{自定义过滤?}
    Custom -->|| FilterCustom[执行自定义过滤]
    Custom -->|| Sort
    FilterCustom --> Sort
    
    Sort[设备排序]
    Sort --> Result[返回过滤结果]
    
    classDef primary fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
    classDef warning fill:#F5A623,stroke:#C17A00,stroke-width:2px,color:#fff
    classDef info fill:#7ED321,stroke:#5BA315,stroke-width:2px,color:#fff
    
    class Start,Load,Sort,Result primary
    class Type,Cap,State,Trust,Custom warning
    class FilterType,FilterCap,FilterState,FilterTrust,FilterCustom info

2.2.2 过滤实现

/**
 * 设备过滤器实现
 */
class DeviceFilterImpl {
  /**
   * 应用过滤器
   */
  applyFilter(
    devices: DeviceInfo[],
    filter: DeviceFilter
  ): DeviceInfo[] {
    let filtered = [...devices];
    
    // 设备类型过滤
    if (filter.deviceTypes && filter.deviceTypes.length > 0) {
      filtered = filtered.filter(device => 
        filter.deviceTypes!.includes(device.deviceType)
      );
    }
    
    // 能力过滤
    if (filter.capabilities && filter.capabilities.length > 0) {
      filtered = filtered.filter(device => 
        this.hasCapabilities(device, filter.capabilities!)
      );
    }
    
    // 状态过滤
    if (filter.states && filter.states.length > 0) {
      filtered = filtered.filter(device => 
        filter.states!.includes(device.state)
      );
    }
    
    // 信任等级过滤
    if (filter.trustLevels && filter.trustLevels.length > 0) {
      filtered = filtered.filter(device => 
        filter.trustLevels!.includes(device.trustLevel)
      );
    }
    
    // 自定义过滤
    if (filter.customFilter) {
      filtered = filtered.filter(filter.customFilter);
    }
    
    return filtered;
  }
  
  /**
   * 检查设备是否有指定能力
   */
  private hasCapabilities(
    device: DeviceInfo,
    capabilities: string[]
  ): boolean {
    for (const cap of capabilities) {
      if (!device.capabilities.includes(cap)) {
        return false;
      }
    }
    return true;
  }
}

2.3 设备排序机制

/**
 * 设备排序策略
 */
enum DeviceSortStrategy {
  RECENTLY_USED = 'recently_used',     // 最近使用
  ALPHABETICAL = 'alphabetical',       // 字母顺序
  DEVICE_TYPE = 'device_type',         // 设备类型
  TRUST_LEVEL = 'trust_level',         // 信任等级
  STATE_PRIORITY = 'state_priority'    // 状态优先级
}

/**
 * 设备排序器
 */
class DeviceSorter {
  /**
   * 排序设备列表
   */
  sort(
    devices: DeviceInfo[],
    strategy: DeviceSortStrategy
  ): DeviceInfo[] {
    const sorted = [...devices];
    
    switch (strategy) {
      case DeviceSortStrategy.RECENTLY_USED:
        sorted.sort((a, b) => b.lastUsedTime - a.lastUsedTime);
        break;
        
      case DeviceSortStrategy.ALPHABETICAL:
        sorted.sort((a, b) => a.deviceName.localeCompare(b.deviceName));
        break;
        
      case DeviceSortStrategy.DEVICE_TYPE:
        sorted.sort((a, b) => a.deviceType - b.deviceType);
        break;
        
      case DeviceSortStrategy.TRUST_LEVEL:
        sorted.sort((a, b) => b.trustLevel - a.trustLevel);
        break;
        
      case DeviceSortStrategy.STATE_PRIORITY:
        sorted.sort((a, b) => 
          this.getStatePriority(b.state) - this.getStatePriority(a.state)
        );
        break;
    }
    
    return sorted;
  }
  
  /**
   * 获取状态优先级
   */
  private getStatePriority(state: DeviceState): number {
    const priorityMap = {
      [DeviceState.READY]: 5,
      [DeviceState.ONLINE]: 4,
      [DeviceState.IDLE]: 3,
      [DeviceState.BUSY]: 2,
      [DeviceState.SLEEP]: 1,
      [DeviceState.OFFLINE]: 0
    };
    
    return priorityMap[state] || 0;
  }
}

三、代码实战:DevicePicker使用

3.1 基础使用

import { DevicePicker, DevicePickerDialog } from '@ohos.distributedHardware.devicePicker';

/**
 * DevicePicker基础使用示例
 */
@Component
export struct DevicePickerBasicExample {
  @State selectedDevice: DeviceInfo | null = null;
  
  build() {
    Column() {
      // 显示已选设备
      if (this.selectedDevice) {
        Row() {
          Text(`已选设备: ${this.selectedDevice.deviceName}`)
            .fontSize(16)
          
          Blank()
          
          Button('重新选择')
            .onClick(() => {
              this.showDevicePicker();
            })
        }
        .width('100%')
        .padding(16)
      } else {
        Button('选择设备')
          .width('80%')
          .height(48)
          .onClick(() => {
            this.showDevicePicker();
          })
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
  
  /**
   * 显示设备选择器
   */
  private showDevicePicker(): void {
    // 创建设备选择器对话框
    DevicePickerDialog.show({
      title: '选择设备',
      subtitle: '请选择目标设备',
      mode: DevicePickerMode.SINGLE,
      
      // 设备过滤
      filter: {
        states: [DeviceState.READY, DeviceState.ONLINE]
      },
      
      // UI配置
      showDeviceType: true,
      showDeviceState: true,
      showSearchBar: true,
      
      // 选择回调
      onDeviceSelected: (device: DeviceInfo) => {
        console.info(`选中设备: ${device.deviceName}`);
        this.selectedDevice = device;
      },
      
      // 取消回调
      onCancelled: () => {
        console.info('取消选择');
      }
    });
  }
}

3.2 场景化使用

/**
 * DevicePicker场景化封装
 */
export class SceneDevicePicker {
  /**
   * 选择摄像头设备
   */
  static async pickCamera(): Promise<DeviceInfo | null> {
    return new Promise(resolve => {
      DevicePickerDialog.show({
        title: '选择摄像头',
        subtitle: '选择用于拍照或录像的摄像头',
        
        filter: {
          capabilities: ['sensor.camera.front', 'sensor.camera.back'],
          states: [DeviceState.READY]
        },
        
        onDeviceSelected: (device) => {
          resolve(device);
        },
        
        onCancelled: () => {
          resolve(null);
        }
      });
    });
  }
  
  /**
   * 选择显示设备(投屏)
   */
  static async pickDisplay(): Promise<DeviceInfo | null> {
    return new Promise(resolve => {
      DevicePickerDialog.show({
        title: '选择显示设备',
        subtitle: '选择投屏目标设备',
        
        filter: {
          deviceTypes: [DeviceType.TV, DeviceType.PC, DeviceType.TABLET],
          capabilities: ['output.display'],
          states: [DeviceState.READY, DeviceState.ONLINE]
        },
        
        showDeviceCapability: true,
        
        onDeviceSelected: (device) => {
          resolve(device);
        },
        
        onCancelled: () => {
          resolve(null);
        }
      });
    });
  }
  
  /**
   * 选择音频输出设备
   */
  static async pickAudioOutput(): Promise<DeviceInfo | null> {
    return new Promise(resolve => {
      DevicePickerDialog.show({
        title: '选择音频设备',
        subtitle: '选择音频播放设备',
        
        filter: {
          capabilities: ['output.speaker', 'output.headphone'],
          states: [DeviceState.READY]
        },
        
        onDeviceSelected: (device) => {
          resolve(device);
        },
        
        onCancelled: () => {
          resolve(null);
        }
      });
    });
  }
  
  /**
   * 选择文件传输目标设备
   */
  static async pickFileTransferTarget(): Promise<DeviceInfo | null> {
    return new Promise(resolve => {
      DevicePickerDialog.show({
        title: '发送到设备',
        subtitle: '选择接收文件的设备',
        
        filter: {
          capabilities: ['storage'],
          states: [DeviceState.READY, DeviceState.ONLINE]
        },
        
        showDeviceState: true,
        
        onDeviceSelected: (device) => {
          resolve(device);
        },
        
        onCancelled: () => {
          resolve(null);
        }
      });
    });
  }
  
  /**
   * 选择健康监测设备
   */
  static async pickHealthDevice(): Promise<DeviceInfo | null> {
    return new Promise(resolve => {
      DevicePickerDialog.show({
        title: '选择健康设备',
        subtitle: '选择用于健康监测的设备',
        
        filter: {
          deviceTypes: [DeviceType.WATCH],
          capabilities: ['sensor.heartrate', 'sensor.spo2'],
          states: [DeviceState.READY]
        },
        
        onDeviceSelected: (device) => {
          resolve(device);
        },
        
        onCancelled: () => {
          resolve(null);
        }
      });
    });
  }
  
  /**
   * 多设备选择
   */
  static async pickMultipleDevices(
    maxCount: number = 5
  ): Promise<DeviceInfo[]> {
    return new Promise(resolve => {
      DevicePickerDialog.show({
        title: '选择设备',
        subtitle: `最多选择${maxCount}个设备`,
        
        mode: DevicePickerMode.MULTI,
        multiSelect: true,
        maxSelectCount: maxCount,
        
        filter: {
          states: [DeviceState.READY, DeviceState.ONLINE]
        },
        
        onDevicesSelected: (devices) => {
          resolve(devices);
        },
        
        onCancelled: () => {
          resolve([]);
        }
      });
    });
  }
}

3.3 自定义DevicePicker组件

/**
 * 自定义设备选择器组件
 * 提供更灵活的定制能力
 */
@Component
export struct CustomDevicePicker {
  // 配置参数
  @Prop title: string = '选择设备';
  @Prop filter: DeviceFilter = {};
  @Prop multiSelect: boolean = false;
  @Prop maxSelectCount: number = 1;
  
  // 状态
  @State devices: DeviceInfo[] = [];
  @State filteredDevices: DeviceInfo[] = [];
  @State selectedDevices: Set<string> = new Set();
  @State searchText: string = '';
  @State loading: boolean = true;
  
  // 回调
  private onSelected?: (devices: DeviceInfo[]) => void;
  private onCancelled?: () => void;
  
  aboutToAppear(): void {
    this.loadDevices();
  }
  
  /**
   * 加载设备列表
   */
  private async loadDevices(): Promise<void> {
    this.loading = true;
    
    try {
      // 获取设备列表
      const deviceMgr = deviceManager.createDeviceManager('com.example.app');
      const allDevices = await deviceMgr.getTrustedDeviceList(true);
      
      this.devices = allDevices;
      
      // 应用过滤
      this.applyFilter();
    } catch (error) {
      console.error(`加载设备失败: ${JSON.stringify(error)}`);
    } finally {
      this.loading = false;
    }
  }
  
  /**
   * 应用过滤
   */
  private applyFilter(): void {
    let filtered = [...this.devices];
    
    // 应用配置的过滤器
    const filterImpl = new DeviceFilterImpl();
    filtered = filterImpl.applyFilter(filtered, this.filter);
    
    // 应用搜索过滤
    if (this.searchText) {
      filtered = filtered.filter(device => 
        device.deviceName.toLowerCase().includes(this.searchText.toLowerCase())
      );
    }
    
    this.filteredDevices = filtered;
  }
  
  build() {
    Column() {
      // 标题栏
      Row() {
        Text(this.title)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
        
        Blank()
        
        Button('取消')
          .fontSize(14)
          .backgroundColor(Color.Transparent)
          .fontColor('#666666')
          .onClick(() => {
            this.onCancelled?.();
          })
      }
      .width('100%')
      .padding(16)
      
      Divider()
      
      // 搜索栏
      Row() {
        TextInput({ placeholder: '搜索设备' })
          .width('100%')
          .height(40)
          .onChange((value) => {
            this.searchText = value;
            this.applyFilter();
          })
      }
      .width('100%')
      .padding({ left: 16, right: 16, top: 12, bottom: 12 })
      
      Divider()
      
      // 设备列表
      if (this.loading) {
        Column() {
          LoadingProgress()
            .width(48)
            .height(48)
          
          Text('加载中...')
            .fontSize(14)
            .fontColor('#999999')
            .margin({ top: 12 })
        }
        .width('100%')
        .layoutWeight(1)
        .justifyContent(FlexAlign.Center)
      } else if (this.filteredDevices.length === 0) {
        Column() {
          Text('暂无设备')
            .fontSize(16)
            .fontColor('#999999')
        }
        .width('100%')
        .layoutWeight(1)
        .justifyContent(FlexAlign.Center)
      } else {
        List() {
          ForEach(this.filteredDevices, (device: DeviceInfo) => {
            ListItem() {
              this.DeviceItem(device)
            }
          }, (device: DeviceInfo) => device.deviceId)
        }
        .width('100%')
        .layoutWeight(1)
      }
      
      Divider()
      
      // 底部操作栏
      Row() {
        Text(`已选${this.selectedDevices.size}个设备`)
          .fontSize(14)
          .fontColor('#666666')
        
        Blank()
        
        Button('确认')
          .fontSize(16)
          .height(40)
          .enabled(this.selectedDevices.size > 0)
          .onClick(() => {
            this.confirmSelection();
          })
      }
      .width('100%')
      .padding(16)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }
  
  @Builder
  DeviceItem(device: DeviceInfo) {
    Row() {
      // 选择指示器(多选模式)
      if (this.multiSelect) {
        Checkbox()
          .select(this.selectedDevices.has(device.deviceId))
          .onChange((checked) => {
            this.handleSelect(device, checked);
          })
          .margin({ right: 12 })
      }
      
      // 设备图标
      Circle()
        .width(48)
        .height(48)
        .fill(this.getDeviceColor(device.deviceType))
      
      // 设备信息
      Column() {
        Text(device.deviceName)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        
        Row() {
          Text(this.getDeviceTypeText(device.deviceType))
            .fontSize(12)
            .fontColor('#999999')
          
          Text(' · ')
            .fontSize(12)
            .fontColor('#999999')
          
          Text(this.getStateText(device.state))
            .fontSize(12)
            .fontColor(this.getStateColor(device.state))
        }
        .margin({ top: 4 })
      }
      .alignItems(HorizontalAlign.Start)
      .margin({ left: 12 })
      .layoutWeight(1)
      
      // 单选模式的选择指示器
      if (!this.multiSelect) {
        Radio({ value: device.deviceId, group: 'devicePicker' })
          .checked(this.selectedDevices.has(device.deviceId))
          .onChange((checked) => {
            if (checked) {
              this.handleSelect(device, true);
            }
          })
      }
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(8)
    .margin({ bottom: 8 })
    .onClick(() => {
      if (!this.multiSelect) {
        this.handleSelect(device, true);
      }
    })
  }
  
  /**
   * 处理设备选择
   */
  private handleSelect(device: DeviceInfo, selected: boolean): void {
    if (selected) {
      if (this.multiSelect) {
        // 多选模式
        if (this.selectedDevices.size < this.maxSelectCount) {
          this.selectedDevices.add(device.deviceId);
        }
      } else {
        // 单选模式
        this.selectedDevices.clear();
        this.selectedDevices.add(device.deviceId);
      }
    } else {
      this.selectedDevices.delete(device.deviceId);
    }
  }
  
  /**
   * 确认选择
   */
  private confirmSelection(): void {
    const selected = this.filteredDevices.filter(
      device => this.selectedDevices.has(device.deviceId)
    );
    
    this.onSelected?.(selected);
  }
  
  /**
   * 获取设备类型文本
   */
  private getDeviceTypeText(type: DeviceType): string {
    const typeMap: Record<number, string> = {
      [DeviceType.PHONE]: '手机',
      [DeviceType.TABLET]: '平板',
      [DeviceType.WATCH]: '手表',
      [DeviceType.TV]: '电视',
      [DeviceType.PC]: '电脑'
    };
    
    return typeMap[type] || '设备';
  }
  
  /**
   * 获取设备颜色
   */
  private getDeviceColor(type: DeviceType): ResourceColor {
    const colorMap: Record<number, ResourceColor> = {
      [DeviceType.PHONE]: '#4A90E2',
      [DeviceType.TABLET]: '#7ED321',
      [DeviceType.WATCH]: '#F5A623',
      [DeviceType.TV]: '#9B59B6',
      [DeviceType.PC]: '#E74C3C'
    };
    
    return colorMap[type] || '#999999';
  }
  
  /**
   * 获取状态文本
   */
  private getStateText(state: DeviceState): string {
    const stateMap: Record<DeviceState, string> = {
      [DeviceState.OFFLINE]: '离线',
      [DeviceState.ONLINE]: '在线',
      [DeviceState.READY]: '就绪',
      [DeviceState.BUSY]: '繁忙',
      [DeviceState.SLEEP]: '休眠',
      [DeviceState.ERROR]: '错误',
      [DeviceState.UNKNOWN]: '未知'
    };
    
    return stateMap[state] || '未知';
  }
  
  /**
   * 获取状态颜色
   */
  private getStateColor(state: DeviceState): ResourceColor {
    if (state === DeviceState.READY || state === DeviceState.ONLINE) {
      return '#7ED321';
    }
    
    if (state === DeviceState.OFFLINE || state === DeviceState.ERROR) {
      return '#D0021B';
    }
    
    return '#F5A623';
  }
  
  /**
   * 设置选择回调
   */
  setOnSelected(callback: (devices: DeviceInfo[]) => void): this {
    this.onSelected = callback;
    return this;
  }
  
  /**
   * 设置取消回调
   */
  setOnCancelled(callback: () => void): this {
    this.onCancelled = callback;
    return this;
  }
}

3.4 DevicePicker集成示例

/**
 * DevicePicker集成示例
 * 展示在实际应用中的使用
 */
@Component
export struct DevicePickerIntegrationExample {
  @State currentScene: string = '';
  @State selectedDevice: DeviceInfo | null = null;
  
  build() {
    Column() {
      // 场景选择
      Row() {
        Text('选择场景:')
          .fontSize(16)
        
        Blank()
        
        Button('视频通话')
          .onClick(() => this.handleScene('video_call'))
        
        Button('音乐播放')
          .onClick(() => this.handleScene('music'))
        
        Button('文件传输')
          .onClick(() => this.handleScene('file'))
      }
      .width('100%')
      .padding(16)
      
      Divider()
      
      // 结果展示
      Column() {
        if (this.selectedDevice) {
          Text(`已选择设备: ${this.selectedDevice.deviceName}`)
            .fontSize(18)
            .fontWeight(FontWeight.Medium)
          
          Text(`设备类型: ${this.selectedDevice.deviceType}`)
            .fontSize(14)
            .fontColor('#666666')
            .margin({ top: 8 })
          
          Text(`设备状态: ${this.selectedDevice.state}`)
            .fontSize(14)
            .fontColor('#666666')
            .margin({ top: 4 })
        } else {
          Text('请选择场景并选择设备')
            .fontSize(16)
            .fontColor('#999999')
        }
      }
      .width('100%')
      .layoutWeight(1)
      .justifyContent(FlexAlign.Center)
    }
    .width('100%')
    .height('100%')
  }
  
  /**
   * 处理场景选择
   */
  private async handleScene(scene: string): Promise<void> {
    this.currentScene = scene;
    
    switch (scene) {
      case 'video_call':
        // 视频通话场景:选择有摄像头和麦克风的设备
        this.selectedDevice = await SceneDevicePicker.pickCamera();
        break;
        
      case 'music':
        // 音乐播放场景:选择音频输出设备
        this.selectedDevice = await SceneDevicePicker.pickAudioOutput();
        break;
        
      case 'file':
        // 文件传输场景:选择存储设备
        this.selectedDevice = await SceneDevicePicker.pickFileTransferTarget();
        break;
    }
    
    if (this.selectedDevice) {
      // 执行场景相关操作
      this.executeSceneAction(scene, this.selectedDevice);
    }
  }
  
  /**
   * 执行场景操作
   */
  private executeSceneAction(scene: string, device: DeviceInfo): void {
    console.info(`执行场景操作: ${scene}, 设备: ${device.deviceName}`);
    
    // 根据场景执行不同操作
    switch (scene) {
      case 'video_call':
        // 启动视频通话
        this.startVideoCall(device);
        break;
        
      case 'music':
        // 切换音频输出
        this.switchAudioOutput(device);
        break;
        
      case 'file':
        // 发送文件
        this.sendFile(device);
        break;
    }
  }
  
  // 场景操作实现(简化)
  private startVideoCall(device: DeviceInfo): void {
    console.info(`启动视频通话,使用设备: ${device.deviceName}`);
  }
  
  private switchAudioOutput(device: DeviceInfo): void {
    console.info(`切换音频输出到: ${device.deviceName}`);
  }
  
  private sendFile(device: DeviceInfo): void {
    console.info(`发送文件到: ${device.deviceName}`);
  }
}

四、踩坑与注意事项

4.1 过滤相关

坑1:过滤条件过严导致无设备

// ❌ 错误做法:过滤条件过严
filter: {
  deviceTypes: [DeviceType.WATCH],
  capabilities: ['sensor.camera.front'],  // 手表通常没有前置摄像头
  states: [DeviceState.READY]
}

// ✅ 正确做法:合理设置过滤条件
filter: {
  deviceTypes: [DeviceType.WATCH],
  capabilities: ['sensor.heartrate'],  // 手表有心率传感器
  states: [DeviceState.READY, DeviceState.ONLINE]  // 包含在线状态
}

坑2:忘记处理取消情况

// ❌ 错误做法:只处理选择,不处理取消
onDeviceSelected: (device) => {
  this.selectedDevice = device;
}

// ✅ 正确做法:同时处理取消
onDeviceSelected: (device) => {
  this.selectedDevice = device;
},
onCancelled: () => {
  console.info('用户取消选择');
  // 可以显示提示或执行其他操作
}

4.2 UI相关

坑3:设备列表不刷新

设备状态变化后,DevicePicker列表未更新。

// ✅ 正确做法:监听设备状态变化并刷新
aboutToAppear(): void {
  // 监听设备状态变化
  deviceMgr.on('deviceStateChange', () => {
    // 刷新设备列表
    this.loadDevices();
  });
}

坑4:多选数量限制未生效

// ✅ 正确做法:在handleSelect中检查数量限制
private handleSelect(device: DeviceInfo, selected: boolean): void {
  if (selected && this.multiSelect) {
    if (this.selectedDevices.size >= this.maxSelectCount) {
      // 已达到最大选择数量,提示用户
      this.showToast(`最多选择${this.maxSelectCount}个设备`);
      return;
    }
    this.selectedDevices.add(device.deviceId);
  }
}

4.3 性能相关

坑5:大量设备导致性能问题

// ✅ 正确做法:实现虚拟滚动或分页加载
List() {
  ForEach(this.filteredDevices, (device: DeviceInfo) => {
    ListItem() {
      this.DeviceItem(device)
    }
  }, (device: DeviceInfo) => device.deviceId)
}
.cachedCount(5)  // 缓存5个ListItem

五、HarmonyOS 6适配指南

5.1 API变更

DevicePicker API增强

// HarmonyOS 5.0
DevicePickerDialog.show({
  title: '选择设备',
  onDeviceSelected: callback
});

// HarmonyOS 6.0
import { DevicePicker } from '@ohos.distributedHardware.devicePicker';

// 支持更多配置
DevicePicker.show({
  title: '选择设备',
  
  // 新增:智能推荐
  recommendation: {
    enabled: true,
    strategy: 'AI_BASED'  // AI推荐策略
  },
  
  // 新增:设备分组
  grouping: {
    enabled: true,
    groupBy: 'DEVICE_TYPE'  // 按设备类型分组
  },
  
  // 新增:设备预览
  preview: {
    enabled: true,
    showCapability: true,
    showStatus: true
  },
  
  onDeviceSelected: callback
});

5.2 行为变更

变更1:智能推荐

HarmonyOS 6新增AI智能推荐能力。

// HarmonyOS 6新增智能推荐
const recommendation = await DevicePicker.getRecommendation({
  context: 'video_call',  // 当前场景
  history: true           // 基于历史数据
});

console.info(`推荐设备: ${recommendation.devices}`);

变更2:设备预览

// HarmonyOS 6新增设备预览
DevicePicker.show({
  preview: {
    enabled: true,
    onPreview: async (device) => {
      // 返回预览内容
      return {
        thumbnail: await this.getDeviceThumbnail(device),
        description: this.getDeviceDescription(device)
      };
    }
  }
});

六、总结

DevicePicker是HarmonyOS提供的系统级设备选择组件,通过封装设备发现、过滤、排序、UI渲染、交互回调等完整流程,大幅简化了设备选择的开发成本。

核心要点回顾

  1. 组件架构:配置驱动的设备选择组件
  2. 设备过滤:多维度过滤能力
  3. 设备排序:多种排序策略
  4. 场景封装:常用场景的快速选择

最佳实践建议

  • 合理设置过滤条件,避免无设备可选
  • 同时处理选择和取消回调
  • 监听设备状态变化并刷新列表
  • 检查多选数量限制
  • 实现虚拟滚动优化性能
  • 使用兼容层适配多版本API

DevicePicker让设备选择从"重复造轮子"升级到"一行代码搞定",是提升分布式应用开发效率的利器。


【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。