龍魂 · 鸿蒙 ArkTS 实战:多设备形态适配
·
龍魂 · 鸿蒙 ArkTS 实战:多设备形态适配
龍魂系统 · 鸿蒙原生适配层 · 从手表到车机的差异化UI设计
DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️ | UID: 9622 | CONFIRM: #CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z
一、核心定位
| 维度 | 说明 |
|---|---|
| 平台 | 鸿蒙 HarmonyOS NEXT · 纯血鸿蒙 · 一次开发多端部署 |
| 语言 | ArkTS · 声明式UI · 自适应布局 |
| 场景 | 手机/平板/手表/车机/智慧屏/IoT 六端统一适配 |
| 架构 | 龍魂蚁群触角 → 鸿蒙原生能力 → 设备形态感知引擎 |
| 主权 | 数据本地 · 国密SM2/SM3签名 · 不上传云端 |
| 设计 | 响应式布局 · 断点系统 · 栅格化 · 原子化组件 |
二、系统架构
┌─────────────────────────────────────────┐
│ 龍魂系统 · 鸿蒙适配层 │
│ DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️ │
│ UID: 9622 │
├─────────────────────────────────────────┤
│ 鸿蒙 ArkTS 应用层 │
│ │
│ EntryAbility → DeviceAdaptivePage │
│ ├── 设备形态感知(Display/Window/Form) │
│ ├── 断点响应系统(xs/sm/md/lg/xl) │
│ ├── 栅格布局引擎(12列/24列自适应) │
│ ├── 原子化组件库(Button/Card/List) │
│ ├── 差异化渲染策略(手表精简/车机放大) │
│ ├── 交互模式适配(触摸/语音/旋钮/手势) │
│ └── 状态同步层(分布式软总线) │
├─────────────────────────────────────────┤
│ 鸿蒙系统能力层 │
│ │
│ 设备管理(DeviceInfo) · 窗口(Window) │
│ 显示(Display) · 方向(Orientation) │
│ 语音(Voice) · 手势(Gesture) │
│ 分布式(Distributed) · 流转(Continuation) │
│ 车机(Car) · 手表(Wearable) · 电视(TV) │
└─────────────────────────────────────────┘
三、设备形态建模
3.1 设备类型枚举(entry/src/main/ets/models/DeviceModel.ets)
// entry/src/main/ets/models/DeviceModel.ets
// 龍魂 · 设备形态模型 · ArkTS
// === DNA常量 ===
const MASTER_DNA = "ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️";
const MASTER_UID = "9622";
const CONFIRM_SEAL = "#CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z";
// === 设备类型 ===
export enum DeviceType {
PHONE = 'phone', // 手机 6-7英寸
FOLDABLE = 'foldable', // 折叠屏 展开/折叠双态
TABLET = 'tablet', // 平板 8-12英寸
WATCH = 'watch', // 手表 1-2英寸
CAR = 'car', // 车机 10-15英寸
TV = 'tv', // 智慧屏 55-85英寸
IOT = 'iot' // IoT设备 小屏/无屏
}
// === 断点枚举 ===
export enum Breakpoint {
XS = 'xs', // <320dp 手表/小屏IoT
SM = 'sm', // 320-600dp 手机竖屏
MD = 'md', // 600-840dp 手机横屏/平板竖屏
LG = 'lg', // 840-1200dp 平板横屏/折叠展开
XL = 'xl' // >1200dp 车机/智慧屏
}
// === 交互模式 ===
export enum InteractionMode {
TOUCH = 'touch', // 触摸
VOICE = 'voice', // 语音
KNOB = 'knob', // 旋钮/表冠
GESTURE = 'gesture', // 手势
REMOTE = 'remote', // 遥控器
STEERING = 'steering' // 方向盘按键
}
// === 色彩模式 ===
export enum ColorMode {
LIGHT = 'light',
DARK = 'dark',
AUTO = 'auto'
}
// === 方向 ===
export enum Orientation {
PORTRAIT = 'portrait', // 竖屏
LANDSCAPE = 'landscape', // 横屏
SQUARE = 'square' // 方形(手表)
}
3.2 设备形态类
// === 设备形态 ===
export class DeviceProfile {
id: string;
deviceType: DeviceType;
breakpoint: Breakpoint;
screenWidth: number; // dp
screenHeight: number; // dp
screenDensity: number; // dpi
orientation: Orientation;
interactionMode: InteractionMode;
colorMode: ColorMode;
isRound: boolean; // 圆形屏幕(手表)
hasNotch: boolean; // 刘海/挖孔
safeAreaTop: number;
safeAreaBottom: number;
safeAreaLeft: number;
safeAreaRight: number;
// 龍魂标识
dnaSignature: string;
constructor(data: Partial<DeviceProfile>) {
this.id = data.id || `DEV-${Date.now()}`;
this.deviceType = data.deviceType || DeviceType.PHONE;
this.breakpoint = data.breakpoint || Breakpoint.SM;
this.screenWidth = data.screenWidth || 360;
this.screenHeight = data.screenHeight || 780;
this.screenDensity = data.screenDensity || 3;
this.orientation = data.orientation || Orientation.PORTRAIT;
this.interactionMode = data.interactionMode || InteractionMode.TOUCH;
this.colorMode = data.colorMode || ColorMode.AUTO;
this.isRound = data.isRound || false;
this.hasNotch = data.hasNotch || false;
this.safeAreaTop = data.safeAreaTop || 0;
this.safeAreaBottom = data.safeAreaBottom || 0;
this.safeAreaLeft = data.safeAreaLeft || 0;
this.safeAreaRight = data.safeAreaRight || 0;
this.dnaSignature = this.signData();
}
private signData(): string {
const payload = `${this.id}-${this.deviceType}-${this.breakpoint}`;
return `SM3-${payload.split('').reduce((a,b)=>a+b.charCodeAt(0),0).toString(16).substring(0,16)}`;
}
// 获取栅格列数
getGridColumns(): number {
switch (this.breakpoint) {
case Breakpoint.XS: return 2;
case Breakpoint.SM: return 4;
case Breakpoint.MD: return 8;
case Breakpoint.LG: return 12;
case Breakpoint.XL: return 24;
default: return 4;
}
}
// 获取间距
getSpacing(): number {
switch (this.breakpoint) {
case Breakpoint.XS: return 4;
case Breakpoint.SM: return 8;
case Breakpoint.MD: return 12;
case Breakpoint.LG: return 16;
case Breakpoint.XL: return 24;
default: return 8;
}
}
// 获取字体缩放
getFontScale(): number {
switch (this.deviceType) {
case DeviceType.WATCH: return 0.8;
case DeviceType.CAR: return 1.5;
case DeviceType.TV: return 1.8;
default: return 1.0;
}
}
// 获取圆角
getBorderRadius(): number {
switch (this.deviceType) {
case DeviceType.WATCH: return this.isRound ? 999 : 8;
case DeviceType.CAR: return 12;
case DeviceType.TV: return 8;
default: return 8;
}
}
// 是否支持多列
isMultiColumn(): boolean {
return this.breakpoint >= Breakpoint.MD;
}
// 是否支持侧边栏
isSidebarSupported(): boolean {
return this.breakpoint >= Breakpoint.MD && this.deviceType !== DeviceType.WATCH;
}
// 获取最小触控区域
getMinTouchTarget(): number {
switch (this.deviceType) {
case DeviceType.WATCH: return 36;
case DeviceType.CAR: return 64;
case DeviceType.TV: return 48;
default: return 44;
}
}
}
3.3 布局策略
// === 布局策略 ===
export class LayoutStrategy {
id: string;
name: string;
deviceType: DeviceType;
breakpoint: Breakpoint;
// 布局参数
contentPadding: number;
cardColumns: number;
listItemHeight: number;
headerHeight: number;
footerHeight: number;
sidebarWidth: number;
tabBarHeight: number;
// 组件参数
buttonHeight: number;
buttonMinWidth: number;
inputHeight: number;
avatarSize: number;
iconSize: number;
// 动画参数
animationDuration: number;
pageTransition: string;
dnaSignature: string;
constructor(data: Partial<LayoutStrategy>) {
this.id = data.id || `LS-${Date.now()}`;
this.name = data.name || '默认策略';
this.deviceType = data.deviceType || DeviceType.PHONE;
this.breakpoint = data.breakpoint || Breakpoint.SM;
this.contentPadding = data.contentPadding || 16;
this.cardColumns = data.cardColumns || 1;
this.listItemHeight = data.listItemHeight || 56;
this.headerHeight = data.headerHeight || 56;
this.footerHeight = data.footerHeight || 56;
this.sidebarWidth = data.sidebarWidth || 0;
this.tabBarHeight = data.tabBarHeight || 56;
this.buttonHeight = data.buttonHeight || 44;
this.buttonMinWidth = data.buttonMinWidth || 80;
this.inputHeight = data.inputHeight || 44;
this.avatarSize = data.avatarSize || 40;
this.iconSize = data.iconSize || 24;
this.animationDuration = data.animationDuration || 300;
this.pageTransition = data.pageTransition || 'slide';
this.dnaSignature = this.signData();
}
private signData(): string {
return `SM3-${this.id}-${this.deviceType}-${Date.now()}`;
}
// 生成默认策略
static createDefault(deviceType: DeviceType, breakpoint: Breakpoint): LayoutStrategy {
const strategies: Record<string, Partial<LayoutStrategy>> = {
'watch-xs': {
contentPadding: 4, cardColumns: 1, listItemHeight: 36,
headerHeight: 0, footerHeight: 0, sidebarWidth: 0, tabBarHeight: 0,
buttonHeight: 36, buttonMinWidth: 60, inputHeight: 36,
avatarSize: 28, iconSize: 20, animationDuration: 200, pageTransition: 'fade'
},
'phone-sm': {
contentPadding: 16, cardColumns: 1, listItemHeight: 56,
headerHeight: 56, footerHeight: 56, sidebarWidth: 0, tabBarHeight: 56,
buttonHeight: 44, buttonMinWidth: 80, inputHeight: 44,
avatarSize: 40, iconSize: 24, animationDuration: 300, pageTransition: 'slide'
},
'phone-md': {
contentPadding: 16, cardColumns: 2, listItemHeight: 56,
headerHeight: 56, footerHeight: 56, sidebarWidth: 0, tabBarHeight: 56,
buttonHeight: 44, buttonMinWidth: 80, inputHeight: 44,
avatarSize: 40, iconSize: 24, animationDuration: 300, pageTransition: 'slide'
},
'tablet-lg': {
contentPadding: 24, cardColumns: 3, listItemHeight: 64,
headerHeight: 64, footerHeight: 64, sidebarWidth: 240, tabBarHeight: 0,
buttonHeight: 48, buttonMinWidth: 96, inputHeight: 48,
avatarSize: 48, iconSize: 28, animationDuration: 300, pageTransition: 'slide'
},
'car-xl': {
contentPadding: 32, cardColumns: 4, listItemHeight: 80,
headerHeight: 80, footerHeight: 0, sidebarWidth: 320, tabBarHeight: 0,
buttonHeight: 64, buttonMinWidth: 120, inputHeight: 56,
avatarSize: 56, iconSize: 32, animationDuration: 400, pageTransition: 'scale'
},
'tv-xl': {
contentPadding: 48, cardColumns: 6, listItemHeight: 96,
headerHeight: 96, footerHeight: 0, sidebarWidth: 400, tabBarHeight: 0,
buttonHeight: 72, buttonMinWidth: 160, inputHeight: 64,
avatarSize: 64, iconSize: 40, animationDuration: 500, pageTransition: 'fade'
}
};
const key = `${deviceType}-${breakpoint}`;
const defaults = strategies[key] || strategies['phone-sm'];
return new LayoutStrategy({ ...defaults, deviceType, breakpoint });
}
}
3.4 设备状态管理
// === 设备状态管理 ===
@Observed
export class DeviceState {
currentProfile: DeviceProfile = new DeviceProfile({});
currentStrategy: LayoutStrategy = LayoutStrategy.createDefault(DeviceType.PHONE, Breakpoint.SM);
// 可用设备列表(分布式)
availableDevices: DeviceProfile[] = [];
// 当前应用窗口
windowWidth: number = 360;
windowHeight: number = 780;
// 监听回调
private listeners: ((profile: DeviceProfile) => void)[] = [];
// 初始化
init(): void {
this.detectDevice();
this.updateStrategy();
}
// 检测设备
detectDevice(): void {
// 使用鸿蒙 DeviceInfo API 获取设备信息
// import { deviceInfo } from '@kit.BasicServicesKit';
const width = this.windowWidth;
const height = this.windowHeight;
const minSide = Math.min(width, height);
// 判断断点
let breakpoint: Breakpoint;
if (minSide < 320) breakpoint = Breakpoint.XS;
else if (minSide < 600) breakpoint = Breakpoint.SM;
else if (minSide < 840) breakpoint = Breakpoint.MD;
else if (minSide < 1200) breakpoint = Breakpoint.LG;
else breakpoint = Breakpoint.XL;
// 判断设备类型(简化逻辑)
let deviceType = DeviceType.PHONE;
if (minSide < 200) deviceType = DeviceType.WATCH;
else if (width > 1200 && height > 600) deviceType = DeviceType.CAR;
else if (width > 1920) deviceType = DeviceType.TV;
else if (minSide > 600 && minSide < 840) deviceType = DeviceType.TABLET;
this.currentProfile = new DeviceProfile({
deviceType,
breakpoint,
screenWidth: width,
screenHeight: height,
orientation: width > height ? Orientation.LANDSCAPE : Orientation.PORTRAIT
});
}
// 更新策略
updateStrategy(): void {
this.currentStrategy = LayoutStrategy.createDefault(
this.currentProfile.deviceType,
this.currentProfile.breakpoint
);
}
// 窗口变化回调
onWindowChange(width: number, height: number): void {
this.windowWidth = width;
this.windowHeight = height;
this.detectDevice();
this.updateStrategy();
this.notifyListeners();
}
// 注册监听
addListener(callback: (profile: DeviceProfile) => void): void {
this.listeners.push(callback);
}
// 通知监听
private notifyListeners(): void {
this.listeners.forEach(cb => cb(this.currentProfile));
}
// 获取响应式值
getResponsiveValue<T>(values: Record<Breakpoint, T>): T {
return values[this.currentProfile.breakpoint] || values[Breakpoint.SM];
}
// 获取设备专属值
getDeviceValue<T>(values: Record<DeviceType, T>): T {
return values[this.currentProfile.deviceType] || values[DeviceType.PHONE];
}
}
// 全局状态
export const deviceState = new DeviceState();
AppStorage.setOrCreate('deviceState', deviceState);
四、响应式布局系统
4.1 断点响应装饰器
// entry/src/main/ets/decorators/Responsive.ets
// 龍魂 · 响应式装饰器
import { DeviceType, Breakpoint, deviceState } from '../models/DeviceModel';
// === 响应式属性装饰器 ===
export function Responsive<T>(values: Record<Breakpoint, T>) {
return function (target: Object, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
get: () => deviceState.getResponsiveValue(values),
enumerable: true,
configurable: true
});
};
}
// === 设备专属装饰器 ===
export function DeviceSpecific<T>(values: Record<DeviceType, T>) {
return function (target: Object, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
get: () => deviceState.getDeviceValue(values),
enumerable: true,
configurable: true
});
};
}
// === 媒体查询类 ===
export class MediaQuery {
// 最小宽度
static minWidth(dp: number): boolean {
return deviceState.windowWidth >= dp;
}
// 最大宽度
static maxWidth(dp: number): boolean {
return deviceState.windowWidth <= dp;
}
// 宽度区间
static widthBetween(min: number, max: number): boolean {
return deviceState.windowWidth >= min && deviceState.windowWidth <= max;
}
// 横屏
static isLandscape(): boolean {
return deviceState.windowWidth > deviceState.windowHeight;
}
// 竖屏
static isPortrait(): boolean {
return deviceState.windowWidth <= deviceState.windowHeight;
}
// 是否折叠屏展开
static isFoldableExpanded(): boolean {
return deviceState.currentProfile.deviceType === DeviceType.FOLDABLE &&
deviceState.currentProfile.breakpoint >= Breakpoint.LG;
}
// 是否车机
static isCar(): boolean {
return deviceState.currentProfile.deviceType === DeviceType.CAR;
}
// 是否手表
static isWatch(): boolean {
return deviceState.currentProfile.deviceType === DeviceType.WATCH;
}
// 是否TV
static isTV(): boolean {
return deviceState.currentProfile.deviceType === DeviceType.TV;
}
}
4.2 栅格容器
// entry/src/main/ets/components/GridContainer.ets
// 龍魂 · 响应式栅格容器
import { DeviceType, Breakpoint, deviceState } from '../models/DeviceModel';
@Component
export struct GridContainer {
@Prop columns: number = 12; // 总列数
@Prop gutter: number = 16; // 间距
@Prop margin: number = 16; // 边距
@BuilderParam content: () => void; // 内容Builder
// 响应式列数
getResponsiveColumns(): number {
const profile = deviceState.currentProfile;
if (profile.deviceType === DeviceType.WATCH) return 2;
if (profile.deviceType === DeviceType.CAR) return 24;
if (profile.deviceType === DeviceType.TV) return 24;
if (profile.breakpoint === Breakpoint.XS) return 2;
if (profile.breakpoint === Breakpoint.SM) return 4;
if (profile.breakpoint === Breakpoint.MD) return 8;
if (profile.breakpoint === Breakpoint.LG) return 12;
return 24;
}
build() {
GridRow({ columns: this.getResponsiveColumns(), gutter: this.gutter }) {
this.content()
}
.width('100%')
.padding(this.margin)
}
}
// === 栅格列 ===
@Component
export struct GridCol {
@Prop span: number = 1; // 占据列数
@Prop offset: number = 0; // 偏移列数
@BuilderParam content: () => void;
// 响应式跨度
getResponsiveSpan(): number {
const profile = deviceState.currentProfile;
if (profile.deviceType === DeviceType.WATCH) return 2;
if (profile.deviceType === DeviceType.CAR) return Math.min(this.span * 2, 24);
return this.span;
}
build() {
GridCol({ span: this.getResponsiveSpan(), offset: this.offset }) {
this.content()
}
}
}
五、差异化组件实现
5.1 自适应按钮
// entry/src/main/ets/components/AdaptiveButton.ets
// 龍魂 · 自适应按钮
import { DeviceType, deviceState } from '../models/DeviceModel';
@Component
export struct AdaptiveButton {
@Prop label: string = '';
@Prop icon: string = '';
@Prop type: 'primary' | 'secondary' | 'danger' = 'primary';
@Prop disabled: boolean = false;
@Event onClick: () => void = () => {};
// 获取按钮高度
getButtonHeight(): number {
const device = deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 36;
case DeviceType.CAR: return 64;
case DeviceType.TV: return 72;
default: return 44;
}
}
// 获取字体大小
getFontSize(): number {
const device = deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 12;
case DeviceType.CAR: return 18;
case DeviceType.TV: return 20;
default: return 14;
}
}
// 获取圆角
getBorderRadius(): number {
const device = deviceState.currentProfile.deviceType;
if (device === DeviceType.WATCH && deviceState.currentProfile.isRound) return 999;
return 8;
}
// 获取背景色
getBgColor(): string {
switch (this.type) {
case 'primary': return '#c41e3a';
case 'secondary': return '#333';
case 'danger': return '#ff0000';
default: return '#c41e3a';
}
}
build() {
Button() {
Row() {
if (this.icon) {
Text(this.icon)
.fontSize(this.getFontSize())
.margin({ right: 8 })
}
Text(this.label)
.fontSize(this.getFontSize())
.fontColor('#fff')
}
}
.type(ButtonType.Normal)
.backgroundColor(this.getBgColor())
.borderRadius(this.getBorderRadius())
.height(this.getButtonHeight())
.padding({ left: 16, right: 16 })
.enabled(!this.disabled)
.onClick(() => { this.onClick(); })
}
}
5.2 自适应卡片
// entry/src/main/ets/components/AdaptiveCard.ets
// 龍魂 · 自适应卡片
import { DeviceType, deviceState } from '../models/DeviceModel';
@Component
export struct AdaptiveCard {
@Prop title: string = '';
@Prop subtitle: string = '';
@Prop icon: string = '';
@Prop badge: string = '';
@BuilderParam content: () => void;
@Event onClick: () => void = () => {};
getPadding(): number {
const device = deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 8;
case DeviceType.CAR: return 24;
case DeviceType.TV: return 32;
default: return 16;
}
}
getBorderRadius(): number {
const device = deviceState.currentProfile.deviceType;
if (device === DeviceType.WATCH && deviceState.currentProfile.isRound) return 16;
return 8;
}
getTitleSize(): number {
const device = deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 12;
case DeviceType.CAR: return 18;
case DeviceType.TV: return 22;
default: return 14;
}
}
getSubtitleSize(): number {
const device = deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 10;
case DeviceType.CAR: return 14;
case DeviceType.TV: return 16;
default: return 12;
}
}
build() {
Column() {
// 头部
if (this.title || this.icon) {
Row() {
if (this.icon) {
Text(this.icon)
.fontSize(this.getTitleSize())
.margin({ right: 8 })
}
Column() {
Text(this.title)
.fontSize(this.getTitleSize())
.fontWeight(FontWeight.Bold)
.fontColor('#fff')
if (this.subtitle) {
Text(this.subtitle)
.fontSize(this.getSubtitleSize())
.fontColor('#888')
.margin({ top: 2 })
}
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
if (this.badge) {
Text(this.badge)
.fontSize(11)
.fontColor('#fff')
.backgroundColor('#c41e3a')
.borderRadius(999)
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
}
}
.width('100%')
.padding({ bottom: this.getPadding() / 2 })
}
// 内容
this.content()
}
.width('100%')
.padding(this.getPadding())
.backgroundColor('#1a1a1a')
.borderRadius(this.getBorderRadius())
.onClick(() => { this.onClick(); })
}
}
5.3 自适应列表
// entry/src/main/ets/components/AdaptiveList.ets
// 龍魂 · 自适应列表
import { DeviceType, deviceState } from '../models/DeviceModel';
@Component
export struct AdaptiveList {
@Prop items: ListItemData[] = [];
@Prop showIcon: boolean = true;
@Prop showBadge: boolean = true;
@Event onItemClick: (item: ListItemData) => void = () => {};
@Event onItemLongPress: (item: ListItemData) => void = () => {};
getItemHeight(): number {
const device = deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 40;
case DeviceType.CAR: return 80;
case DeviceType.TV: return 96;
default: return 56;
}
}
getFontSize(): number {
const device = deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 12;
case DeviceType.CAR: return 18;
case DeviceType.TV: return 20;
default: return 14;
}
}
getIconSize(): number {
const device = deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 20;
case DeviceType.CAR: return 32;
case DeviceType.TV: return 40;
default: return 24;
}
}
build() {
List() {
ForEach(this.items, (item: ListItemData) => {
ListItem() {
Row() {
if (this.showIcon && item.icon) {
Text(item.icon)
.fontSize(this.getIconSize())
.margin({ right: 12 })
}
Column() {
Text(item.title)
.fontSize(this.getFontSize())
.fontWeight(FontWeight.Medium)
.fontColor('#fff')
if (item.subtitle) {
Text(item.subtitle)
.fontSize(this.getFontSize() - 2)
.fontColor('#888')
.margin({ top: 2 })
}
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
if (this.showBadge && item.badge) {
Text(item.badge)
.fontSize(11)
.fontColor('#fff')
.backgroundColor('#c41e3a')
.borderRadius(999)
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
}
if (item.arrow !== false) {
Text('›')
.fontSize(this.getFontSize() + 4)
.fontColor('#666')
.margin({ left: 8 })
}
}
.width('100%')
.height(this.getItemHeight())
.padding({ left: 16, right: 16 })
.backgroundColor('#0a0a0a')
}
.onClick(() => { this.onItemClick(item); })
.onLongPress(() => { this.onItemLongPress(item); })
}, (item: ListItemData) => item.id)
}
.width('100%')
.divider({ strokeWidth: 1, color: '#222' })
.scrollBar(BarState.Auto)
.edgeEffect(EdgeEffect.Spring)
}
}
export interface ListItemData {
id: string;
title: string;
subtitle?: string;
icon?: string;
badge?: string;
arrow?: boolean;
}
六、主页面实现
6.1 页面入口(entry/src/main/ets/pages/DeviceAdaptivePage.ets)
// entry/src/main/ets/pages/DeviceAdaptivePage.ets
// 龍魂 · 多设备适配主页面 · ArkTS
import { DeviceType, Breakpoint, DeviceProfile, LayoutStrategy, deviceState, MediaQuery } from '../models/DeviceModel';
import { GridContainer, GridCol } from '../components/GridContainer';
import { AdaptiveButton } from '../components/AdaptiveButton';
import { AdaptiveCard } from '../components/AdaptiveCard';
import { AdaptiveList, ListItemData } from '../components/AdaptiveList';
import { DeviceDatabase } from '../database/DeviceDatabase';
@Entry
@Component
struct DeviceAdaptivePage {
@StorageLink('deviceState') deviceState: DeviceState = deviceState;
@State private selectedTab: number = 0;
@State private showDeviceInfo: boolean = false;
aboutToAppear() {
this.deviceState.init();
DeviceDatabase.init();
this.listenWindowChange();
}
// 监听窗口变化
listenWindowChange(): void {
// 使用鸿蒙 Window API 监听尺寸变化
// window.getLastWindow(getContext()).then(win => {
// win.on('windowSizeChange', (data) => {
// this.deviceState.onWindowChange(data.width, data.height);
// });
// });
}
build() {
Column() {
this.HeaderBuilder()
this.DeviceInfoBuilder()
this.ContentBuilder()
this.FooterBuilder()
}
.width('100%')
.height('100%')
.backgroundColor('#0a0a0a')
}
6.2 标题栏
@Builder
HeaderBuilder() {
Row() {
Text('🐉')
.fontSize(this.getHeaderIconSize())
.margin({ right: 8 })
Column() {
Text('龍魂设备适配')
.fontSize(this.getHeaderTitleSize())
.fontWeight(FontWeight.Bold)
.fontColor('#c41e3a')
Text(`${this.getDeviceLabel()} · ${this.deviceState.currentProfile.breakpoint.toUpperCase()}`)
.fontSize(this.getHeaderSubtitleSize())
.fontColor('#666')
}
.alignItems(HorizontalAlign.Start)
Blank()
// 设备信息切换
Button() {
Text(this.showDeviceInfo ? '✕' : 'ℹ️')
.fontSize(20)
}
.type(ButtonType.Circle)
.backgroundColor('#1a1a1a')
.width(40)
.height(40)
.onClick(() => {
this.showDeviceInfo = !this.showDeviceInfo;
})
}
.width('100%')
.height(this.getHeaderHeight())
.padding({ left: 16, right: 16 })
.backgroundColor('#1a1a1a')
.border({ width: { bottom: 1 }, color: '#333' })
}
// 响应式标题栏尺寸
getHeaderHeight(): number {
return this.deviceState.currentStrategy.headerHeight;
}
getHeaderIconSize(): number {
const device = this.deviceState.currentProfile.deviceType;
return device === DeviceType.WATCH ? 20 : 28;
}
getHeaderTitleSize(): number {
const device = this.deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 14;
case DeviceType.CAR: return 24;
case DeviceType.TV: return 28;
default: return 20;
}
}
getHeaderSubtitleSize(): number {
const device = this.deviceState.currentProfile.deviceType;
switch (device) {
case DeviceType.WATCH: return 9;
case DeviceType.CAR: return 14;
case DeviceType.TV: return 16;
default: return 12;
}
}
getDeviceLabel(): string {
const device = this.deviceState.currentProfile.deviceType;
const labels: Record<DeviceType, string> = {
[DeviceType.PHONE]: '📱 手机',
[DeviceType.FOLDABLE]: '📱 折叠屏',
[DeviceType.TABLET]: '💻 平板',
[DeviceType.WATCH]: '⌚ 手表',
[DeviceType.CAR]: '🚗 车机',
[DeviceType.TV]: '📺 智慧屏',
[DeviceType.IOT]: '🔌 IoT'
};
return labels[device] || '📱 手机';
}
6.3 设备信息面板
@Builder
DeviceInfoBuilder() {
if (this.showDeviceInfo) {
Column() {
GridRow({ columns: this.deviceState.currentProfile.getGridColumns(), gutter: 8 }) {
GridCol({ span: this.getInfoSpan() }) {
this.InfoItem('设备类型', this.getDeviceLabel())
}
GridCol({ span: this.getInfoSpan() }) {
this.InfoItem('断点', this.deviceState.currentProfile.breakpoint.toUpperCase())
}
GridCol({ span: this.getInfoSpan() }) {
this.InfoItem('屏幕', `${this.deviceState.windowWidth}x${this.deviceState.windowHeight}`)
}
GridCol({ span: this.getInfoSpan() }) {
this.InfoItem('方向', this.deviceState.currentProfile.orientation === 'landscape' ? '横屏' : '竖屏')
}
GridCol({ span: this.getInfoSpan() }) {
this.InfoItem('交互', this.getInteractionLabel())
}
GridCol({ span: this.getInfoSpan() }) {
this.InfoItem('栅格', `${this.deviceState.currentProfile.getGridColumns()}列`)
}
}
.width('100%')
.padding(12)
}
.width('100%')
.backgroundColor('#1a1a1a')
.border({ width: { bottom: 1 }, color: '#333' })
}
}
getInfoSpan(): number {
const cols = this.deviceState.currentProfile.getGridColumns();
if (cols >= 12) return 4;
if (cols >= 8) return 4;
return cols;
}
getInteractionLabel(): string {
const mode = this.deviceState.currentProfile.interactionMode;
const labels: Record<string, string> = {
'touch': '👆 触摸',
'voice': '🎤 语音',
'knob': '🔘 旋钮',
'gesture': '👋 手势',
'remote': '📱 遥控',
'steering': '🎮 方向盘'
};
return labels[mode] || '👆 触摸';
}
@Builder
InfoItem(label: string, value: string) {
Column() {
Text(label)
.fontSize(11)
.fontColor('#666')
Text(value)
.fontSize(13)
.fontWeight(FontWeight.Medium)
.fontColor('#fff')
.margin({ top: 4 })
}
.padding(8)
.backgroundColor('#0a0a0a')
.borderRadius(8)
.alignItems(HorizontalAlign.Start)
}
6.4 内容区(六端演示)
@Builder
ContentBuilder() {
Scroll() {
Column() {
// 自适应按钮演示
this.ButtonDemoBuilder()
// 自适应卡片演示
this.CardDemoBuilder()
// 自适应列表演示
this.ListDemoBuilder()
// 栅格布局演示
this.GridDemoBuilder()
}
.width('100%')
.padding({ bottom: 24 })
}
.width('100%')
.layoutWeight(1)
.scrollBar(BarState.Auto)
}
// 按钮演示
@Builder
ButtonDemoBuilder() {
Column() {
Text('自适应按钮')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#fff')
.margin({ left: 16, top: 16, bottom: 12 })
Row() {
AdaptiveButton({
label: '主要按钮',
icon: '✓',
type: 'primary',
onClick: () => { console.info('主要按钮点击'); }
})
AdaptiveButton({
label: '次要按钮',
type: 'secondary',
onClick: () => { console.info('次要按钮点击'); }
})
if (!MediaQuery.isWatch()) {
AdaptiveButton({
label: '危险按钮',
type: 'danger',
onClick: () => { console.info('危险按钮点击'); }
})
}
}
.width('100%')
.padding({ left: 16, right: 16 })
.justifyContent(FlexAlign.SpaceAround)
}
.width('100%')
.margin({ top: 8 })
}
// 卡片演示
@Builder
CardDemoBuilder() {
Column() {
Text('自适应卡片')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#fff')
.margin({ left: 16, top: 16, bottom: 12 })
GridRow({ columns: this.deviceState.currentProfile.getGridColumns(), gutter: 12 }) {
GridCol({ span: MediaQuery.isWatch() ? 2 : MediaQuery.isTV() ? 8 : 4 }) {
AdaptiveCard({
title: '项目进度',
subtitle: '本周完成率',
icon: '📊',
badge: '85%',
content: () => {
Column() {
Text('85%')
.fontSize(MediaQuery.isCar() ? 32 : 24)
.fontWeight(FontWeight.Bold)
.fontColor('#00ff00')
Text('SPI: 1.02')
.fontSize(12)
.fontColor('#888')
}
.alignItems(HorizontalAlign.Start)
}
})
}
GridCol({ span: MediaQuery.isWatch() ? 2 : MediaQuery.isTV() ? 8 : 4 }) {
AdaptiveCard({
title: '工时统计',
subtitle: '本周累计',
icon: '⏱️',
badge: '40h',
content: () => {
Column() {
Text('40h')
.fontSize(MediaQuery.isCar() ? 32 : 24)
.fontWeight(FontWeight.Bold)
.fontColor('#ffcc00')
Text('加班: 4h')
.fontSize(12)
.fontColor('#888')
}
.alignItems(HorizontalAlign.Start)
}
})
}
GridCol({ span: MediaQuery.isWatch() ? 2 : MediaQuery.isTV() ? 8 : 4 }) {
AdaptiveCard({
title: '风险预警',
subtitle: '待处理',
icon: '⚠️',
badge: '3',
content: () => {
Column() {
Text('3项')
.fontSize(MediaQuery.isCar() ? 32 : 24)
.fontWeight(FontWeight.Bold)
.fontColor('#ff0000')
Text('高风险: 1')
.fontSize(12)
.fontColor('#888')
}
.alignItems(HorizontalAlign.Start)
}
})
}
}
.width('100%')
.padding({ left: 16, right: 16 })
}
.width('100%')
.margin({ top: 8 })
}
// 列表演示
@Builder
ListDemoBuilder() {
Column() {
Text('自适应列表')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#fff')
.margin({ left: 16, top: 16, bottom: 12 })
AdaptiveList({
items: [
{ id: '1', title: '里程碑时间线', subtitle: '项目进度追踪', icon: '📊', badge: '进行中' },
{ id: '2', title: '印章管理', subtitle: '用印登记审批', icon: '🔏', badge: '在库' },
{ id: '3', title: '周报管理', subtitle: 'Weekly Review', icon: '📝', badge: '已提交' },
{ id: '4', title: '设备适配', subtitle: '多端统一渲染', icon: '📱', badge: '当前' },
{ id: '5', title: '数据看板', subtitle: '统计可视化', icon: '📈', badge: '更新' }
],
onItemClick: (item) => {
console.info(`[龍魂] 点击: ${item.title}`);
}
})
.padding({ left: 16, right: 16 })
}
.width('100%')
.margin({ top: 8 })
}
// 栅格演示
@Builder
GridDemoBuilder() {
Column() {
Text(`栅格布局 (${this.deviceState.currentProfile.getGridColumns()}列)`)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#fff')
.margin({ left: 16, top: 16, bottom: 12 })
GridRow({ columns: this.deviceState.currentProfile.getGridColumns(), gutter: 8 }) {
ForEach([1, 2, 3, 4, 5, 6], (num: number) => {
GridCol({ span: this.getGridItemSpan() }) {
Column() {
Text(`${num}`)
.fontSize(MediaQuery.isTV() ? 24 : 18)
.fontWeight(FontWeight.Bold)
.fontColor('#fff')
Text(`${this.getGridItemSpan()}列`)
.fontSize(11)
.fontColor('#888')
}
.width('100%')
.height(MediaQuery.isWatch() ? 40 : MediaQuery.isCar() ? 80 : 60)
.backgroundColor('#1a1a1a')
.borderRadius(8)
.justifyContent(FlexAlign.Center)
}
})
}
.width('100%')
.padding({ left: 16, right: 16 })
}
.width('100%')
.margin({ top: 8 })
}
getGridItemSpan(): number {
const cols = this.deviceState.currentProfile.getGridColumns();
if (cols >= 24) return 4;
if (cols >= 12) return 3;
if (cols >= 8) return 2;
return cols;
}
6.5 底部操作栏
@Builder
FooterBuilder() {
Row() {
Column() {
Text('🐉').fontSize(20)
Text(`UID:${MASTER_UID}`).fontSize(10).fontColor('#666')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
Row() {
Button() {
Text('📱 模拟手机').fontSize(12).fontColor('#fff')
}
.type(ButtonType.Normal).backgroundColor('#333').borderRadius(8)
.padding({ left: 12, right: 12, top: 6, bottom: 6 }).margin({ right: 8 })
.onClick(() => { this.simulateDevice(DeviceType.PHONE, 360, 780); })
Button() {
Text('⌚ 模拟手表').fontSize(12).fontColor('#fff')
}
.type(ButtonType.Normal).backgroundColor('#333').borderRadius(8)
.padding({ left: 12, right: 12, top: 6, bottom: 6 }).margin({ right: 8 })
.onClick(() => { this.simulateDevice(DeviceType.WATCH, 180, 180); })
Button() {
Text('🚗 模拟车机').fontSize(12).fontColor('#fff')
}
.type(ButtonType.Normal).backgroundColor('#333').borderRadius(8)
.padding({ left: 12, right: 12, top: 6, bottom: 6 }).margin({ right: 8 })
.onClick(() => { this.simulateDevice(DeviceType.CAR, 1280, 720); })
Button() {
Text('📺 模拟TV').fontSize(12).fontColor('#fff')
}
.type(ButtonType.Normal).backgroundColor('#333').borderRadius(8)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
.onClick(() => { this.simulateDevice(DeviceType.TV, 1920, 1080); })
}
}
.width('100%')
.height(this.deviceState.currentStrategy.footerHeight || 56)
.padding({ left: 16, right: 16 })
.backgroundColor('#1a1a1a')
.border({ width: { top: 1 }, color: '#333' })
}
simulateDevice(type: DeviceType, width: number, height: number): void {
this.deviceState.onWindowChange(width, height);
console.info(`[龍魂] 模拟设备: ${type} ${width}x${height}`);
}
}
const MASTER_DNA = "ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️";
const MASTER_UID = "9622";
七、组件清单
| 文件 | 路径 | 说明 |
|---|---|---|
| 数据模型 | entry/src/main/ets/models/DeviceModel.ets |
DeviceProfile/LayoutStrategy/DeviceState |
| 响应式装饰器 | entry/src/main/ets/decorators/Responsive.ets |
@Responsive/@DeviceSpecific/MediaQuery |
| 栅格容器 | entry/src/main/ets/components/GridContainer.ets |
GridContainer/GridCol |
| 自适应按钮 | entry/src/main/ets/components/AdaptiveButton.ets |
六端差异化按钮 |
| 自适应卡片 | entry/src/main/ets/components/AdaptiveCard.ets |
六端差异化卡片 |
| 自适应列表 | entry/src/main/ets/components/AdaptiveList.ets |
六端差异化列表 |
| 主页面 | entry/src/main/ets/pages/DeviceAdaptivePage.ets |
完整UI演示 |
| 数据库 | entry/src/main/ets/database/DeviceDatabase.ets |
设备配置持久化 |
八、鸿蒙特性使用
| 特性 | 用途 | API |
|---|---|---|
| ArkTS声明式UI | 界面构建 | @Component @Entry |
| 状态管理 | 数据响应 | @State @Observed @StorageLink |
| 栅格布局 | 响应式网格 | GridRow GridCol |
| 媒体查询 | 断点检测 | mediaQuery |
| 窗口管理 | 尺寸监听 | window on('windowSizeChange') |
| 设备信息 | 设备类型识别 | deviceInfo |
| 显示管理 | 屏幕参数 | display |
| 分布式 | 多端协同 | distributedObject |
| 流转 | 跨设备迁移 | continuationManager |
| 国密算法 | 数据签名 | cryptoFramework SM2/SM3 |
九、六端适配对照表
| 维度 | 手表 | 手机 | 平板 | 折叠屏 | 车机 | 智慧屏 |
|---|---|---|---|---|---|---|
| 屏幕尺寸 | 1-2" | 6-7" | 8-12" | 6-8"/展开 | 10-15" | 55-85" |
| 断点 | xs | sm | md | md-lg | xl | xl |
| 栅格列数 | 2 | 4 | 8 | 12 | 24 | 24 |
| 按钮高度 | 36dp | 44dp | 48dp | 48dp | 64dp | 72dp |
| 列表项高 | 40dp | 56dp | 64dp | 64dp | 80dp | 96dp |
| 字体缩放 | 0.8x | 1.0x | 1.0x | 1.0x | 1.5x | 1.8x |
| 触控目标 | 36dp | 44dp | 44dp | 44dp | 64dp | 48dp |
| 交互模式 | 旋钮/触摸 | 触摸 | 触摸 | 触摸 | 语音/方向盘 | 遥控/语音 |
| 圆角 | 圆形/8 | 8 | 8 | 8 | 12 | 8 |
| 动画时长 | 200ms | 300ms | 300ms | 300ms | 400ms | 500ms |
| 侧边栏 | 无 | 无 | 240dp | 240dp | 320dp | 400dp |
| 页签栏 | 无 | 56dp | 无 | 无 | 无 | 无 |
| 安全区域 | 无 | 刘海 | 无 | 无 | 无 | 无 |
| 分布式 | 弱 | 强 | 强 | 强 | 中 | 中 |
十、设计原则
| 原则 | 说明 | 实现 |
|---|---|---|
| 内容优先 | 根据内容决定布局,非设备决定 | 断点响应系统 |
| 触控友好 | 最小触控区域44dp,车机64dp | getMinTouchTarget() |
| 信息密度 | 手表精简,TV放大,保持可读性 | 字体缩放+间距调整 |
| 交互一致 | 同一功能在不同设备交互方式一致 | 交互模式映射 |
| 状态同步 | 跨设备状态实时同步 | 分布式软总线 |
| 渐进增强 | 基础功能全端支持,高级功能按需 | 能力检测+降级 |
十一、龍魂标识
| 位置 | 内容 |
|---|---|
| 应用名称 | 龍魂设备适配 |
| 标题栏 | 🐉 龍魂设备适配 |
| 底部标识 | UID:9622 |
| 数据签名 | SM3-哈希 |
| DNA | ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️ |
🐉 龍魂 · 鸿蒙 ArkTS 实战:多设备形态适配 交付完成
DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼♀️❤️♾️
UID: 9622
确认码: #CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z
时间: 2026-07-14
模块: 8核心文件
特性: 设备感知 · 断点响应 · 栅格布局 · 原子化组件 · 六端适配 · 交互模式映射 · 分布式状态同步 · 国密签名
更多推荐


所有评论(0)