鸿蒙OS开发:【一次开发,多端部署】(app市场首页)项目

一多应用市场首页

介绍

本示例展示了应用市场首页,页面中包括Tab栏、运营横幅、精品应用、精品游戏等。

本示例使用一次开发多端部署中介绍的自适应布局能力和响应式布局能力进行多设备(或多窗口尺寸)适配,保证应用在不同设备或不同窗口尺寸下可以正常显示。

用到了媒体查询接口[@ohos.mediaquery]

效果预览

本示例在预览器中的效果:

image.png

本示例在开发板上运行的效果:

image.png

使用说明:

  1. 启动应用,可以查看本应用在全屏状态下的显示效果。
  2. 在应用顶部,下滑出现窗口操作按钮。(建议通过外接鼠标操作,接入鼠标只需要将鼠标移动至顶部即可出现窗口)
  3. 点击悬浮图标,将应用悬浮在其它界面上显示。
  4. 拖动应用悬浮窗口的边框,改变窗口尺寸,触发应用刷新,即可查看应用在不同窗口下的显示效果。
  5. 改变窗口尺寸的过程中,窗口尺寸可能超出屏幕尺寸。此时在屏幕中只能看到应用部分区域的显示,但可以通过移动窗口位置,查看应用其它区域的显示。
  6. 开发前请熟悉鸿蒙开发指导文档:gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。

工程目录

AppMarket/entry/src/main/ets/
|---model
|   |---HomeData.ets                       // 主页用到的图片资源
|   |---HomeDataType.ets                   // 事件监听函数
|---pages                                  
|   |---index.ets                          // 首页
|---common                                    
|   |---BreakpointSystem.ets               // 媒体查询
|   |---Home.ets                           // 主容器
|   |---IndexApps.ets                      // app模块(包含安装,展示图片,更多功能)
|   |---IndexContent.ets                   // 内容模块
|   |---IndexEntrance.ets                  // 下一步模块(箭头跳转组件)
|   |---IndexHeader.ets                    // 头部组件
|   |---IndexSwiper.ets                    // 轮播图   
|   |---TabBarItem.ets                     // 导航栏                                            

具体实现

本示例介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口,将页面分拆为5个部分。

底部/侧边导航栏

1、在sm和md断点下,导航栏在底部;在lg断点下,导航栏在左侧。
2、通过Tab组件的barPosition和vertical属性控制TabBar的位置在主轴方向起始或结尾位置和水平或垂直方向,同时还可以通过barWidth和barHeight属性控制TabBar的尺寸,[源码参考]。

/*

 * Copyright (c) 2022 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



import Home from '../common/Home'

import TabBarItem from '../common/TabBarItem'

import BreakpointSystem from '../common/BreakpointSystem'



@Entry

@Component

struct Index {

  @State currentIndex: number = 0

  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md'

  private breakpointSystem: BreakpointSystem = new BreakpointSystem()

  private onTabChange = (index: number) => {

    this.currentIndex = index

  }



  aboutToAppear() {

    this.breakpointSystem.register()

  }



  aboutToDisappear() {

    this.breakpointSystem.unregister()

  }



  @Builder

  tabItem(index: number, title: Resource, icon: Resource, iconSelected: Resource) {

    TabBarItem({

      index: index,

      currentIndex: this.currentIndex,

      title: title,

      icon: icon,

      iconSelected: iconSelected

    })

  }



  build() {

    Tabs({ barPosition: this.currentBreakpoint === 'lg' ? BarPosition.Start : BarPosition.End }) {

      TabContent() {

        Home()

      }

      .tabBar(this.tabItem(0, $r('app.string.tabBar1'), $r('app.media.ic_home_normal'), $r('app.media.ic_home_actived')))



      TabContent() {

      }

      .tabBar(this.tabItem(1, $r('app.string.tabBar2'), $r('app.media.ic_app_normal'), $r('app.media.ic_app_actived')))



      TabContent() {

      }

      .tabBar(this.tabItem(2, $r('app.string.tabBar3'), $r('app.media.ic_game_normal'), $r('app.media.ic_mine_actived')))



      TabContent() {

      }

      .tabBar(this.tabItem(3, $r('app.string.tabBar4'), $r('app.media.ic_search_normal'), $r('app.media.ic_search_actived')))



      TabContent() {

      }

      .tabBar(this.tabItem(4, $r('app.string.tabBar4'), $r('app.media.ic_mine_normal'), $r('app.media.ic_mine_actived')))

    }

    .barWidth(this.currentBreakpoint === 'lg' ? 96 : '100%')

    .barHeight(this.currentBreakpoint === 'lg' ? '60%' : 56)

    .vertical(this.currentBreakpoint === 'lg')

    .onChange(this.onTabChange)

    .backgroundColor('#F1F3F5')

  }

}
标题栏与搜索栏

通过栅格实现标题栏和搜索栏:在sm和md断点下分两行显示,在lg断点下单行显示,[源码参考]。

/*

 * Copyright (c) 2022 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



@Component

export default struct IndexHeader {

  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md'



  @Builder searchBar() {

    Stack({alignContent: Alignment.End}) {

      TextInput({ placeholder: $r('app.string.search') })

        .placeholderColor('#FF000000')

        .placeholderFont({ size: 16, weight: 400 })

        .textAlign(TextAlign.Start)

        .caretColor('#FF000000')

        .width('100%')

        .height(40)

        .fontWeight(400)

        .padding({ top: 9, bottom: 9 })

        .fontSize(16)

        .backgroundColor(Color.White)



      Image($r('app.media.ic_public_search'))

        .width(16)

        .height(16)

        .margin({ right: 20 })

    }.height(56).width('100%')

  }



  @Builder titleBar() {

    Text($r('app.string.tabBar1'))

      .fontSize(24)

      .fontWeight(500)

      .fontColor('#18181A')

      .textAlign(TextAlign.Start)

      .height(56)

      .width('100%')

  }



  build() {

    GridRow() {

      GridCol({ span: { xs: 12, lg: 8 } }) {

        this.titleBar()

      }

      GridCol({ span: { xs: 12, lg: 4 } }) {

        this.searchBar()

      }

    }

    .width('100%')

    .height(this.currentBreakpoint === 'lg' ? 56 : 112)

    .padding({ left: 12, right: 12 })

  }

}

2、在sm和md断点下,标题栏和搜索栏占满12列,此时会自动换行显示。
3、在lg断点下,标题栏占8列而搜索栏占4列,此时标题栏和搜索栏在同一行中显示。

运营横幅

实现不同断点下的运营横幅:通过Swiper组件的displayCount属性,sm断点下显示一张图片,md断点下显示两张图片,lg断点下显示三张图片。

快捷入口

通过将justifyContent参数配置为FlexAlign.SpaceEvenly实现均分布局:在不同的断点下,快捷入口的5个图标始终均匀排布。

精品应用

通过List组件能力,实现延伸能力场景:随着可用显示区域的增加,精品应用中显示的图标数量也不断增加,[源码参考]。

/*

 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



import { AppItem, MyAppSource } from '../model/HomeDataType'



@Component

export default struct IndexApps {

  private title: Resource

  @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md'

  private apps: AppItem[] = []



  @Builder

  appListHeader() {

    Row() {

      Text(this.title)

        .width(100)

        .fontSize(16)

        .textAlign(TextAlign.Start)

        .fontWeight(500)

      Blank()

      Text($r('app.string.more'))

        .fontSize(14)

        .textAlign(TextAlign.End)

        .fontWeight(400)

        .margin({ right: 2 })

      Image($r('app.media.ic_public_arrow_right'))

        .width(12)

        .height(18)

        .opacity(0.9)

        .objectFit(ImageFit.Fill)

    }

    .margin({ bottom: 9, top: 9 })

    .width('100%')

    .alignItems(VerticalAlign.Bottom)

  }



  @Builder

  appListItem(app:AppItem) {

    Column() {

      Image(app.image)

        .width(this.currentBreakpoint === 'lg' ? 80 : 56)

        .height(this.currentBreakpoint === 'lg' ? 80 : 56)

        .margin({ bottom: 8 })

      Text(app.title)

        .width(this.currentBreakpoint === 'lg' ? 80 : 56)

        .height(16)

        .fontSize(12)

        .textAlign(TextAlign.Center)

        .fontColor('#18181A')

        .margin({ bottom: 8 })

      Text($r('app.string.install'))

        .width(this.currentBreakpoint === 'lg' ? 80 : 56)

        .height(28)

        .fontColor('#0A59F7')

        .textAlign(TextAlign.Center)

        .borderRadius(this.currentBreakpoint === 'lg' ? 26 : 20)

        .fontWeight(500)

        .fontSize(12)

        .padding({ top: 6, bottom: 6, left: 8, right: 8 })

        .backgroundColor('rgba(0,0,0,0.05)')

    }

  }





  build() {

    Column() {

      this.appListHeader()

      List({ space: this.currentBreakpoint === 'lg' ? 44 : 20}) {

        LazyForEach(new MyAppSource(this.apps), app => {

          ListItem() {

            this.appListItem(app)

          }

        }, app => app.id)

      }

      .width('100%')

      .height(this.currentBreakpoint === 'lg' ? 140 : 120)

      .listDirection(Axis.Horizontal)

    }

    .width('100%')

    .height(this.currentBreakpoint === 'lg' ? 188 : 164)

    .padding({ bottom: 8, left: 12, right: 12 })

  }

}

`HarmonyOS与OpenHarmony鸿蒙文档籽料:mau123789是v直接拿`

搜狗高速浏览器截图20240326151450.png

总体运行效果

通过将上述各页面在List() {}中引用组件后,可实现首页的组件整合渲染,即可完成整体页面开发。

  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值