《导航切换》案例

通过该案例,我们可以熟练掌握以下知识点
● 使用页Tabs组件进行页面导航
● Swiper组件实现轮播图、
● Grid网格布局
● 以及List列表布局
● 结构化数据封装
● 路由页面切换

1.页面效果

点击登录之后就会进入首页,效果如下图①所示。

iShot_2024-07-28_21.24

2.页面导航

为了开发方便,把图①效果简化为图②,先把页面导航做出来。

iShot_2024-07-28_21.41
完成上述导航效果,实际上由3个页面构成

● MainPage.ets 用于实现页面导航布局
● Home.ets 用于展示首页内容
● Setting.ets 用于展示我的页面内容

2.1MainPage

在pages目录下创建MainPage页面,使用Tabs组件进行页面导航,我们先把页面导航效果做出来。

点击查看代码
import { Home } from '../view/Home'
import { Setting } from '../view/Setting'

@Entry
@Component
struct MainPage {
  @State currentIndex: number = 0

  @Builder
  TabBuilder(title: string, index: number, selectedImg: Resource, normalImg: Resource) {
    Column() {
      Image(this.currentIndex === index ? selectedImg : normalImg)
        .width(25)
        .height(25)
      Text(title)
        .margin({ top: 4 })
        .fontSize(10)
        .fontColor(this.currentIndex === index ? '#1698CE' : '#6B6B6B')
    }
    .justifyContent(FlexAlign.Center)
    .height(56)
    .width('100%')
    .onClick(() => {
      this.currentIndex = index;
      this.tabsController.changeIndex(this.currentIndex);
    })
  }

  private tabsController: TabsController = new TabsController();

  build() {
    Tabs({
      barPosition: BarPosition.End,
      controller: this.tabsController
    }) {
      TabContent() {
        Home()
      }
      .padding({ left: 12, right: 12 })
      .backgroundColor('#F1F3FS')
      .tabBar(this.TabBuilder('首页', 0,
        $r('app.media.home_selected'), $r('app.media.home_normal')))

      TabContent() {
        Setting()
      }
      .padding({ left: 12, right: 12 })
      .backgroundColor('#F1F3FS')
      .tabBar(this.TabBuilder('我的', 1,
        $r('app.media.mine_selected'), $r('app.media.mine_normal')))
    }
    .width('100%')
    .backgroundColor(Color.White)
    .barHeight(56)
    .onChange((index: number) => {
      this.currentIndex = index;
    })
  }
}
## 2.3 Setting 在view目录下创建Setting.ets页面,代码如下
点击查看代码
@Component
export struct Setting {
  build() {
    Column() {
      Text('我的')
    }.width('100%')
    .height('100%').justifyContent(FlexAlign.Center)
  }
}
# 3.首页(Home.ets) 接下来,完成首页效果。

image
整体采用Colum布局完成,依次从上到下进行代码编写

点击查看代码
@Entry
@Component
export struct Home {
  build() {
    Column({ space: 12 }) {
      //Text头部标题

      //Swiper轮播图

      //Text文本

      //FristGrid网格

      //SecondGrid网格
    }
  }
}

3.1 头部标题

点击查看代码
Text('首页')
  .fontSize(24)
  .fontWeight(FontWeight.Medium)
  .width('100%')
  .margin({top:12})
## 3.2 Swiper轮播图 ### 3.2.1 准备轮播图数据 新建ets/viewmode/MainViewModel.ets。代码如下
点击查看代码
//导出MainViewModel类,以便其他位置使用
export class MainViewModel {
  getSwiperImages(): Array<Resource> {
    let swiperImages: Resource[] = [
      $r('app.media.fig1'),
      $r('app.media.fig2'),
      $r('app.media.fig3'),
      $r('app.media.fig4'),
    ]
    return swiperImages;
  }

   getFirstGridData(): Array<FirstGridItemData> {
    let firstGridData: FirstGridItemData[] = [
      new FirstGridItemData('我的最爱', $r('app.media.love')),
      new FirstGridItemData('历史记录', $r('app.media.record')),
      new FirstGridItemData('消息', $r('app.media.message')),
      new FirstGridItemData('购物车', $r('app.media.shopping')),
      new FirstGridItemData('我的目标', $r('app.media.target')),
      new FirstGridItemData('圈子', $r('app.media.circle')),
      new FirstGridItemData('收藏', $r('app.media.favorite')),
      new FirstGridItemData('回收站', $r('app.media.recycle')),
    ]

    return firstGridData;
  }
}

//创建对象并导出
export default new MainViewModel();

3.2.2 加载数据到界面

点击查看代码
Swiper() {
  ForEach(MainViewModel.getSwiperImages(), (img: Resource) => {
    Image(img).borderRadius(16)
      .width('100%')
  })
}
.margin({ top: 12 })
.autoPlay(true) //自动播放
## 3.3 Grid网格一 ### 3.3.1 准备数据 1. 新建ets/viewmode/ItemData.ets,用于封装各种数据模型,它有一个图片和标题组成。
点击查看代码
//第一个网格的Item数据模型
export class FirstGridItemData {
  title: string		//item标题
  img: Resource		//item图标

  constructor(title: string, img: Resource) {
    this.title = title;
    this.img = img;
  }
}
2. 在ets/viewmode/MainViewModel准备8个FirstGridItemData,并存储到一个数组中
点击查看代码
//导出MainViewModel类,以便其他位置使用
export class MainViewModel {
   //...

   getFirstGridData(): Array<FirstGridItemData> {
    let firstGridData: FirstGridItemData[] = [
      new FirstGridItemData('我的最爱', $r('app.media.love')),
      new FirstGridItemData('历史记录', $r('app.media.record')),
      new FirstGridItemData('消息', $r('app.media.message')),
      new FirstGridItemData('购物车', $r('app.media.shopping')),
      new FirstGridItemData('我的目标', $r('app.media.target')),
      new FirstGridItemData('圈子', $r('app.media.circle')),
      new FirstGridItemData('收藏', $r('app.media.favorite')),
      new FirstGridItemData('回收站', $r('app.media.recycle')),
    ]

    return firstGridData;
  }
}

//创建对象并导出
export default new MainViewModel();
### 3.3.2 加载数据到界面
点击查看代码
Grid() {
  ForEach(MainViewModel.getFirstGridData(), (item: FirstGridItemData) => {
    GridItem() {
      Column() {
        Image(item.img)
          .width(24)
          .height(24)
        Text(item.title)
          .fontSize(12)
          .margin({ top: 4 })
      }
    }
  })
}
.height(124)
.columnsTemplate('1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr')
.columnsGap(18)
.rowsGap(12)
.backgroundColor(Color.White)
.borderRadius(24)
## 3.4 Grid网格二 ### 3.4.1 准备数据 1. 新建类class SecondGridItemData用来表示网格二的数据模
点击查看代码
//第二个网格的Item数据模型
export class SecondGridItemData{
  title:string
  secondTitle:string
  img: Resource

  constructor(title: string, secondTitle: string, img: Resource) {
    this.title = title;
    this.secondTitle = secondTitle;
    this.img = img;
  }
}
2. 创建4个SecondGridItemData对象,并存入数据中。
点击查看代码
getSecondGridData():Array<SecondGridItemData>{
  let secondGridItemData: SecondGridItemData[] = [
    new SecondGridItemData('排行榜','厦门站,我们不见不散',$r('app.media.top')),
    new SecondGridItemData('新品发布','厦门站,我们部件不散',$r('app.media.new')),
    new SecondGridItemData('打牌闪购','更多大牌',$r('app.media.brand')),
    new SecondGridItemData('发现好物','厦门站,我们不见不散',$r('app.media.found')),
  ]
  return secondGridItemData
}
### 3.4.2 加载数据到界面
点击查看代码
//...Text(’列表‘)

Grid() {
  ForEach(MainViewModel.getSecondGridData(), (item: SecondGridItemData) => {
    GridItem() {
      Column() {
        Text(item.title)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        Text(item.secondTitle)
          .fontSize(12)
          .fontColor('#99182431')
          .margin({ top: 4 })
      }
    }
    .padding({ top: 8, left: 8 })
    .align(Alignment.TopStart)
    .backgroundImage(item.img)
    .backgroundImageSize(ImageSize.Cover)		//背景图、居中填充
    .borderRadius(12)
    .width('100%')
    .height('100%')
  })
}
.width('100%')
.height(260)
.columnsTemplate('1fr 1fr')
.rowsTemplate('1fr 1fr')
.columnsGap(8)
.rowsGap(8)
.margin({ bottom: 55 })
# 4. 我的(Setting) 接下来,完成我的页面。下面是效果图和布局分析图。

image (1)
如上图所示,整体采用Column布局,然后依次从上到下完成代码编写。

4.1 个人信息

点击查看代码
import { ListItemData } from '../viewmodel/ItemData'
import MainViewModel from '../viewmodel/MainViewModel'
import router from '@ohos.router'

@Entry
@Component
export struct Setting {
  build() {
    Column() {
      //1. 头部标题
      Text('我的')
        .fontSize(24)
        .fontWeight(FontWeight.Medium)
        .width('100%')
        .margin({ top: 12 })

      //2. 个人信息
      Row() {
        Image($r('app.media.account'))
          .width(45)
        Column() {
          Text('李先生').fontSize(18).fontWeight(FontWeight.Bold)
          Text('quarkn@forxmail.com')
        }.alignItems(HorizontalAlign.Start).margin({ left: 25 })
      }
      .justifyContent(FlexAlign.Start)
      .width('100%')
      .height(100)
      .backgroundColor(Color.White)
      .margin({ top: 20 })
      .padding({ left: 20 })
      .borderRadius(12)
      
    }.width('100%').height('100%')
  }
}
## 4.2 功能列表
点击查看代码
import { ListItemData } from '../viewmodel/ItemData'
import MainViewModel from '../viewmodel/MainViewModel'
import router from '@ohos.router'

@Entry
@Component
export struct Setting {
  build() {
    Column() {
      //1. 头部标题...
      //2. 头部标题...

      //3. 列表
      List() {
        ForEach(MainViewModel.getListData(), (item: ListItemData) => {
          ListItem() {
            Row() {
              Image(item.img).width(25)
              Text(item.title).margin({ left: 10 })
              Blank()
              if(item.index===0){
                Toggle({ type: ToggleType.Switch, isOn: false })
              }else {
                Image($r('app.media.arrow')).width(20)
              }
            }
            .width('100%')
            .height(50)
            .backgroundColor(Color.White)
            .padding({ left: 10, right: 10 })
          }
        })
      }.borderRadius(12).margin({top:15}).divider({
        strokeWidth:0.25,
        color:Color.Grey,
        startMargin:40,
        endMargin: 10
      })

      Blank()

      Button('退出登录').width('80%').backgroundColor('#dedede')
        .margin({bottom:100}).fontColor(Color.Red)
        .onClick(()=>{
          router.replaceUrl({ url: "pages/LoginPage" })
        })

    }.width('100%').height('100%')
  }
}
## 4.3 退出按钮 点击登录按钮时,调整到登录页。
点击查看代码
import { ListItemData } from '../viewmodel/ItemData'
import MainViewModel from '../viewmodel/MainViewModel'
import router from '@ohos.router'

@Entry
@Component
export struct Setting {
  build() {
    Column() {
      //1. 头部标题...
      //2. 头部标题...
      //3. 列表...
      
      Blank()	//弹性空白

      Button('退出登录').width('80%').backgroundColor('#dedede')
        .margin({bottom:100}).fontColor(Color.Red)
        .onClick(()=>{
          router.replaceUrl({ url: "pages/LoginPage" })	//跳转到登录页
        })

    }.width('100%').height('100%')
  }
}
最终效果如下图所示

iShot_2024-07-29_02.40
鸿蒙学习地址

posted @ 2025-10-29 15:17  leon_teacher  阅读(12)  评论(0)    收藏  举报