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组件的配置和使用
  • 设备卡片的设计和实现,包括不同类型设备的控制组件
  • 布局效果分析
相关推荐
whysqwhw7 分钟前
鸿蒙AVSession Kit
harmonyos
whysqwhw2 小时前
鸿蒙各种生命周期
harmonyos
whysqwhw3 小时前
鸿蒙音频编码
harmonyos
whysqwhw3 小时前
鸿蒙音频解码
harmonyos
whysqwhw3 小时前
鸿蒙视频解码
harmonyos
whysqwhw3 小时前
鸿蒙视频编码
harmonyos
ajassi20003 小时前
开源 Arkts 鸿蒙应用 开发(十八)通讯--Ble低功耗蓝牙服务器
华为·开源·harmonyos
前端世界4 小时前
在鸿蒙应用中快速接入地图功能:从配置到实战案例全解析
华为·harmonyos
江拥羡橙6 小时前
【基础-判断】HarmonyOS提供了基础的应用加固安全能力,包括混淆、加密和代码签名能力
安全·华为·typescript·harmonyos
前端世界17 小时前
HarmonyOS 实战:用 @Observed + @ObjectLink 玩转多组件实时数据更新
华为·harmonyos