73.[HarmonyOS NEXT 实战案例十一] 智能家居控制面板网格布局(上)

项目已开源,开源地址: gitcode.com/nutpi/Harmo... , 欢迎fork & star

效果演示

1. 概述

本教程将详细讲解如何使用HarmonyOS NEXT中的GridRow和GridCol组件实现智能家居控制面板的网格布局。通过网格布局,我们可以创建一个直观、美观的智能家居控制界面,方便用户管理和控制家中的各种智能设备。

本教程将涵盖以下内容:

  • 智能设备数据结构设计
  • 数据准备
  • 整体布局实现
  • GridRow和GridCol组件配置
  • 设备卡片实现
  • 布局效果分析

2. 数据结构设计

首先,我们需要定义智能设备的数据结构,包含设备的基本信息:

typescript 复制代码
// 智能设备类型
export interface DeviceType {
  id: number;         // 设备ID
  name: string;       // 设备名称
  icon: Resource;     // 设备图标
  type: string;       // 设备类型
  room: string;       // 所在房间
  status: boolean;    // 设备状态(开/关)
  value?: number;     // 设备值(如亮度、温度等)
  color?: string;     // 设备颜色(如灯光颜色)
}

// 房间类型
export interface RoomType {
  id: number;         // 房间ID
  name: string;       // 房间名称
  icon: Resource;     // 房间图标
  deviceCount: number; // 设备数量
}

3. 数据准备

接下来,我们准备一些示例数据用于展示:

typescript 复制代码
// 房间数据
private rooms: RoomType[] = [
  {
    id: 1,
    name: '客厅',
    icon: $r('app.media.ic_living_room'),
    deviceCount: 5
  },
  {
    id: 2,
    name: '卧室',
    icon: $r('app.media.ic_bedroom'),
    deviceCount: 3
  },
  {
    id: 3,
    name: '厨房',
    icon: $r('app.media.ic_kitchen'),
    deviceCount: 4
  },
  {
    id: 4,
    name: '浴室',
    icon: $r('app.media.ic_bathroom'),
    deviceCount: 2
  },
  {
    id: 5,
    name: '书房',
    icon: $r('app.media.ic_study'),
    deviceCount: 3
  }
];

// 智能设备数据
private devices: DeviceType[] = [
  {
    id: 1,
    name: '客厅灯',
    icon: $r('app.media.ic_light'),
    type: 'light',
    room: '客厅',
    status: true,
    value: 80,
    color: '#FFD700'
  },
  {
    id: 2,
    name: '空调',
    icon: $r('app.media.ic_ac'),
    type: 'ac',
    room: '客厅',
    status: true,
    value: 24
  },
  {
    id: 3,
    name: '电视',
    icon: $r('app.media.ic_tv'),
    type: 'tv',
    room: '客厅',
    status: false
  },
  {
    id: 4,
    name: '窗帘',
    icon: $r('app.media.ic_curtain'),
    type: 'curtain',
    room: '客厅',
    status: true,
    value: 100
  },
  {
    id: 5,
    name: '音响',
    icon: $r('app.media.ic_speaker'),
    type: 'speaker',
    room: '客厅',
    status: false
  },
  {
    id: 6,
    name: '卧室灯',
    icon: $r('app.media.ic_light'),
    type: 'light',
    room: '卧室',
    status: false,
    value: 0,
    color: '#FFFFFF'
  },
  {
    id: 7,
    name: '床头灯',
    icon: $r('app.media.ic_bedside_lamp'),
    type: 'light',
    room: '卧室',
    status: true,
    value: 30,
    color: '#FFA07A'
  },
  {
    id: 8,
    name: '卧室空调',
    icon: $r('app.media.ic_ac'),
    type: 'ac',
    room: '卧室',
    status: true,
    value: 26
  },
  {
    id: 9,
    name: '厨房灯',
    icon: $r('app.media.ic_light'),
    type: 'light',
    room: '厨房',
    status: false,
    value: 0,
    color: '#FFFFFF'
  },
  {
    id: 10,
    name: '冰箱',
    icon: $r('app.media.ic_fridge'),
    type: 'fridge',
    room: '厨房',
    status: true,
    value: 4
  },
  {
    id: 11,
    name: '烤箱',
    icon: $r('app.media.ic_oven'),
    type: 'oven',
    room: '厨房',
    status: false
  },
  {
    id: 12,
    name: '洗碗机',
    icon: $r('app.media.ic_dishwasher'),
    type: 'dishwasher',
    room: '厨房',
    status: false
  },
  {
    id: 13,
    name: '浴室灯',
    icon: $r('app.media.ic_light'),
    type: 'light',
    room: '浴室',
    status: false,
    value: 0,
    color: '#FFFFFF'
  },
  {
    id: 14,
    name: '热水器',
    icon: $r('app.media.ic_water_heater'),
    type: 'water_heater',
    room: '浴室',
    status: true,
    value: 45
  },
  {
    id: 15,
    name: '书房灯',
    icon: $r('app.media.ic_light'),
    type: 'light',
    room: '书房',
    status: true,
    value: 70,
    color: '#F5F5DC'
  },
  {
    id: 16,
    name: '台灯',
    icon: $r('app.media.ic_desk_lamp'),
    type: 'light',
    room: '书房',
    status: true,
    value: 50,
    color: '#F0E68C'
  },
  {
    id: 17,
    name: '书房空调',
    icon: $r('app.media.ic_ac'),
    type: 'ac',
    room: '书房',
    status: false,
    value: 0
  }
];

4. 布局实现

4.1 整体布局结构

我们将使用Column作为最外层容器,包含顶部状态栏、房间选择栏和设备网格列表:

typescript 复制代码
build() {
  Column() {
    // 顶部状态栏
    this.StatusBar()
    
    // 房间选择栏
    this.RoomSelector()
    
    // 设备网格列表
    this.DeviceGrid()
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#F5F5F5')
}

4.2 顶部状态栏

typescript 复制代码
@Builder
private StatusBar() {
  Row() {
    Column() {
      Text('智能家居')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
      
      Text('欢迎回家,张先生')
        .fontSize(14)
        .fontColor('#666666')
        .margin({ top: 4 })
    }
    .alignItems(HorizontalAlign.Start)
    
    Blank()
    
    Row() {
      // 添加设备按钮
      Button({ type: ButtonType.Circle }) {
        Image($r('app.media.ic_add'))
          .width(24)
          .height(24)
      }
      .width(40)
      .height(40)
      .backgroundColor('#EEEEEE')
      .margin({ right: 12 })
      
      // 设置按钮
      Button({ type: ButtonType.Circle }) {
        Image($r('app.media.ic_settings'))
          .width(24)
          .height(24)
      }
      .width(40)
      .height(40)
      .backgroundColor('#EEEEEE')
    }
  }
  .width('100%')
  .padding({ left: 16, right: 16, top: 16, bottom: 16 })
  .backgroundColor(Color.White)
}

4.3 房间选择栏

typescript 复制代码
@State currentRoom: string = '全部';

@Builder
private RoomSelector() {
  Scroll(ScrollDirection.Horizontal) {
    Row() {
      // 全部选项
      Column() {
        Image($r('app.media.ic_all_rooms'))
          .width(40)
          .height(40)
          .margin({ bottom: 8 })
        
        Text('全部')
          .fontSize(14)
          .fontColor(this.currentRoom === '全部' ? '#4285F4' : '#666666')
          .fontWeight(this.currentRoom === '全部' ? FontWeight.Bold : FontWeight.Normal)
      }
      .width(80)
      .height(80)
      .padding(8)
      .margin({ right: 12 })
      .borderRadius(12)
      .backgroundColor(this.currentRoom === '全部' ? '#E8F0FE' : Color.White)
      .onClick(() => {
        this.currentRoom = '全部';
      })
      
      // 各个房间选项
      ForEach(this.rooms, (room: RoomType) => {
        Column() {
          Image(room.icon)
            .width(40)
            .height(40)
            .margin({ bottom: 8 })
          
          Text(room.name)
            .fontSize(14)
            .fontColor(this.currentRoom === room.name ? '#4285F4' : '#666666')
            .fontWeight(this.currentRoom === room.name ? FontWeight.Bold : FontWeight.Normal)
        }
        .width(80)
        .height(80)
        .padding(8)
        .margin({ right: 12 })
        .borderRadius(12)
        .backgroundColor(this.currentRoom === room.name ? '#E8F0FE' : Color.White)
        .onClick(() => {
          this.currentRoom = room.name;
        })
      })
    }
    .padding({ left: 16, right: 16 })
  }
  .scrollBar(BarState.Off)
  .width('100%')
  .margin({ top: 16, bottom: 16 })
}

4.4 设备网格列表

这是本教程的核心部分,我们使用GridRow和GridCol组件实现设备网格列表:

typescript 复制代码
@Builder
private DeviceGrid() {
  Column() {
    // 标题栏
    Row() {
      Text(this.currentRoom === '全部' ? '所有设备' : `${this.currentRoom}设备`)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
      
      Blank()
      
      Text(`${this.getFilteredDevices().length}个设备`)
        .fontSize(14)
        .fontColor('#666666')
    }
    .width('100%')
    .padding({ left: 16, right: 16, bottom: 12 })
    
    // 使用GridRow和GridCol实现网格布局
    Scroll() {
      GridRow({
        columns: { xs: 2, sm: 2, md: 3, lg: 4 },
        gutter: { x: 16, y: 16 }
      }) {
        ForEach(this.getFilteredDevices(), (device: DeviceType) => {
          GridCol() {
            this.DeviceCard(device)
          }
        })
      }
      .width('100%')
      .padding(16)
    }
    .scrollBar(BarState.Off)
    .scrollable(ScrollDirection.Vertical)
    .width('100%')
    .layoutWeight(1)
  }
  .width('100%')
  .layoutWeight(1)
  .backgroundColor('#F5F5F5')
  .borderRadius({ topLeft: 24, topRight: 24 })
}

4.5 设备卡片实现

typescript 复制代码
@Builder
private DeviceCard(device: DeviceType) {
  Column() {
    // 设备图标和状态
    Row() {
      Image(device.icon)
        .width(24)
        .height(24)
        .fillColor(device.status ? '#4285F4' : '#999999')
      
      Blank()
      
      Toggle({ type: ToggleType.Switch, isOn: device.status })
        .width(40)
        .height(24)
        .selectedColor('#4285F4')
        .onChange((isOn: boolean) => {
          this.toggleDevice(device.id, isOn);
        })
    }
    .width('100%')
    
    // 设备名称
    Text(device.name)
      .fontSize(16)
      .fontWeight(FontWeight.Medium)
      .fontColor('#333333')
      .margin({ top: 12 })
    
    // 设备状态文本
    Text(this.getDeviceStatusText(device))
      .fontSize(14)
      .fontColor('#666666')
      .margin({ top: 4 })
    
    // 设备控制组件(根据设备类型显示不同控制)
    if (device.type === 'light' && device.status) {
      // 灯光亮度控制
      Column() {
        Row() {
          Image($r('app.media.ic_brightness'))
            .width(16)
            .height(16)
            .margin({ right: 8 })
          
          Text('亮度')
            .fontSize(14)
            .fontColor('#666666')
          
          Blank()
          
          Text(`${device.value}%`)
            .fontSize(14)
            .fontColor('#4285F4')
        }
        .width('100%')
        .margin({ top: 12 })
        
        Slider({
          value: device.value,
          min: 0,
          max: 100,
          step: 1,
          style: SliderStyle.OutSet
        })
          .width('100%')
          .margin({ top: 8 })
          .selectedColor('#4285F4')
          .onChange((value: number) => {
            this.updateDeviceValue(device.id, value);
          })
      }
      .width('100%')
    } else if (device.type === 'ac' && device.status) {
      // 空调温度控制
      Row() {
        Button('-')
          .width(32)
          .height(32)
          .fontSize(16)
          .fontColor('#666666')
          .backgroundColor('#EEEEEE')
          .borderRadius(16)
          .onClick(() => {
            if (device.value > 16) {
              this.updateDeviceValue(device.id, device.value - 1);
            }
          })
        
        Text(`${device.value}°C`)
          .fontSize(16)
          .fontColor('#4285F4')
          .fontWeight(FontWeight.Bold)
          .margin({ left: 12, right: 12 })
        
        Button('+')
          .width(32)
          .height(32)
          .fontSize(16)
          .fontColor('#666666')
          .backgroundColor('#EEEEEE')
          .borderRadius(16)
          .onClick(() => {
            if (device.value < 30) {
              this.updateDeviceValue(device.id, device.value + 1);
            }
          })
      }
      .width('100%')
      .margin({ top: 12 })
      .justifyContent(FlexAlign.Center)
    }
  }
  .width('100%')
  .padding(16)
  .backgroundColor(Color.White)
  .borderRadius(16)
  .onClick(() => {
    this.showDeviceDetail(device);
  })
}

4.6 辅助方法

typescript 复制代码
// 获取当前房间的设备列表
private getFilteredDevices(): DeviceType[] {
  if (this.currentRoom === '全部') {
    return this.devices;
  } else {
    return this.devices.filter(device => device.room === this.currentRoom);
  }
}

// 切换设备状态
private toggleDevice(deviceId: number, status: boolean): void {
  const index = this.devices.findIndex(device => device.id === deviceId);
  if (index !== -1) {
    this.devices[index].status = status;
  }
}

// 更新设备值
private updateDeviceValue(deviceId: number, value: number): void {
  const index = this.devices.findIndex(device => device.id === deviceId);
  if (index !== -1) {
    this.devices[index].value = value;
  }
}

// 获取设备状态文本
private getDeviceStatusText(device: DeviceType): string {
  if (!device.status) {
    return '已关闭';
  }
  
  switch (device.type) {
    case 'light':
      return `亮度 ${device.value}%`;
    case 'ac':
      return `温度 ${device.value}°C`;
    case 'curtain':
      return `开启度 ${device.value}%`;
    case 'fridge':
      return `温度 ${device.value}°C`;
    case 'water_heater':
      return `温度 ${device.value}°C`;
    default:
      return '已开启';
  }
}

// 显示设备详情
private showDeviceDetail(device: DeviceType): void {
  // 在实际应用中,这里会跳转到设备详情页面
  console.info(`显示设备详情:${device.name}`);
}

5. GridRow和GridCol配置详解

在本案例中,我们使用了GridRow和GridCol组件实现网格布局。下面详细解析其配置:

5.1 GridRow配置

typescript 复制代码
GridRow({
  columns: { xs: 2, sm: 2, md: 3, lg: 4 },
  gutter: { x: 16, y: 16 }
})
  • columns:定义不同屏幕尺寸下的列数

    • xs: 2:极小屏幕(如小型手机)显示2列
    • sm: 2:小屏幕(如大型手机)显示2列
    • md: 3:中等屏幕(如平板)显示3列
    • lg: 4:大屏幕(如桌面)显示4列
  • gutter:定义网格间的间距

    • x: 16:水平间距为16像素
    • y: 16:垂直间距为16像素

5.2 GridCol配置

在本案例中,我们使用了默认的GridCol配置,没有指定span属性,这意味着每个设备卡片占据一个网格单元。

typescript 复制代码
GridCol() {
  this.DeviceCard(device)
}

如果需要某些设备卡片占据更多的空间,可以通过span属性进行配置:

typescript 复制代码
GridCol({
  span: { xs: 2, sm: 1, md: 1, lg: 1 }
}) {
  this.DeviceCard(device)
}

这样配置后,在极小屏幕(xs)上,该设备卡片会占据2列,而在其他屏幕尺寸上占据1列。

6. 布局效果分析

6.1 响应式布局

通过GridRow的columns配置,我们实现了响应式布局,使应用能够适应不同屏幕尺寸的设备:

屏幕尺寸 列数 效果
极小屏幕(xs) 2列 每行显示2个设备卡片
小屏幕(sm) 2列 每行显示2个设备卡片
中等屏幕(md) 3列 每行显示3个设备卡片
大屏幕(lg) 4列 每行显示4个设备卡片

6.2 网格间距

通过GridRow的gutter配置,我们设置了网格间的间距为16像素,使布局更加美观、清晰。

6.3 设备卡片设计

我们设计的设备卡片包含以下元素:

  1. 设备图标和开关:显示设备类型和当前状态,用户可以直接切换设备的开关状态
  2. 设备名称:清晰地显示设备的名称
  3. 设备状态文本:显示设备的当前状态,如亮度、温度等
  4. 设备控制组件:根据设备类型显示不同的控制组件,如灯光的亮度滑块、空调的温度控制按钮等

这种设计使用户能够快速了解设备状态,并进行简单的控制操作。

7. GridRow和GridCol组件详解

7.1 GridRow组件

GridRow是HarmonyOS NEXT提供的网格行容器组件,用于创建网格布局。它具有以下主要属性:

属性 类型 描述
columns number | { xs?: number, sm?: number, md?: number, lg?: number, xl?: number, xxl?: number } 当前行的总列数
gutter number | { x?: number, y?: number } 栅格间隔
breakpoints { value: number, reference: BreakpointsReference }[] 自定义断点值

7.2 GridCol组件

GridCol是HarmonyOS NEXT提供的网格列容器组件,用于在GridRow中创建网格列。它具有以下主要属性:

属性 类型 描述
span number | { xs?: number, sm?: number, md?: number, lg?: number, xl?: number, xxl?: number } 列宽度
offset number | { xs?: number, sm?: number, md?: number, lg?: number, xl?: number, xxl?: number } 列偏移量
order number | { xs?: number, sm?: number, md?: number, lg?: number, xl?: number, xxl?: number } 列顺序

8. 完整代码

typescript 复制代码
@Entry
@Component
struct SmartHomeGrid {
  // 智能设备类型
  interface DeviceType {
    id: number;         // 设备ID
    name: string;       // 设备名称
    icon: Resource;     // 设备图标
    type: string;       // 设备类型
    room: string;       // 所在房间
    status: boolean;    // 设备状态(开/关)
    value?: number;     // 设备值(如亮度、温度等)
    color?: string;     // 设备颜色(如灯光颜色)
  }

  // 房间类型
  interface RoomType {
    id: number;         // 房间ID
    name: string;       // 房间名称
    icon: Resource;     // 房间图标
    deviceCount: number; // 设备数量
  }

  // 房间数据
  private rooms: RoomType[] = [
    {
      id: 1,
      name: '客厅',
      icon: $r('app.media.ic_living_room'),
      deviceCount: 5
    },
    {
      id: 2,
      name: '卧室',
      icon: $r('app.media.ic_bedroom'),
      deviceCount: 3
    },
    {
      id: 3,
      name: '厨房',
      icon: $r('app.media.ic_kitchen'),
      deviceCount: 4
    },
    {
      id: 4,
      name: '浴室',
      icon: $r('app.media.ic_bathroom'),
      deviceCount: 2
    },
    {
      id: 5,
      name: '书房',
      icon: $r('app.media.ic_study'),
      deviceCount: 3
    }
  ];

  // 智能设备数据
  @State devices: DeviceType[] = [
    {
      id: 1,
      name: '客厅灯',
      icon: $r('app.media.ic_light'),
      type: 'light',
      room: '客厅',
      status: true,
      value: 80,
      color: '#FFD700'
    },
    {
      id: 2,
      name: '空调',
      icon: $r('app.media.ic_ac'),
      type: 'ac',
      room: '客厅',
      status: true,
      value: 24
    },
    {
      id: 3,
      name: '电视',
      icon: $r('app.media.ic_tv'),
      type: 'tv',
      room: '客厅',
      status: false
    },
    {
      id: 4,
      name: '窗帘',
      icon: $r('app.media.ic_curtain'),
      type: 'curtain',
      room: '客厅',
      status: true,
      value: 100
    },
    {
      id: 5,
      name: '音响',
      icon: $r('app.media.ic_speaker'),
      type: 'speaker',
      room: '客厅',
      status: false
    },
    {
      id: 6,
      name: '卧室灯',
      icon: $r('app.media.ic_light'),
      type: 'light',
      room: '卧室',
      status: false,
      value: 0,
      color: '#FFFFFF'
    },
    {
      id: 7,
      name: '床头灯',
      icon: $r('app.media.ic_bedside_lamp'),
      type: 'light',
      room: '卧室',
      status: true,
      value: 30,
      color: '#FFA07A'
    },
    {
      id: 8,
      name: '卧室空调',
      icon: $r('app.media.ic_ac'),
      type: 'ac',
      room: '卧室',
      status: true,
      value: 26
    },
    {
      id: 9,
      name: '厨房灯',
      icon: $r('app.media.ic_light'),
      type: 'light',
      room: '厨房',
      status: false,
      value: 0,
      color: '#FFFFFF'
    },
    {
      id: 10,
      name: '冰箱',
      icon: $r('app.media.ic_fridge'),
      type: 'fridge',
      room: '厨房',
      status: true,
      value: 4
    },
    {
      id: 11,
      name: '烤箱',
      icon: $r('app.media.ic_oven'),
      type: 'oven',
      room: '厨房',
      status: false
    },
    {
      id: 12,
      name: '洗碗机',
      icon: $r('app.media.ic_dishwasher'),
      type: 'dishwasher',
      room: '厨房',
      status: false
    },
    {
      id: 13,
      name: '浴室灯',
      icon: $r('app.media.ic_light'),
      type: 'light',
      room: '浴室',
      status: false,
      value: 0,
      color: '#FFFFFF'
    },
    {
      id: 14,
      name: '热水器',
      icon: $r('app.media.ic_water_heater'),
      type: 'water_heater',
      room: '浴室',
      status: true,
      value: 45
    },
    {
      id: 15,
      name: '书房灯',
      icon: $r('app.media.ic_light'),
      type: 'light',
      room: '书房',
      status: true,
      value: 70,
      color: '#F5F5DC'
    },
    {
      id: 16,
      name: '台灯',
      icon: $r('app.media.ic_desk_lamp'),
      type: 'light',
      room: '书房',
      status: true,
      value: 50,
      color: '#F0E68C'
    },
    {
      id: 17,
      name: '书房空调',
      icon: $r('app.media.ic_ac'),
      type: 'ac',
      room: '书房',
      status: false,
      value: 0
    }
  ];

  @State currentRoom: string = '全部';

  build() {
    Column() {
      // 顶部状态栏
      this.StatusBar()
      
      // 房间选择栏
      this.RoomSelector()
      
      // 设备网格列表
      this.DeviceGrid()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  @Builder
  private StatusBar() {
    Row() {
      Column() {
        Text('智能家居')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
        
        Text('欢迎回家,张先生')
          .fontSize(14)
          .fontColor('#666666')
          .margin({ top: 4 })
      }
      .alignItems(HorizontalAlign.Start)
      
      Blank()
      
      Row() {
        // 添加设备按钮
        Button({ type: ButtonType.Circle }) {
          Image($r('app.media.ic_add'))
            .width(24)
            .height(24)
        }
        .width(40)
        .height(40)
        .backgroundColor('#EEEEEE')
        .margin({ right: 12 })
        
        // 设置按钮
        Button({ type: ButtonType.Circle }) {
          Image($r('app.media.ic_settings'))
            .width(24)
            .height(24)
        }
        .width(40)
        .height(40)
        .backgroundColor('#EEEEEE')
      }
    }
    .width('100%')
    .padding({ left: 16, right: 16, top: 16, bottom: 16 })
    .backgroundColor(Color.White)
  }

  @Builder
  private RoomSelector() {
    Scroll(ScrollDirection.Horizontal) {
      Row() {
        // 全部选项
        Column() {
          Image($r('app.media.ic_all_rooms'))
            .width(40)
            .height(40)
            .margin({ bottom: 8 })
          
          Text('全部')
            .fontSize(14)
            .fontColor(this.currentRoom === '全部' ? '#4285F4' : '#666666')
            .fontWeight(this.currentRoom === '全部' ? FontWeight.Bold : FontWeight.Normal)
        }
        .width(80)
        .height(80)
        .padding(8)
        .margin({ right: 12 })
        .borderRadius(12)
        .backgroundColor(this.currentRoom === '全部' ? '#E8F0FE' : Color.White)
        .onClick(() => {
          this.currentRoom = '全部';
        })
        
        // 各个房间选项
        ForEach(this.rooms, (room: RoomType) => {
          Column() {
            Image(room.icon)
              .width(40)
              .height(40)
              .margin({ bottom: 8 })
            
            Text(room.name)
              .fontSize(14)
              .fontColor(this.currentRoom === room.name ? '#4285F4' : '#666666')
              .fontWeight(this.currentRoom === room.name ? FontWeight.Bold : FontWeight.Normal)
          }
          .width(80)
          .height(80)
          .padding(8)
          .margin({ right: 12 })
          .borderRadius(12)
          .backgroundColor(this.currentRoom === room.name ? '#E8F0FE' : Color.White)
          .onClick(() => {
            this.currentRoom = room.name;
          })
        })
      }
      .padding({ left: 16, right: 16 })
    }
    .scrollBar(BarState.Off)
    .width('100%')
    .margin({ top: 16, bottom: 16 })
  }

  @Builder
  private DeviceGrid() {
    Column() {
      // 标题栏
      Row() {
        Text(this.currentRoom === '全部' ? '所有设备' : `${this.currentRoom}设备`)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
        
        Blank()
        
        Text(`${this.getFilteredDevices().length}个设备`)
          .fontSize(14)
          .fontColor('#666666')
      }
      .width('100%')
      .padding({ left: 16, right: 16, bottom: 12 })
      
      // 使用GridRow和GridCol实现网格布局
      Scroll() {
        GridRow({
          columns: { xs: 2, sm: 2, md: 3, lg: 4 },
          gutter: { x: 16, y: 16 }
        }) {
          ForEach(this.getFilteredDevices(), (device: DeviceType) => {
            GridCol() {
              this.DeviceCard(device)
            }
          })
        }
        .width('100%')
        .padding(16)
      }
      .scrollBar(BarState.Off)
      .scrollable(ScrollDirection.Vertical)
      .width('100%')
      .layoutWeight(1)
    }
    .width('100%')
    .layoutWeight(1)
    .backgroundColor('#F5F5F5')
    .borderRadius({ topLeft: 24, topRight: 24 })
  }

  @Builder
  private DeviceCard(device: DeviceType) {
    Column() {
      // 设备图标和状态
      Row() {
        Image(device.icon)
          .width(24)
          .height(24)
          .fillColor(device.status ? '#4285F4' : '#999999')
        
        Blank()
        
        Toggle({ type: ToggleType.Switch, isOn: device.status })
          .width(40)
          .height(24)
          .selectedColor('#4285F4')
          .onChange((isOn: boolean) => {
            this.toggleDevice(device.id, isOn);
          })
      }
      .width('100%')
      
      // 设备名称
      Text(device.name)
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .fontColor('#333333')
        .margin({ top: 12 })
      
      // 设备状态文本
      Text(this.getDeviceStatusText(device))
        .fontSize(14)
        .fontColor('#666666')
        .margin({ top: 4 })
      
      // 设备控制组件(根据设备类型显示不同控制)
      if (device.type === 'light' && device.status) {
        // 灯光亮度控制
        Column() {
          Row() {
            Image($r('app.media.ic_brightness'))
              .width(16)
              .height(16)
              .margin({ right: 8 })
            
            Text('亮度')
              .fontSize(14)
              .fontColor('#666666')
            
            Blank()
            
            Text(`${device.value}%`)
              .fontSize(14)
              .fontColor('#4285F4')
          }
          .width('100%')
          .margin({ top: 12 })
          
          Slider({
            value: device.value,
            min: 0,
            max: 100,
            step: 1,
            style: SliderStyle.OutSet
          })
            .width('100%')
            .margin({ top: 8 })
            .selectedColor('#4285F4')
            .onChange((value: number) => {
              this.updateDeviceValue(device.id, value);
            })
        }
        .width('100%')
      } else if (device.type === 'ac' && device.status) {
        // 空调温度控制
        Row() {
          Button('-')
            .width(32)
            .height(32)
            .fontSize(16)
            .fontColor('#666666')
            .backgroundColor('#EEEEEE')
            .borderRadius(16)
            .onClick(() => {
              if (device.value > 16) {
                this.updateDeviceValue(device.id, device.value - 1);
              }
            })
          
          Text(`${device.value}°C`)
            .fontSize(16)
            .fontColor('#4285F4')
            .fontWeight(FontWeight.Bold)
            .margin({ left: 12, right: 12 })
          
          Button('+')
            .width(32)
            .height(32)
            .fontSize(16)
            .fontColor('#666666')
            .backgroundColor('#EEEEEE')
            .borderRadius(16)
            .onClick(() => {
              if (device.value < 30) {
                this.updateDeviceValue(device.id, device.value + 1);
              }
            })
        }
        .width('100%')
        .margin({ top: 12 })
        .justifyContent(FlexAlign.Center)
      }
    }
    .width('100%')
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(16)
    .onClick(() => {
      this.showDeviceDetail(device);
    })
  }

  // 获取当前房间的设备列表
  private getFilteredDevices(): DeviceType[] {
    if (this.currentRoom === '全部') {
      return this.devices;
    } else {
      return this.devices.filter(device => device.room === this.currentRoom);
    }
  }

  // 切换设备状态
  private toggleDevice(deviceId: number, status: boolean): void {
    const index = this.devices.findIndex(device => device.id === deviceId);
    if (index !== -1) {
      this.devices[index].status = status;
    }
  }

  // 更新设备值
  private updateDeviceValue(deviceId: number, value: number): void {
    const index = this.devices.findIndex(device => device.id === deviceId);
    if (index !== -1) {
      this.devices[index].value = value;
    }
  }

  // 获取设备状态文本
  private getDeviceStatusText(device: DeviceType): string {
    if (!device.status) {
      return '已关闭';
    }
    
    switch (device.type) {
      case 'light':
        return `亮度 ${device.value}%`;
      case 'ac':
        return `温度 ${device.value}°C`;
      case 'curtain':
        return `开启度 ${device.value}%`;
      case 'fridge':
        return `温度 ${device.value}°C`;
      case 'water_heater':
        return `温度 ${device.value}°C`;
      default:
        return '已开启';
    }
  }

  // 显示设备详情
  private showDeviceDetail(device: DeviceType): void {
    // 在实际应用中,这里会跳转到设备详情页面
    console.info(`显示设备详情:${device.name}`);
  }
}

9. 总结

本教程详细讲解了如何使用HarmonyOS NEXT中的GridRow和GridCol组件实现智能家居控制面板的网格布局。通过合理的数据结构设计、精心的UI设计和灵活的GridRow配置,我们实现了一个美观、响应式的智能家居控制面板。

主要内容包括:

  • 智能设备数据结构设计和数据准备
  • 整体布局实现,包括顶部状态栏、房间选择栏和设备网格列表
  • GridRow和GridCol组件的配置和使用
  • 设备卡片的设计和实现,包括不同类型设备的控制组件
  • 布局效果分析
相关推荐
Georgewu7 分钟前
【 HarmonyOS 5 入门系列 】鸿蒙HarmonyOS示例项目讲解
harmonyos
libo_20252 小时前
HarmonyOS5 元宇宙3D原子化服务开发实践
harmonyos
半路下车2 小时前
【Harmony OS 5】DevEco Testing重塑教育质量
harmonyos·arkts
90后的晨仔2 小时前
解析鸿蒙 ArkTS 中的 Union 类型与 TypeAliases类型
前端·harmonyos
风浅月明2 小时前
[Harmony]颜色初始化
harmonyos·color
风浅月明2 小时前
[Harmony]网络状态监听
harmonyos·网络状态
半路下车3 小时前
【Harmony OS 5】DevEco Testing在教育领域的应用与实践
harmonyos·产品
simple丶3 小时前
【HarmonyOS Relational Database】鸿蒙关系型数据库
harmonyos·arkts·arkui
哼唧唧_3 小时前
使用 React Native 开发鸿蒙(HarmonyOS)运动健康类应用的系统化准备工作
react native·react.js·harmonyos·harmony os5·运动健康
三掌柜6664 小时前
HarmonyOS开发:显示图片功能详解
华为·harmonyos