118.[HarmonyOS NEXT 实战案例:旅行应用] 基础篇 - 水平分割布局打造旅行规划应用

简介: 在本案例中,我们将使用以下HarmonyOS NEXT组件:| 组件名称 | 功能描述 || ---------- | -------------------------------------- || `RowSplit` | 水平分割布局容器,将界面分为左右两部分 || `Column` | 垂直布局容器,用于垂直排列子组件 || `Row` | 水平布局容器,用于水平排列子组件 || `Scroll` | 滚动容器,用于在有限空间内展示更多内容 || `Image`

[HarmonyOS NEXT 实战案例:旅行应用] 基础篇 - 水平分割布局打造旅行规划应用

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

效果演示

组件概述

在本案例中,我们将使用以下HarmonyOS NEXT组件:

组件名称 功能描述
RowSplit 水平分割布局容器,将界面分为左右两部分
Column 垂直布局容器,用于垂直排列子组件
Row 水平布局容器,用于水平排列子组件
Scroll 滚动容器,用于在有限空间内展示更多内容
Image 图片组件,用于显示景点图片和图标
Text 文本组件,用于显示行程信息和描述
Button 按钮组件,用于添加行程、导航和分享操作
ForEach 循环渲染组件,用于渲染行程列表和活动项

数据模型定义

在实现旅行规划应用之前,我们首先定义了两个接口来描述数据结构:

// 类型定义
interface TripDay {
   
    day: string
    date: string
    location: string
    description: string
    hotel: string
    restaurant: string
    image: Resource
    activities: Activity[]
}

interface Activity {
   
    time: string
    title: string
    icon: Resource
}

这两个接口定义了旅行规划应用所需的数据结构:

  • TripDay:表示一天的行程信息,包含日期、地点、描述、住宿、餐饮、图片和活动列表
  • Activity:表示一个具体的活动项,包含时间、标题和图标

代码实现

外层容器

我们使用RowSplit组件作为最外层容器,将界面分为左右两部分:

RowSplit() {
   
    // 左侧行程列表 (40%)
    Column() {
   
        // 行程列表内容
    }
    .width('40%')
    .backgroundColor('#f1f8e9')
    .padding(10)

    // 右侧详情 (60%)
    Column() {
   
        // 详情内容
    }
    .padding(15)
    .width('60%')
}
.height(600)
.width('100%')

RowSplit设置了600的高度和100%的宽度,确保整个界面有足够的显示空间。左侧的行程列表区域占总宽度的40%,背景色为浅绿色,设置了10的内边距。右侧的详情区域占总宽度的60%,设置了15的内边距。

左侧行程列表

左侧行程列表区域包含标题、行程列表和添加按钮:

Column() {
   
    Text('我的行程')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({
    top: 20, bottom: 15 })

    Scroll() {
   
        Column() {
   
            ForEach(this.tripDays, (item: TripDay, index: number) => {
   
                Column() {
   
                    Row() {
   
                        Text(item.day)
                            .fontSize(16)
                            .fontWeight(FontWeight.Bold)
                            .layoutWeight(1)

                        Text(item.date)
                            .fontSize(12)
                            .fontColor('#666666')
                    }

                    Text(item.location)
                        .fontSize(14)
                        .fontColor('#4CAF50')
                        .margin({
    top: 2 })
                }
                .padding(5)
                .width('100%')
                .backgroundColor(index === this.selectedDay ? '#e8f5e9' : 'transparent')
                .borderRadius(8)
                .onClick(() => this.selectedDay = index)
            })
        }
    }
    .margin({
    bottom: 10 })

    Button('添加新行程')
        .width('80%')
        .margin({
    bottom: 20 })
}

在这个区域中:

  1. 顶部是一个标题文本"我的行程",设置了字体大小、粗细和边距
  2. 中间是一个Scroll滚动容器,包含一个Column,使用ForEach循环渲染行程列表
  3. 每个行程项包含天数、日期和地点信息,设置了点击事件处理函数,点击时更新selectedDay状态
  4. 底部是一个"添加新行程"按钮

右侧详情区域

右侧详情区域包含顶部操作栏、内容区和底部操作按钮:

Column() {
   
    // 顶部操作栏
    Row() {
   
        Text(this.tripDays[this.selectedDay].day + ' · ' + this.tripDays[this.selectedDay].location)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .layoutWeight(1)

        Image(this.showMap ? $r('app.media.big15') : $r('app.media.big14'))
            .width(24)
            .height(24)
            .onClick(() => this.showMap = !this.showMap)
    }
    .padding({
    bottom: 15 })

    // 内容区(条件渲染)
    if (this.showMap) {
   
        // 地图视图
        // ...
    } else {
   
        // 详情视图
        // ...
    }

    // 底部操作按钮
    Row() {
   
        Button('导航')
            .width(120)
            .height(40)

        Button('分享行程')
            .width(120)
            .height(40)
            .margin({
    left: 10 })
    }
    .margin({
    top: 15 })
    .justifyContent(FlexAlign.Center)
}

在这个区域中:

  1. 顶部操作栏显示当前选中的行程信息和一个切换按钮,用于切换地图视图和详情视图
  2. 内容区根据showMap状态条件渲染不同的内容
  3. 底部操作按钮包含"导航"和"分享行程"两个按钮

地图视图

showMaptrue时,显示地图视图:

Column() {
   
    Image($r('app.media.big13'))
        .width('100%')
        .height(300)
        .objectFit(ImageFit.Cover)
        .borderRadius(8)

    Text('地图位置')
        .fontSize(16)
        .margin({
    top: 15 })
}
.height('100%')

地图视图包含一个地图图片和一个位置文本。

详情视图

showMapfalse时,显示详情视图:

Scroll() {
   
    Column() {
   
        // 景点图片
        Image(this.tripDays[this.selectedDay].image)
            .width('100%')
            .height(200)
            .objectFit(ImageFit.Cover)
            .borderRadius(8)
            .margin({
    bottom: 15 })

        // 行程描述
        Text(this.tripDays[this.selectedDay].description)
            .fontSize(16)
            .lineHeight(24)
            .margin({
    bottom: 20 })

        // 活动时间表
        Text('当日行程')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .margin({
    bottom: 10 })

        Column() {
   
            ForEach(this.tripDays[this.selectedDay].activities, (activity: Activity) => {
   
                Row() {
   
                    Image(activity.icon)
                        .width(20)
                        .height(20)
                        .margin({
    right: 15 })

                    Text(activity.time)
                        .fontSize(14)
                        .width(60)

                    Text(activity.title)
                        .fontSize(16)
                }
                .padding(10)
                .margin({
    bottom: 5 })
                .backgroundColor('#f5f5f5')
                .borderRadius(8)
            })
        }
        .margin({
    bottom: 20 })

        // 住宿和餐饮
        Column() {
   
            // 住宿信息
            Row() {
   
                Image($r('app.media.01'))
                    .width(24)
                    .height(24)
                    .margin({
    right: 10 })

                Column() {
   
                    Text('住宿')
                        .fontSize(12)
                        .fontColor('#666666')
                    Text(this.tripDays[this.selectedDay].hotel)
                        .fontSize(16)
                }
            }
            .padding(10)
            .margin({
    bottom: 5 })
            .backgroundColor('#f5f5f5')
            .borderRadius(8)

            // 餐饮信息
            Row() {
   
                Image($r('app.media.02'))
                    .width(24)
                    .height(24)
                    .margin({
    right: 10 })

                Column() {
   
                    Text('餐饮')
                        .fontSize(12)
                        .fontColor('#666666')
                    Text(this.tripDays[this.selectedDay].restaurant)
                        .fontSize(16)
                }
            }
            .padding(10)
            .backgroundColor('#f5f5f5')
            .borderRadius(8)
        }
    }
}

详情视图包含以下内容:

  1. 景点图片:显示当前选中行程的图片
  2. 行程描述:显示当前选中行程的描述文本
  3. 活动时间表:使用ForEach循环渲染当前选中行程的活动列表
  4. 住宿和餐饮信息:显示当前选中行程的住宿和餐饮信息

布局技巧

1. 使用RowSplit实现左右分栏

RowSplit组件是HarmonyOS NEXT中用于实现水平分割布局的容器组件,它可以将界面分为左右两部分,每部分可以设置不同的宽度比例。在本案例中,我们将左侧行程列表区域设置为总宽度的40%,右侧详情区域设置为总宽度的60%。

2. 使用Scroll处理溢出内容

在左侧行程列表和右侧详情视图中,我们都使用了Scroll组件来处理可能的内容溢出情况。这样,当内容超出可视区域时,用户可以通过滚动来查看所有内容。

3. 使用条件渲染切换视图

在右侧详情区域,我们使用条件渲染来切换地图视图和详情视图:

if (this.showMap) {
   
    // 地图视图
    // ...
} else {
   
    // 详情视图
    // ...
}

这样,用户可以通过点击顶部操作栏中的切换按钮来切换不同的视图。

4. 使用ForEach循环渲染列表

在左侧行程列表和右侧详情视图的活动时间表中,我们都使用了ForEach组件来循环渲染列表数据。这样,我们可以根据数据数组的长度动态生成列表项,而不需要手动编写重复的代码。

交互实现

1. 行程选择

在左侧行程列表中,我们为每个行程项添加了点击事件处理函数:

.onClick(() => this.selectedDay = index)

当用户点击一个行程项时,会更新selectedDay状态,从而触发界面更新,显示对应的行程详情。

2. 视图切换

在右侧详情区域的顶部操作栏中,我们为切换按钮添加了点击事件处理函数:

.onClick(() => this.showMap = !this.showMap)

当用户点击切换按钮时,会切换showMap状态,从而触发界面更新,切换地图视图和详情视图。

3. 状态管理

在本案例中,我们使用了@State装饰器来定义组件的状态变量:

@State selectedDay: number = 0
@State showMap: boolean = false
@State tripDays: TripDay[] = [ /* ... */ ]

这些状态变量的变化会触发界面的自动更新,实现响应式的用户界面。

小结

在本教程中,我们学习了如何使用HarmonyOS NEXT的RowSplit组件构建一个旅行规划应用。通过水平分割布局,我们将界面分为左侧行程列表和右侧详情区域,实现了行程选择、视图切换等交互功能。

相关文章
|
2月前
|
监控 JavaScript 编译器
从“天书”到源码:HarmonyOS NEXT 崩溃堆栈解析实战指南
本文详解如何利用 hiAppEvent 监控并获取 sourcemap、debug so 等核心产物,剖析了 hstack 工具如何将混淆的 Native 与 ArkTS 堆栈还原为源码,助力开发者掌握异常分析方法,提升应用稳定性。
432 40
|
3月前
|
开发者 容器
鸿蒙应用开发从入门到实战(十四):ArkUI组件Column&Row&线性布局
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解Column和Row组件的使用以及线性布局的方法。
310 12
|
3月前
|
API 数据处理
鸿蒙应用开发从入门到实战(十三):ArkUI组件Slider&Progress
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解滑块Slider和进度条Progress组件的使用。
192 1
|
3月前
|
JavaScript 开发者 索引
鸿蒙应用开发从入门到实战(九):ArkTS渲染控制
ArkTS拓展了TypeScript,可以结合ArkUI进行渲染控制,是的界面设计具有可编程性。本文简要描述鸿蒙应用开发中的条件渲染和循环渲染。
186 5
|
2月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
304 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
2月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
270 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
2月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
631 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
3月前
|
数据安全/隐私保护 开发者
鸿蒙应用开发从入门到实战(十一):ArkUI组件Text&TextInput
ArkUI提供了丰富的系统组件,用于制作鸿蒙原生应用APP的UI,本文主要讲解文本组件Text和TextInput的使用。
303 3
|
2月前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
165 0
|
3月前
|
存储 缓存 5G
鸿蒙 HarmonyOS NEXT端云一体化开发-云存储篇
本文介绍用户登录后获取昵称、头像的方法,包括通过云端API和AppStorage两种方式,并实现上传头像至云存储及更新用户信息。同时解决图片缓存问题,添加上传进度提示,支持自动登录判断,提升用户体验。
186 1

热门文章

最新文章