71.[HarmonyOS NEXT 实战案例十] 电子书网格布局(上)

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

效果演示

1. 概述

本教程将详细讲解如何使用HarmonyOS NEXT中的GridRow和GridCol组件实现电子书网格布局。通过网格布局,我们可以以美观、规整的方式展示各种电子书信息,为用户提供良好的浏览体验。

本教程将涵盖以下内容:

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

2. 数据结构设计

首先,我们需要定义电子书的数据结构,包含电子书的基本信息:

typescript 复制代码
// 电子书数据类型
export interface BookType {
  id: number;         // 书籍ID
  title: string;      // 书名
  cover: Resource;    // 封面图片
  author: string;     // 作者
  category: string;   // 分类
  rating: number;     // 评分
  price: number;      // 价格
  isFree: boolean;    // 是否免费
  isNew: boolean;     // 是否新书
  description: string; // 简介
}

3. 数据准备

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

typescript 复制代码
// 电子书数据
private books: BookType[] = [
  {
    id: 1,
    title: '人工智能导论',
    cover: $r('app.media.book_ai'),
    author: '李明',
    category: '计算机',
    rating: 4.8,
    price: 68,
    isFree: false,
    isNew: true,
    description: '本书全面介绍了人工智能的基本概念、发展历史、核心技术和应用领域,适合人工智能初学者阅读。'
  },
  {
    id: 2,
    title: '数据结构与算法',
    cover: $r('app.media.book_algorithm'),
    author: '张伟',
    category: '计算机',
    rating: 4.9,
    price: 78,
    isFree: false,
    isNew: false,
    description: '本书详细讲解了常用的数据结构和算法,包括数组、链表、栈、队列、树、图、排序和搜索算法等。'
  },
  {
    id: 3,
    title: '深度学习实战',
    cover: $r('app.media.book_deep_learning'),
    author: '王芳',
    category: '计算机',
    rating: 4.7,
    price: 88,
    isFree: false,
    isNew: true,
    description: '本书通过实际案例讲解深度学习的原理和应用,包括神经网络、卷积神经网络、循环神经网络等。'
  },
  {
    id: 4,
    title: '经济学原理',
    cover: $r('app.media.book_economics'),
    author: '刘强',
    category: '经济',
    rating: 4.6,
    price: 0,
    isFree: true,
    isNew: false,
    description: '本书介绍了经济学的基本原理和概念,包括供需关系、市场结构、宏观经济政策等。'
  },
  {
    id: 5,
    title: '心理学入门',
    cover: $r('app.media.book_psychology'),
    author: '陈静',
    category: '心理',
    rating: 4.5,
    price: 58,
    isFree: false,
    isNew: false,
    description: '本书介绍了心理学的基本理论和研究方法,包括认知心理学、发展心理学、社会心理学等。'
  },
  {
    id: 6,
    title: '现代文学赏析',
    cover: $r('app.media.book_literature'),
    author: '赵红',
    category: '文学',
    rating: 4.4,
    price: 0,
    isFree: true,
    isNew: false,
    description: '本书精选了现代文学作品进行赏析,包括小说、诗歌、散文等多种体裁。'
  },
  {
    id: 7,
    title: '健康生活指南',
    cover: $r('app.media.book_health'),
    author: '孙健',
    category: '健康',
    rating: 4.3,
    price: 48,
    isFree: false,
    isNew: true,
    description: '本书提供了健康生活的指导和建议,包括饮食、运动、心理健康等方面。'
  },
  {
    id: 8,
    title: '旅行摄影技巧',
    cover: $r('app.media.book_photography'),
    author: '吴光',
    category: '摄影',
    rating: 4.7,
    price: 68,
    isFree: false,
    isNew: false,
    description: '本书介绍了旅行摄影的技巧和方法,包括构图、光线、后期处理等方面。'
  },
  {
    id: 9,
    title: '烹饪艺术',
    cover: $r('app.media.book_cooking'),
    author: '郑厨',
    category: '美食',
    rating: 4.8,
    price: 0,
    isFree: true,
    isNew: true,
    description: '本书介绍了烹饪的基本技巧和方法,包括中餐、西餐、甜点等多种菜系。'
  },
  {
    id: 10,
    title: '投资理财基础',
    cover: $r('app.media.book_finance'),
    author: '黄金',
    category: '金融',
    rating: 4.5,
    price: 58,
    isFree: false,
    isNew: false,
    description: '本书介绍了投资理财的基本知识和方法,包括股票、基金、债券等投资工具。'
  }
];

4. 布局实现

4.1 整体布局结构

我们将使用Column作为最外层容器,包含顶部搜索栏、分类标签栏和电子书网格列表:

typescript 复制代码
build() {
  Column() {
    // 顶部搜索栏
    this.SearchBar()
    
    // 分类标签栏
    this.CategoryTabs()
    
    // 电子书网格列表
    this.BookGrid()
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#F5F5F5')
}

4.2 顶部搜索栏

typescript 复制代码
@Builder
private SearchBar() {
  Row() {
    // 搜索框
    Row() {
      Image($r('app.media.ic_search'))
        .width(20)
        .height(20)
        .margin({ right: 8 })
      
      TextInput({ placeholder: '搜索书名、作者' })
        .layoutWeight(1)
        .backgroundColor('transparent')
        .placeholderColor('#999999')
        .fontSize(14)
        .height('100%')
    }
    .width('85%')
    .height(40)
    .backgroundColor(Color.White)
    .borderRadius(20)
    .padding({ left: 12, right: 12 })
    
    // 筛选按钮
    Image($r('app.media.ic_filter'))
      .width(24)
      .height(24)
      .margin({ left: 12 })
  }
  .width('100%')
  .padding({ left: 16, right: 16, top: 12, bottom: 12 })
  .backgroundColor('#673AB7')
}

4.3 分类标签栏

typescript 复制代码
@State currentCategory: string = '推荐';
private categories: string[] = ['推荐', '计算机', '经济', '文学', '心理', '健康', '摄影', '美食', '金融'];

@Builder
private CategoryTabs() {
  Scroll(ScrollDirection.Horizontal) {
    Row() {
      ForEach(this.categories, (category: string) => {
        Text(category)
          .fontSize(14)
          .fontColor(this.currentCategory === category ? '#673AB7' : '#666666')
          .fontWeight(this.currentCategory === category ? FontWeight.Bold : FontWeight.Normal)
          .padding({ left: 16, right: 16, top: 12, bottom: 12 })
          .backgroundColor(this.currentCategory === category ? '#EDE7F6' : 'transparent')
          .borderRadius(16)
          .margin({ right: 8 })
          .onClick(() => {
            this.currentCategory = category;
          })
      })
    }
    .padding({ left: 16, right: 16 })
  }
  .scrollBar(BarState.Off)
  .width('100%')
}

4.4 电子书网格列表

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

typescript 复制代码
@Builder
private BookGrid() {
  Scroll() {
    Column() {
      // 新书上架标题
      Row() {
        Text('新书上架')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
        
        Blank()
        
        Text('查看更多 >')
          .fontSize(14)
          .fontColor('#673AB7')
      }
      .width('100%')
      .padding({ left: 16, right: 16, top: 16, bottom: 8 })
      
      // 新书上架横向滚动列表
      Scroll(ScrollDirection.Horizontal) {
        Row() {
          ForEach(this.getNewBooks(), (book: BookType) => {
            this.NewBookCard(book)
          })
        }
        .padding({ left: 16, right: 16 })
      }
      .scrollBar(BarState.Off)
      .width('100%')
      .margin({ bottom: 16 })
      
      // 免费好书标题
      Row() {
        Text('免费好书')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
        
        Blank()
        
        Text('查看更多 >')
          .fontSize(14)
          .fontColor('#673AB7')
      }
      .width('100%')
      .padding({ left: 16, right: 16, bottom: 8 })
      
      // 免费好书横向滚动列表
      Scroll(ScrollDirection.Horizontal) {
        Row() {
          ForEach(this.getFreeBooks(), (book: BookType) => {
            this.FreeBookCard(book)
          })
        }
        .padding({ left: 16, right: 16 })
      }
      .scrollBar(BarState.Off)
      .width('100%')
      .margin({ bottom: 16 })
      
      // 全部书籍标题
      Row() {
        Text('全部书籍')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
        
        Blank()
        
        Text('查看更多 >')
          .fontSize(14)
          .fontColor('#673AB7')
      }
      .width('100%')
      .padding({ left: 16, right: 16, bottom: 8 })
      
      // 使用GridRow和GridCol实现网格布局
      GridRow({
        columns: { xs: 2, sm: 3, md: 4, lg: 5 },
        gutter: { x: 16, y: 16 }
      }) {
        ForEach(this.books, (book: BookType) => {
          GridCol() {
            this.BookCard(book)
          }
        })
      }
      .width('100%')
      .padding(16)
    }
    .width('100%')
  }
  .scrollBar(BarState.Off)
  .scrollable(ScrollDirection.Vertical)
  .width('100%')
  .layoutWeight(1)
}

4.5 电子书卡片实现

typescript 复制代码
// 新书卡片
@Builder
private NewBookCard(book: BookType) {
  Column() {
    // 书籍封面
    Stack() {
      Image(book.cover)
        .width(120)
        .height(160)
        .borderRadius(8)
        .objectFit(ImageFit.Cover)
      
      // 新书标签
      Text('新书')
        .fontSize(12)
        .fontColor(Color.White)
        .backgroundColor('#FF4081')
        .borderRadius(4)
        .padding({ left: 6, right: 6, top: 2, bottom: 2 })
        .position({ x: 8, y: 8 })
    }
    .width(120)
    .height(160)
    
    // 书名
    Text(book.title)
      .fontSize(14)
      .fontWeight(FontWeight.Medium)
      .fontColor('#333333')
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.Ellipsis })
      .width(120)
      .margin({ top: 8 })
    
    // 作者
    Text(book.author)
      .fontSize(12)
      .fontColor('#666666')
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.Ellipsis })
      .width(120)
      .margin({ top: 4 })
  }
  .margin({ right: 12 })
}

// 免费书籍卡片
@Builder
private FreeBookCard(book: BookType) {
  Column() {
    // 书籍封面
    Stack() {
      Image(book.cover)
        .width(120)
        .height(160)
        .borderRadius(8)
        .objectFit(ImageFit.Cover)
      
      // 免费标签
      Text('免费')
        .fontSize(12)
        .fontColor(Color.White)
        .backgroundColor('#4CAF50')
        .borderRadius(4)
        .padding({ left: 6, right: 6, top: 2, bottom: 2 })
        .position({ x: 8, y: 8 })
    }
    .width(120)
    .height(160)
    
    // 书名
    Text(book.title)
      .fontSize(14)
      .fontWeight(FontWeight.Medium)
      .fontColor('#333333')
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.Ellipsis })
      .width(120)
      .margin({ top: 8 })
    
    // 作者
    Text(book.author)
      .fontSize(12)
      .fontColor('#666666')
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.Ellipsis })
      .width(120)
      .margin({ top: 4 })
  }
  .margin({ right: 12 })
}

// 标准书籍卡片
@Builder
private BookCard(book: BookType) {
  Column() {
    // 书籍封面
    Stack() {
      Image(book.cover)
        .width('100%')
        .aspectRatio(0.75) // 3:4的宽高比
        .borderRadius(8)
        .objectFit(ImageFit.Cover)
      
      // 免费或新书标签
      if (book.isFree) {
        Text('免费')
          .fontSize(12)
          .fontColor(Color.White)
          .backgroundColor('#4CAF50')
          .borderRadius(4)
          .padding({ left: 6, right: 6, top: 2, bottom: 2 })
          .position({ x: 8, y: 8 })
      } else if (book.isNew) {
        Text('新书')
          .fontSize(12)
          .fontColor(Color.White)
          .backgroundColor('#FF4081')
          .borderRadius(4)
          .padding({ left: 6, right: 6, top: 2, bottom: 2 })
          .position({ x: 8, y: 8 })
      }
    }
    .width('100%')
    
    // 书名
    Text(book.title)
      .fontSize(14)
      .fontWeight(FontWeight.Medium)
      .fontColor('#333333')
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.Ellipsis })
      .width('100%')
      .margin({ top: 8 })
    
    // 作者
    Text(book.author)
      .fontSize(12)
      .fontColor('#666666')
      .maxLines(1)
      .textOverflow({ overflow: TextOverflow.Ellipsis })
      .width('100%')
      .margin({ top: 4 })
    
    // 评分和价格
    Row() {
      Row() {
        Image($r('app.media.ic_star'))
          .width(12)
          .height(12)
          .margin({ right: 4 })
        
        Text(book.rating.toString())
          .fontSize(12)
          .fontColor('#FFC107')
      }
      
      Blank()
      
      Text(book.isFree ? '免费' : `¥${book.price}`)
        .fontSize(12)
        .fontWeight(FontWeight.Bold)
        .fontColor(book.isFree ? '#4CAF50' : '#FF5722')
    }
    .width('100%')
    .margin({ top: 4 })
  }
  .width('100%')
  .backgroundColor(Color.White)
  .borderRadius(8)
  .padding(8)
}

4.6 辅助方法

typescript 复制代码
// 获取新书列表
private getNewBooks(): BookType[] {
  return this.books.filter(book => book.isNew);
}

// 获取免费书籍列表
private getFreeBooks(): BookType[] {
  return this.books.filter(book => book.isFree);
}

5. GridRow和GridCol配置详解

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

5.1 GridRow配置

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

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

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

5.2 GridCol配置

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

typescript 复制代码
GridCol() {
  this.BookCard(book)
}

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

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

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

6. 布局效果分析

6.1 响应式布局

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

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

6.2 网格间距

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

6.3 电子书卡片设计

我们设计了三种电子书卡片:

  1. 新书卡片:用于横向滚动的新书上架区域

    • 固定宽度为120像素
    • 显示新书标签
    • 显示书名和作者
  2. 免费书籍卡片:用于横向滚动的免费好书区域

    • 固定宽度为120像素
    • 显示免费标签
    • 显示书名和作者
  3. 标准书籍卡片:用于网格布局的全部书籍区域

    • 宽度自适应
    • 根据书籍属性显示新书或免费标签
    • 显示书名、作者、评分和价格

这种设计使用户能够快速获取电子书的关键信息,便于做出选择。

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 EBookGrid {
  // 电子书数据类型
  interface BookType {
    id: number;         // 书籍ID
    title: string;      // 书名
    cover: Resource;    // 封面图片
    author: string;     // 作者
    category: string;   // 分类
    rating: number;     // 评分
    price: number;      // 价格
    isFree: boolean;    // 是否免费
    isNew: boolean;     // 是否新书
    description: string; // 简介
  }

  // 电子书数据
  private books: BookType[] = [
    {
      id: 1,
      title: '人工智能导论',
      cover: $r('app.media.book_ai'),
      author: '李明',
      category: '计算机',
      rating: 4.8,
      price: 68,
      isFree: false,
      isNew: true,
      description: '本书全面介绍了人工智能的基本概念、发展历史、核心技术和应用领域,适合人工智能初学者阅读。'
    },
    {
      id: 2,
      title: '数据结构与算法',
      cover: $r('app.media.book_algorithm'),
      author: '张伟',
      category: '计算机',
      rating: 4.9,
      price: 78,
      isFree: false,
      isNew: false,
      description: '本书详细讲解了常用的数据结构和算法,包括数组、链表、栈、队列、树、图、排序和搜索算法等。'
    },
    {
      id: 3,
      title: '深度学习实战',
      cover: $r('app.media.book_deep_learning'),
      author: '王芳',
      category: '计算机',
      rating: 4.7,
      price: 88,
      isFree: false,
      isNew: true,
      description: '本书通过实际案例讲解深度学习的原理和应用,包括神经网络、卷积神经网络、循环神经网络等。'
    },
    {
      id: 4,
      title: '经济学原理',
      cover: $r('app.media.book_economics'),
      author: '刘强',
      category: '经济',
      rating: 4.6,
      price: 0,
      isFree: true,
      isNew: false,
      description: '本书介绍了经济学的基本原理和概念,包括供需关系、市场结构、宏观经济政策等。'
    },
    {
      id: 5,
      title: '心理学入门',
      cover: $r('app.media.book_psychology'),
      author: '陈静',
      category: '心理',
      rating: 4.5,
      price: 58,
      isFree: false,
      isNew: false,
      description: '本书介绍了心理学的基本理论和研究方法,包括认知心理学、发展心理学、社会心理学等。'
    },
    {
      id: 6,
      title: '现代文学赏析',
      cover: $r('app.media.book_literature'),
      author: '赵红',
      category: '文学',
      rating: 4.4,
      price: 0,
      isFree: true,
      isNew: false,
      description: '本书精选了现代文学作品进行赏析,包括小说、诗歌、散文等多种体裁。'
    },
    {
      id: 7,
      title: '健康生活指南',
      cover: $r('app.media.book_health'),
      author: '孙健',
      category: '健康',
      rating: 4.3,
      price: 48,
      isFree: false,
      isNew: true,
      description: '本书提供了健康生活的指导和建议,包括饮食、运动、心理健康等方面。'
    },
    {
      id: 8,
      title: '旅行摄影技巧',
      cover: $r('app.media.book_photography'),
      author: '吴光',
      category: '摄影',
      rating: 4.7,
      price: 68,
      isFree: false,
      isNew: false,
      description: '本书介绍了旅行摄影的技巧和方法,包括构图、光线、后期处理等方面。'
    },
    {
      id: 9,
      title: '烹饪艺术',
      cover: $r('app.media.book_cooking'),
      author: '郑厨',
      category: '美食',
      rating: 4.8,
      price: 0,
      isFree: true,
      isNew: true,
      description: '本书介绍了烹饪的基本技巧和方法,包括中餐、西餐、甜点等多种菜系。'
    },
    {
      id: 10,
      title: '投资理财基础',
      cover: $r('app.media.book_finance'),
      author: '黄金',
      category: '金融',
      rating: 4.5,
      price: 58,
      isFree: false,
      isNew: false,
      description: '本书介绍了投资理财的基本知识和方法,包括股票、基金、债券等投资工具。'
    }
  ];

  @State currentCategory: string = '推荐';
  private categories: string[] = ['推荐', '计算机', '经济', '文学', '心理', '健康', '摄影', '美食', '金融'];

  build() {
    Column() {
      // 顶部搜索栏
      this.SearchBar()
      
      // 分类标签栏
      this.CategoryTabs()
      
      // 电子书网格列表
      this.BookGrid()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
  }

  @Builder
  private SearchBar() {
    Row() {
      // 搜索框
      Row() {
        Image($r('app.media.ic_search'))
          .width(20)
          .height(20)
          .margin({ right: 8 })
        
        TextInput({ placeholder: '搜索书名、作者' })
          .layoutWeight(1)
          .backgroundColor('transparent')
          .placeholderColor('#999999')
          .fontSize(14)
          .height('100%')
      }
      .width('85%')
      .height(40)
      .backgroundColor(Color.White)
      .borderRadius(20)
      .padding({ left: 12, right: 12 })
      
      // 筛选按钮
      Image($r('app.media.ic_filter'))
        .width(24)
        .height(24)
        .margin({ left: 12 })
    }
    .width('100%')
    .padding({ left: 16, right: 16, top: 12, bottom: 12 })
    .backgroundColor('#673AB7')
  }

  @Builder
  private CategoryTabs() {
    Scroll(ScrollDirection.Horizontal) {
      Row() {
        ForEach(this.categories, (category: string) => {
          Text(category)
            .fontSize(14)
            .fontColor(this.currentCategory === category ? '#673AB7' : '#666666')
            .fontWeight(this.currentCategory === category ? FontWeight.Bold : FontWeight.Normal)
            .padding({ left: 16, right: 16, top: 12, bottom: 12 })
            .backgroundColor(this.currentCategory === category ? '#EDE7F6' : 'transparent')
            .borderRadius(16)
            .margin({ right: 8 })
            .onClick(() => {
              this.currentCategory = category;
            })
        })
      }
      .padding({ left: 16, right: 16 })
    }
    .scrollBar(BarState.Off)
    .width('100%')
  }

  @Builder
  private BookGrid() {
    Scroll() {
      Column() {
        // 新书上架标题
        Row() {
          Text('新书上架')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor('#333333')
          
          Blank()
          
          Text('查看更多 >')
            .fontSize(14)
            .fontColor('#673AB7')
        }
        .width('100%')
        .padding({ left: 16, right: 16, top: 16, bottom: 8 })
        
        // 新书上架横向滚动列表
        Scroll(ScrollDirection.Horizontal) {
          Row() {
            ForEach(this.getNewBooks(), (book: BookType) => {
              this.NewBookCard(book)
            })
          }
          .padding({ left: 16, right: 16 })
        }
        .scrollBar(BarState.Off)
        .width('100%')
        .margin({ bottom: 16 })
        
        // 免费好书标题
        Row() {
          Text('免费好书')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor('#333333')
          
          Blank()
          
          Text('查看更多 >')
            .fontSize(14)
            .fontColor('#673AB7')
        }
        .width('100%')
        .padding({ left: 16, right: 16, bottom: 8 })
        
        // 免费好书横向滚动列表
        Scroll(ScrollDirection.Horizontal) {
          Row() {
            ForEach(this.getFreeBooks(), (book: BookType) => {
              this.FreeBookCard(book)
            })
          }
          .padding({ left: 16, right: 16 })
        }
        .scrollBar(BarState.Off)
        .width('100%')
        .margin({ bottom: 16 })
        
        // 全部书籍标题
        Row() {
          Text('全部书籍')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .fontColor('#333333')
          
          Blank()
          
          Text('查看更多 >')
            .fontSize(14)
            .fontColor('#673AB7')
        }
        .width('100%')
        .padding({ left: 16, right: 16, bottom: 8 })
        
        // 使用GridRow和GridCol实现网格布局
        GridRow({
          columns: { xs: 2, sm: 3, md: 4, lg: 5 },
          gutter: { x: 16, y: 16 }
        }) {
          ForEach(this.books, (book: BookType) => {
            GridCol() {
              this.BookCard(book)
            }
          })
        }
        .width('100%')
        .padding(16)
      }
      .width('100%')
    }
    .scrollBar(BarState.Off)
    .scrollable(ScrollDirection.Vertical)
    .width('100%')
    .layoutWeight(1)
  }

  // 新书卡片
  @Builder
  private NewBookCard(book: BookType) {
    Column() {
      // 书籍封面
      Stack() {
        Image(book.cover)
          .width(120)
          .height(160)
          .borderRadius(8)
          .objectFit(ImageFit.Cover)
        
        // 新书标签
        Text('新书')
          .fontSize(12)
          .fontColor(Color.White)
          .backgroundColor('#FF4081')
          .borderRadius(4)
          .padding({ left: 6, right: 6, top: 2, bottom: 2 })
          .position({ x: 8, y: 8 })
      }
      .width(120)
      .height(160)
      
      // 书名
      Text(book.title)
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor('#333333')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .width(120)
        .margin({ top: 8 })
      
      // 作者
      Text(book.author)
        .fontSize(12)
        .fontColor('#666666')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .width(120)
        .margin({ top: 4 })
    }
    .margin({ right: 12 })
  }

  // 免费书籍卡片
  @Builder
  private FreeBookCard(book: BookType) {
    Column() {
      // 书籍封面
      Stack() {
        Image(book.cover)
          .width(120)
          .height(160)
          .borderRadius(8)
          .objectFit(ImageFit.Cover)
        
        // 免费标签
        Text('免费')
          .fontSize(12)
          .fontColor(Color.White)
          .backgroundColor('#4CAF50')
          .borderRadius(4)
          .padding({ left: 6, right: 6, top: 2, bottom: 2 })
          .position({ x: 8, y: 8 })
      }
      .width(120)
      .height(160)
      
      // 书名
      Text(book.title)
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor('#333333')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .width(120)
        .margin({ top: 8 })
      
      // 作者
      Text(book.author)
        .fontSize(12)
        .fontColor('#666666')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .width(120)
        .margin({ top: 4 })
    }
    .margin({ right: 12 })
  }

  // 标准书籍卡片
  @Builder
  private BookCard(book: BookType) {
    Column() {
      // 书籍封面
      Stack() {
        Image(book.cover)
          .width('100%')
          .aspectRatio(0.75) // 3:4的宽高比
          .borderRadius(8)
          .objectFit(ImageFit.Cover)
        
        // 免费或新书标签
        if (book.isFree) {
          Text('免费')
            .fontSize(12)
            .fontColor(Color.White)
            .backgroundColor('#4CAF50')
            .borderRadius(4)
            .padding({ left: 6, right: 6, top: 2, bottom: 2 })
            .position({ x: 8, y: 8 })
        } else if (book.isNew) {
          Text('新书')
            .fontSize(12)
            .fontColor(Color.White)
            .backgroundColor('#FF4081')
            .borderRadius(4)
            .padding({ left: 6, right: 6, top: 2, bottom: 2 })
            .position({ x: 8, y: 8 })
        }
      }
      .width('100%')
      
      // 书名
      Text(book.title)
        .fontSize(14)
        .fontWeight(FontWeight.Medium)
        .fontColor('#333333')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .width('100%')
        .margin({ top: 8 })
      
      // 作者
      Text(book.author)
        .fontSize(12)
        .fontColor('#666666')
        .maxLines(1)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .width('100%')
        .margin({ top: 4 })
      
      // 评分和价格
      Row() {
        Row() {
          Image($r('app.media.ic_star'))
            .width(12)
            .height(12)
            .margin({ right: 4 })
          
          Text(book.rating.toString())
            .fontSize(12)
            .fontColor('#FFC107')
        }
        
        Blank()
        
        Text(book.isFree ? '免费' : `¥${book.price}`)
          .fontSize(12)
          .fontWeight(FontWeight.Bold)
          .fontColor(book.isFree ? '#4CAF50' : '#FF5722')
      }
      .width('100%')
      .margin({ top: 4 })
    }
    .width('100%')
    .backgroundColor(Color.White)
    .borderRadius(8)
    .padding(8)
  }

  // 获取新书列表
  private getNewBooks(): BookType[] {
    return this.books.filter(book => book.isNew);
  }

  // 获取免费书籍列表
  private getFreeBooks(): BookType[] {
    return this.books.filter(book => book.isFree);
  }
}

9. 总结

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

主要内容包括:

  • 电子书数据结构设计和数据准备
  • 整体布局实现,包括顶部搜索栏、分类标签栏和电子书网格列表
  • GridRow和GridCol组件的配置和使用
  • 电子书卡片的设计和实现,包括新书卡片、免费书籍卡片和标准书籍卡片
  • 布局效果分析
相关推荐
小雨下雨的雨1 分钟前
Flutter 框架跨平台鸿蒙开发 —— Row & Column 布局之轴线控制艺术
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨34 分钟前
Flutter 框架跨平台鸿蒙开发 —— Center 控件之完美居中之道
flutter·ui·华为·harmonyos·鸿蒙
小雨下雨的雨1 小时前
Flutter 框架跨平台鸿蒙开发 —— Icon 控件之图标交互美学
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨1 小时前
Flutter 框架跨平台鸿蒙开发 —— Placeholder 控件之布局雏形美学
flutter·ui·华为·harmonyos·鸿蒙系统
行者962 小时前
OpenHarmony Flutter弹出菜单组件深度实践:从基础到高级的完整指南
flutter·harmonyos·鸿蒙
小雨下雨的雨3 小时前
Flutter 框架跨平台鸿蒙开发 —— Padding 控件之空间呼吸艺术
flutter·ui·华为·harmonyos·鸿蒙系统
行者963 小时前
Flutter到OpenHarmony:横竖屏自适应布局深度实践
flutter·harmonyos·鸿蒙
小雨下雨的雨3 小时前
Flutter 框架跨平台鸿蒙开发 —— Align 控件之精准定位美学
flutter·ui·华为·harmonyos·鸿蒙
行者964 小时前
Flutter与OpenHarmony集成:跨平台开关组件的实践与优化
flutter·harmonyos·鸿蒙
盐焗西兰花14 小时前
鸿蒙学习实战之路-蓝牙设置完全指南
学习·华为·harmonyos