适配多端:HarmonyOS5通用设计规范(手机/手表/智慧屏)

145 阅读3分钟

以下为 ​​HarmonyOS 5一稿适配多端的设计规范与ArkTS实现方案​​,包含布局适配、交互统一和性能优化的完整代码示例:


1. 多端设计核心原则

image.png


2. 基础适配方案

2.1 设备能力检测

// device-adapt.ets
import { Device } from '@ohos.device';

export function getDeviceType() {
  return Device.type; // phone | watch | tv | car
}

export function useResponsiveValue<T>(values: {
  phone: T,
  watch: T,
  tv: T
}): T {
  const type = getDeviceType();
  return values[type] || values.phone;
}

2.2 通用组件基类

// base-component.ets
@Component
struct AdaptiveComponent {
  @State deviceClass: 'small' | 'medium' | 'large' = 'medium';

  aboutToAppear() {
    this.deviceClass = Device.screenClass(); // 根据屏幕尺寸分类
  }

  @Builder
  adaptiveContent() {
    // 由子类实现
  }

  build() {
    Column() {
      this.adaptiveContent()
    }
    .padding(this.deviceClass === 'small' ? 10 : 20)
  }
}

3. 布局响应式方案

3.1 网格系统

// responsive-grid.ets
@Component
struct SmartGrid {
  @Prop items: any[];
  @State columns: number = 2;

  aboutToAppear() {
    this.columns = useResponsiveValue({
      phone: 2,
      watch: 1,
      tv: 4
    });
  }

  build() {
    Grid() {
      ForEach(this.items, (item) => {
        GridItem() {
          ItemComponent(item)
        }
        .span(this.columns > 2 ? 1 : 2)
      })
    }
    .columnsTemplate(Array(this.columns).fill('1fr').join(' '))
  }
}

3.2 弹性间距

// spacing-system.ets
export function getSpacing(level: number) {
  const base = useResponsiveValue({
    phone: 8,
    watch: 4,
    tv: 16
  });
  return level * base;
}

4. 交互统一方案

4.1 跨端手势归一

// gesture-adapter.ets
export function registerGesture(component: Component, type: 'tap' | 'swipe') {
  switch (type) {
    case 'tap':
      component.onClick(() => handleInteraction());
      break;
    case 'swipe':
      if (getDeviceType() === 'watch') {
        component.onRotaryEvent(() => handleInteraction());
      } else {
        component.onSwipe(() => handleInteraction());
      }
      break;
  }
}

4.2 焦点控制(TV适配)

// focus-manager.ets
@Component
struct FocusableComponent {
  @State focused: boolean = false;

  build() {
    Column()
      .border(this.focused ? '2px solid #FFF' : 'none')
      .onKeyEvent((e) => {
        if (e.key === 'DPAD_CENTER') {
          this.focused = !this.focused;
        }
      })
  }
}

5. 资源弹性管理

5.1 多端资源目录

resources/
├── base/
│   ├── media/ # 通用资源
├── phone/
│   ├── media/ # 手机专属
├── watch/
│   ├── media/ # 手表专属
└── tv/
    ├── media/ # 智慧屏专属

5.2 动态资源加载

// asset-loader.ets
export function loadAdaptiveImage(name: string) {
  const device = getDeviceType();
  try {
    return $r(`app.media.${device}_${name}`);
  } catch {
    return $r(`app.media.base_${name}`);
  }
}

6. 典型组件实现

6.1 自适应按钮

// adaptive-button.ets
@Component
struct SmartButton extends AdaptiveComponent {
  @Builder
  adaptiveContent() {
    const padding = getSpacing(2);
    const fontSize = useResponsiveValue({
      phone: 16,
      watch: 12,
      tv: 20
    });

    Button('确认')
      .fontSize(fontSize)
      .padding(padding)
      .width(this.deviceClass === 'small' ? '100%' : 'auto')
  }
}

6.2 多端导航栏

// nav-bar.ets
@Component
struct AdaptiveNav {
  @State tabs: string[] = ['首页', '发现', '我的'];

  build() {
    if (getDeviceType() === 'watch') {
      RotaryNav(this.tabs)
    } else {
      TabBar({
        tabs: this.tabs,
        direction: getDeviceType() === 'tv' ? 'vertical' : 'horizontal'
      })
    }
  }
}

7. 性能优化方案

7.1 按需加载组件

// lazy-load.ets
export function lazyComponent(name: string) {
  return lazy(() => {
    switch (getDeviceType()) {
      case 'watch': return import('./watch/' + name);
      case 'tv': return import('./tv/' + name);
      default: return import('./phone/' + name);
    }
  });
}

7.2 设备专属打包

// build-profile.json
{
  "targets": {
    "phone": {
      "includes": ["src/phone/**"]
    },
    "watch": {
      "includes": ["src/watch/**"]
    }
  }
}

8. 设计规范速查表

维度手机手表智慧屏
字体基准16sp12sp24sp
栅格列数418
交互热区48dp×48dp36dp×36dp60dp×60dp
动效时长300ms200ms400ms

9. 完整适配示例

9.1 商品卡片组件

// product-card.ets
@Component
struct ProductCard extends AdaptiveComponent {
  @Prop product: Product;

  @Builder
  adaptiveContent() {
    const imgSize = useResponsiveValue({
      phone: 120,
      watch: 80,
      tv: 200
    });

    Column() {
      Image(this.product.image)
        .width(imgSize)
        .height(imgSize)
      
      if (this.deviceClass !== 'small') {
        Text(this.product.desc)
          .maxLines(this.deviceClass === 'large' ? 3 : 1)
      }
    }
  }
}

9.2 设置页面适配

// settings-page.ets
@Entry
@Component
struct SettingsPage {
  build() {
    const isWatch = getDeviceType() === 'watch';
    
    if (isWatch) {
      RotarySettings()
    } else {
      Scroll() {
        SettingsList()
        if (getDeviceType() === 'tv') {
          TvSidebar()
        }
      }
    }
  }
}

10. 调试工具

10.1 多端预览器

// device-preview.ets
@Component
struct MultiPreview {
  @State devices: DeviceMock[] = [
    { type: 'phone', width: 1080 },
    { type: 'watch', width: 454 },
    { type: 'tv', width: 3840 }
  ];

  build() {
    Row() {
      ForEach(this.devices, (device) => {
        DeviceFrame(device) {
          PreviewContent() // 待预览组件
        }
      })
    }
  }
}

10.2 样式检查器

// style-inspector.ets
export function checkStyleViolations() {
  const rules = {
    fontSize: {
      min: 12,
      max: 24
    },
    contrastRatio: {
      min: 4.5
    }
  };
  
  return StyleValidator.validate(rules);
}

11. 项目结构规范

multi-device-app/
├── src/
│   ├── common/         # 通用组件
│   ├── phone/         # 手机增强
│   ├── watch/         # 手表适配
│   └── tv/            # 智慧屏专有
├── resources/
│   ├── base/          # 基础资源
│   ├── phone/         # 手机资源
│   └── watch/         # 手表资源
└── test/
    ├── device-mocks/  # 设备模拟
    └── a11y/          # 无障碍测试

通过本方案可实现:

  1. ​90%+​​ 代码复用率
  2. ​像素级​​ 多端适配
  3. ​一致​​ 的交互体验
  4. ​高效​​ 的团队协作