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

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

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

效果演示

image.png

1. 概述

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

本教程将涵盖以下内容:

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

2. 数据结构设计

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

// 智能设备类型
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. 数据准备

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

// 房间数据
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作为最外层容器,包含顶部状态栏、房间选择栏和设备网格列表:

build() {
   
  Column() {
   
    // 顶部状态栏
    this.StatusBar()

    // 房间选择栏
    this.RoomSelector()

    // 设备网格列表
    this.DeviceGrid()
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#F5F5F5')
}

4.2 顶部状态栏

@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 房间选择栏

@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组件实现设备网格列表:

@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 设备卡片实现

@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 辅助方法

// 获取当前房间的设备列表
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配置

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属性,这意味着每个设备卡片占据一个网格单元。

GridCol() {
   
  this.DeviceCard(device)
}

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

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. 完整代码

@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组件的配置和使用
  • 设备卡片的设计和实现,包括不同类型设备的控制组件
  • 布局效果分析
相关文章
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:外卖App
仓颉语言实战分享,教你如何用仓颉开发外卖App界面。内容包括页面布局、导航栏自定义、搜索框实现、列表模块构建等,附完整代码示例。轻松掌握Scroll、List等组件使用技巧,提升HarmonyOS应用开发能力。
|
2月前
|
移动开发 前端开发 JavaScript
鸿蒙NEXT时代你所不知道的全平台跨端框架:CMP、Kuikly、Lynx、uni-app x等
本篇基于当前各大活跃的跨端框架的现状,对比当前它们的情况和未来的可能,帮助你在选择框架时更好理解它们的特点和差异。
286 0
|
3月前
|
安全 API 开发工具
【HarmonyOS NEXT】一键扫码功能
这些Kit为我们应用开发提升了极大地效率。很多简单的功能,如果不需要太深的定制化需求,直接调用kit提供的API就可以实现,在android或者ios上需要很多代码才能实现的功能效果。
109 0
HarmonyOS NEXT仓颉开发语言实战案例:电影App
周末好!本文分享使用仓颉语言重构ArkTS实现的电影App案例,对比两者在UI布局、组件写法及语法差异。内容包括页面结构、列表分组、分类切换与电影展示等。通过代码演示仓颉在HarmonyOS开发中的应用。##仓颉##ArkTS##HarmonyOS开发
|
3月前
|
容器
HarmonyOS NEXT仓颉开发语言实战案例:健身App
本期分享一个健身App首页的布局实现,顶部采用Stack容器实现重叠背景与偏移效果,列表部分使用List结合Scroll实现可滚动内容。代码结构清晰,适合学习HarmonyOS布局技巧。
HarmonyOS NEXT仓颉开发语言实战案例:小而美的旅行App
本文分享了一个旅行App首页的设计与实现,使用List容器搭配Row、Column布局完成个人信息、功能列表及推荐模块的排版,详细展示了HarmonyOS下的界面构建技巧。
|
10天前
|
存储 缓存 5G
鸿蒙 HarmonyOS NEXT端云一体化开发-云存储篇
本文介绍用户登录后获取昵称、头像的方法,包括通过云端API和AppStorage两种方式,并实现上传头像至云存储及更新用户信息。同时解决图片缓存问题,添加上传进度提示,支持自动登录判断,提升用户体验。
59 0
|
10天前
|
存储 负载均衡 数据库
鸿蒙 HarmonyOS NEXT端云一体化开发-云函数篇
本文介绍基于华为AGC的端云一体化开发流程,涵盖项目创建、云函数开通、应用配置及DevEco集成。重点讲解云函数的编写、部署、调用与传参,并涉及环境变量设置、负载均衡、重试机制与熔断策略等高阶特性,助力开发者高效构建稳定云端服务。
87 0
鸿蒙 HarmonyOS NEXT端云一体化开发-云函数篇
|
10天前
|
存储 JSON 数据建模
鸿蒙 HarmonyOS NEXT端云一体化开发-云数据库篇
云数据库采用存储区、对象类型、对象三级结构,支持灵活的数据建模与权限管理,可通过AGC平台或本地项目初始化,实现数据的增删改查及端侧高效调用。
37 0
|
10天前
|
存储 开发者 容器
鸿蒙 HarmonyOS NEXT星河版APP应用开发-ArkTS面向对象及组件化UI开发使用实例
本文介绍了ArkTS语言中的Class类、泛型、接口、模块化、自定义组件及状态管理等核心概念,并结合代码示例讲解了对象属性、构造方法、继承、静态成员、访问修饰符等内容,同时涵盖了路由管理、生命周期和Stage模型等应用开发关键知识点。
96 0
鸿蒙 HarmonyOS NEXT星河版APP应用开发-ArkTS面向对象及组件化UI开发使用实例