当前位置: 首页 > article >正文

1. 页面级一多开发:

核心:

Home 模块:

  1. 页面适配
  2. 网络数据整合

1. 页面级一多开发:

1.1. phone-tabs 适配

完成 tabs 部分的 ui 适配

tabs文档

需求:

  1. sm、md:底部
  2. lg:左侧

核心步骤:

  1. products/phone/src/main/ets/entryability/EntryAbility.ets
    1. 根据窗口尺寸获取断点值
    2. 通过 AppStorage 进行共享
  1. common:
    1. 整合:commons/basic/src/main/ets/utils/BreakPointType.ets
    2. 导出:BreakPointType
  1. phone:
    1. 使用 BreakPointType
      1. 导入,实例化工具对象,获取断点值
      2. 注册,移除事件监听
    1. 根据断点值,调整 tabs 的位置
    2. 涉及Tabs 的属性:
      1. barPosition:位置
      2. vertical:是否垂直
// 导入略
export default class EntryAbility extends UIAbility {
  private curBp: string = ''

  // 根据当前窗口尺寸更新断点
  private updateBreakpoint(windowWidth: number): void {
    // 将长度的单位由px换算为vp
    let windowWidthVp = px2vp(windowWidth)
    let newBp: string = ''
    if (windowWidthVp < 320) {
      newBp = 'xs'
    } else if (windowWidthVp < 600) {
      newBp = 'sm'
    } else if (windowWidthVp < 840) {
      newBp = 'md'
    } else {
      newBp = 'lg'
    }
    if (this.curBp !== newBp) {
      this.curBp = newBp
      // 使用状态变量记录当前断点值
      AppStorage.setOrCreate('currentBreakpoint', this.curBp)
    }
  }

  // 其他略

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
    windowStage.getMainWindow()
      .then((windowObj) => {
        // 获取应用启动时的窗口尺寸
        this.updateBreakpoint(windowObj.getWindowProperties()
          .windowRect
          .width)
        // 注册回调函数,监听窗口尺寸变化
        windowObj.on('windowSizeChange', (windowSize) => {
          this.updateBreakpoint(windowSize.width)
        })
      });

    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');

    });
  }

}
declare interface BreakPointTypeOption<T> {
  xs?: T
  sm?: T
  md?: T
  lg?: T
}

export class BreakPointType<T> {
  options: BreakPointTypeOption<T>

  constructor(option: BreakPointTypeOption<T>) {
    this.options = option
  }

  getValue(currentBreakPoint: string) {
    if (currentBreakPoint === 'xs') {
      return this.options.xs
    } else if (currentBreakPoint === 'sm') {
      return this.options.sm
    } else if (currentBreakPoint === 'md') {
      return this.options.md
    } else if (currentBreakPoint === 'lg') {
      return this.options.lg
    } else {
      return undefined
    }
  }
}
// export { MainPage } from './src/main/ets/components/mainpage/MainPage'

export * from './src/main/ets/utils/BreakPointType'

// 1.导入
import { BreakPointType } from 'basic';

@StorageProp('currentBreakpoint') breakPoint: string = 'xs'


// 调整 tabs 的属性
Tabs() {
  //.... 
}
.barPosition(new BreakPointType({
  sm: BarPosition.End,
  md: BarPosition.End,
  lg: BarPosition.Start
}).getValue(this.breakPoint))
.vertical(this.breakPoint == 'lg' ? true : false)
  

git 记录:

phone-tabs 适配

1.2. phone-抽取常量

上一节代码中的 currentBreakpoint,字符串在使用过程中没有代码提示,为了更加利于后期的维护,咱们抽取为常量。

  1. 创建用来管理常量的类,并导出
// commons/basic/src/main/ets/constants/index.ets
export class BreakpointConstants {
  // 手表等超小屏
  static readonly XS: string = 'xs';
  // 手机竖屏
  static readonly SM: string = 'sm';
  // 手机横屏,折叠屏
  static readonly MD: string = 'md';
  // 平板,2in1 设备
  static readonly LG: string = 'lg';
  // AppStorage 中的 key
  static readonly BREAK_POINT_KEY: string = 'currentBreakpoint'
}
// commons/basic/Index.ets
export { add } from './src/main/ets/utils/Calc'

export { Logger } from './src/main/ets/utils/Logger'

export * from './src/main/ets/utils/BreakpointSystem'

export * from './src/main/ets/constants/index'

  1. 替换原本代码中写死的字符串
    1. products/phone/src/main/ets/entryability/EntryAbility.ets 中的key和判断的值
  // 根据当前窗口尺寸更新断点
  private updateBreakpoint(windowWidth: number): void {
    // 将长度的单位由px换算为vp
    let windowWidthVp = px2vp(windowWidth)
    let newBp: string = ''
    if (windowWidthVp < 320) {
      newBp = BreakpointConstants.XS
    } else if (windowWidthVp < 600) {
      newBp = BreakpointConstants.SM
    } else if (windowWidthVp < 840) {
      newBp = BreakpointConstants.MD
    } else {
      newBp = BreakpointConstants.LG
    }
    if (this.curBp !== newBp) {
      this.curBp = newBp
      // 使用状态变量记录当前断点值
      AppStorage.setOrCreate(BreakpointConstants.BREAK_POINT_KEY, this.curBp)
    }
  }
    1. products/phone/src/main/ets/pages/Index.ets 中获取断点值,及设置及方向

import { Logger, BreakPointType, BreakpointConstants } from 'basic'


// 断点值
@StorageProp(BreakpointConstants.BREAK_POINT_KEY) breakPoint: string = ''

Tabs(){
  // 略
}
.vertical(this.breakPoint === BreakpointConstants.LG ? true : false)
.barPosition(this.breakPoint === BreakpointConstants.LG ? BarPosition.Start : BarPosition.End)

git记录:

phone-抽取断点常量

1.3. Home 模块

Home 模块的一多开发

1.3.1. 素材整合

将图片资源,页面,组件,模块这些整合进来

核心步骤:

  1. commons/basic 模块
    1. 通用图片:commons/basic/src/main/resources/base/media
      1. 📎common-图片资源.zip
    1. 通用数据模型:📎comon-viewmodel.zip
    2. 导出通用数据模型
  1. features/home 模块
    1. 页面组件,页面数据模型,页面:📎home ets 目录.zip
    2. 页面图片资源:📎home 图片资源.zip
    3. 在 oh-package.json5 导入 common/basic (否则无法引用数据模型和资源)
    4. 在模块根目录的 Index.ets 导出 HomeView 组件
  1. products/phone 模块
    1. 在 oh-package.json5 导入 features/home 模块
    2. 在 pages/Index.ets 导入 HomeView 组件
    3. 在 module.json5 开启网络权限(保证首页网络图片可以显示)

// export { MainPage } from './src/main/ets/components/mainpage/MainPage'
export * from './src/main/ets/viewmodel/index'

{
  "name": "home",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "Index.ets",
  "author": "",
  "license": "Apache-2.0",
  "dependencies": {
    "basic": "file:../../commons/basic"
  }
}

git 记录:

home-素材整合

1.3.2. 界面适配

完成 home模块的下的页面适配效果

module.json5、轮播图个数、轮播图间隙

需求:

  1. 无法缩小到 xs 范围(2in1 设备才可以测试效果)
  2. 轮播图:
    1. sm:1张图,0 间隙,导航点
    2. md:2 张图,10 间隙,无导航点
    3. lg:3 张图,20 间隙,无导航点
  1. 分类
  2. 特惠推荐
  3. 新鲜好物
    1. 上面 3 个都是 space:sm:14,md:36,lg:72
  1. 推荐商品
    1. sm:2 列
    2. md:3 列
    3. lg:4 列

注意:

自由窗口模式(2in1,平板)的 minWindowWidth,包括屏幕2 边的间隙,只设置为 320 没用,需要设置到 331,参考官方示例

核心步骤:

  1. 无法缩小到 xs 范围:module.json5 设置 minWindowWidth 属性(2in1设备才可以测试效果)
  2. 通过 AppStorage 获取断点值
  3. 轮播图:
    1. 通过 BreakPointType 设置不同的图片张数 displayCount
    2. 通过 三元设置导航点的显示
    3. 通过 BreakPointType 设置间隙 itemSpace
  1. 分类、特惠推荐、新鲜好物:(别给错了)
    1. 调整 space 可以抽取函数
    2. sm 14,md 36,lg 72
  1. 推荐商品:BreakPointType设置 columnsTemplate 即可
{
  "module": {
    "name": "phone",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet",
      "2in1"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ],
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "minWindowWidth": 331,// 最小窗口宽度 2in1 设备才可以测试
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ]
  }
}
import { BreakpointConstants, BreakPointType } from 'basic'
import { DiscountGoodsComp, DiscountType } from '../components/DiscountGoodsComp'
import { MkGoods } from '../components/MkGoods'
import { Banner, CategoryItem, MkGoodsItem, Params } from '../viewmodel'


@Component
export struct HomeView {
  // 轮播图
  @State banners: Banner[] = []
  // 分类
  @State categories: CategoryItem[] = []
  // 特惠推荐
  @State saleGoods: MkGoodsItem[] = []
  // 爆款推荐
  @State hotGoods: MkGoodsItem[] = []
  // 一站买全
  @State oneGoods: MkGoodsItem[] = []
  // 新鲜好物
  @State newGoods: MkGoodsItem[] = []
  // 推荐商品
  @State recommendGoods: MkGoodsItem[] = []
  // 断点值
  @StorageProp(BreakpointConstants.BREAK_POINT_KEY) breakPoint: string = ''

  // 生成模拟数据
  mockData() {
    this.banners = new Array(6).fill({
      id: "42",
      imgUrl: "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/banner/nuandong_sj.png",
      hrefUrl: "/category/1181622006",
      type: "1"
    })

    this.categories = new Array(10).fill({
      id: "1181622001",
      name: "气质女装",
      picture: "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meikou/c1/qznz.png"
    })

    this.saleGoods = new Array(6).fill({
      "id": "1111002",
      "name": "剪出精致感,金致圆柄复古剪刀",
      "desc": "轻薄设计,简约大方",
      "price": "9.90",
      "picture": "https://yanxuan-item.nosdn.127.net/4c6a9c8a579b00e5e9c7b002d15a33a2.jpg",
      "orderNum": 172
    },)

    this.hotGoods = new Array(8).fill({
      "id": "1135059",
      "name": "手工八年老陈醋500毫升",
      "desc": "地道醇香,醋酸浓郁",
      "price": "30.00",
      "picture": "https://yanxuan-item.nosdn.127.net/77da20e77b02830a26f931901ac1a9e0.png",
      "orderNum": 147
    },)
    this.oneGoods = new Array(8).fill({
      "id": "1135079",
      "name": "免浸泡,12种谷物一次同享,五谷米400克",
      "desc": "无需浸泡,同煮同熟",
      "price": "9.90",
      "picture": "https://yanxuan-item.nosdn.127.net/bfe70bd66efe94f2f18061c707d2a097.png",
      "orderNum": 144
    })
    this.newGoods = new Array(8).fill({
      "id": "4027998",
      "name": "亮碟多效合一洗涤块495g",
      "desc": "洗碗机专用,强力去污",
      "price": "69.90",
      "picture": "https://yanxuan-item.nosdn.127.net/e07c2b63765cf9f4a46d489c6e09c1c1.jpg",
      "orderNum": 643
    })

    this.recommendGoods = new Array(8).fill({
      "id": "4033959",
      "name": "3秒快速拆琴轻松保养odin奥丁12半音阶口琴",
      "price": 329,
      "picture": "https://yanxuan-item.nosdn.127.net/937a8e46a9284e8f7e00e13911ecfbe7.png",
      "payCount": 0
    })
  }

  aboutToAppear(): void {
    // mock数据
    this.mockData()

  }

  @Builder
  DiscountBuilder(params: Params) {
    Column() {
      Row({ space: 10 }) {
        Text(params.title)
          .fontColor($r('[basic].color.black'))
          .fontSize(14)
        Text(params.subTitle)
          .fontColor($r('[basic].color.text'))
          .fontSize(11)
      }
      .width('100%')
      .margin({ bottom: 10 })

      List({ space: 10 }) {
        ForEach(params.list, (item: MkGoodsItem) => {
          ListItem() {
            DiscountGoodsComp({ type: DiscountType.DISCOUNT, goods: item })
          }
        })
      }
      .width('100%')
      .height(116)
      .scrollBar(BarState.Off)
      .listDirection(Axis.Horizontal)
    }
    .height(160)
    .layoutWeight(1)
    .padding(10)
    .backgroundColor(params.bg)
    .borderRadius(8)
  }

  getSpace(): number | undefined {
    return new BreakPointType({ sm: 14, md: 36, lg: 72 }).getValue(this.breakpoint)
  }

  build() {
    Scroll() {
      Column() {
        // 轮播图 + 搜索
        Stack({ alignContent: Alignment.Top }) {
          Swiper() {
            ForEach(this.banners, (item: Banner) => {
              Image(item.imgUrl)
            })
          }
          .indicator(
            this.breakpoint == BreakpointConstants.SM ? DotIndicator.dot()
              .itemWidth(8)
              .itemHeight(4)
              .color('#33191919')
              .selectedItemWidth(24)
              .selectedItemHeight(4)
              .selectedColor('#191919') :  false
          )
          .displayCount(
            new BreakPointType({
              sm: 1,
              md: 2,
              lg: 3
            })
            .getValue(this.breakpoint)
          )
          .itemSpace(
            new BreakPointType({
              sm: 0,
              md: 10,
              lg: 20
            })
            .getValue(this.breakpoint)
          )

          Row() {
            Row({ space: 4 }) {
              Image($r('[basic].media.ic_public_search'))
                .width(16)
                .height(16)
                .fillColor($r('[basic].color.white'))
              Text('搜索...')
                .fontSize(14)
                .fontColor($r('[basic].color.white'))
            }
            .backgroundColor('#33191919')
            .width('100%')
            .height(40)
            .borderRadius(20)
            .padding({ left: 12 })
          }
          .padding({ left: 16, right: 16 })
        }
        .width('100%')


        // 分类
        Column({ space: 10 }) {
          // 分类
          List({ space: this.getSpace() }) {
            ForEach(this.categories, (item: CategoryItem) => {
              ListItem() {
                Column() {
                  Image(item.picture)
                    .width(56)
                    .aspectRatio(1)
                  Text(item.name)
                    .fontSize(10)
                    .fontColor('#CC191919')
                }
                .width(60)
                .height(80)
                .borderRadius(30)

                .clip(true)
                .backgroundImage(item.picture)
                .backgroundImageSize(ImageSize.Contain)
                .backgroundImagePosition(Alignment.Center)
                .backgroundBlurStyle(
                  BlurStyle.BACKGROUND_ULTRA_THICK,
                  { scale: 0.25 }
                )
              }
            })
          }
          .width('100%')
          .height(92)
          .scrollBar(BarState.Off)
          .listDirection(Axis.Horizontal)
          .alignListItem(ListItemAlign.Center)

          // 特惠推荐
          Column({ space: 10 }) {
            Image($r('app.media.home_cmd_title'))
              .width(150)
              .height(20)
            Row() {
              Image($r('app.media.home_cmd_inner'))
                .width(86)
                .height(116)
              List({ space: this.getSpace() }) {
                ForEach(this.saleGoods, (item: MkGoodsItem) => {
                  ListItem() {
                    DiscountGoodsComp({ goods: item })
                  }
                })
              }
              .layoutWeight(1)
              .width('100%')
              .height(116)
              .backgroundColor($r('[basic].color.white'))
              .borderRadius({
                topRight: 8,
                bottomRight: 8
              })
              .padding({ right: 10, left: 10 })
              .scrollBar(BarState.Off)
              .listDirection(Axis.Horizontal)
            }
          }
          .width('100%')
          .height(166)
          .backgroundImage($r('app.media.home_cmd_sm'))
          .backgroundImageSize(ImageSize.Cover)
          .borderRadius(8)
          .padding(10)
          .alignItems(HorizontalAlign.Start)

          // 爆款推荐+一站买全
          Row({ space: 10 }) {
            this.DiscountBuilder({
              title: '爆款推荐',
              subTitle: '最受欢迎',
              bg: '#EDF1FB',
              list: this.hotGoods
            })
            this.DiscountBuilder({
              title: '一站买全',
              subTitle: '精心优选',
              bg: '#FCF6EA',
              list: this.oneGoods
            })
          }

          // 新鲜好物
          Column({ space: 10 }) {
            Image($r('app.media.home_new'))
              .width(146)
              .height(19)
            List({ space: this.getSpace() }) {
              ForEach(this.newGoods, (item: MkGoodsItem) => {
                ListItem() {
                  DiscountGoodsComp({ type: DiscountType.NEW, goods: item })
                }
              })
            }
            .width('100%')
            .height(116)
            .scrollBar(BarState.Off)
            .listDirection(Axis.Horizontal)
          }
          .width('100%')
          .height(156)
          .padding(10)
          .backgroundColor('#F7EFF5')
          .borderRadius(8)
          .alignItems(HorizontalAlign.Start)

          // 推荐商品
          WaterFlow() {
            ForEach(this.recommendGoods, (item: MkGoodsItem) => {
              FlowItem() {
                MkGoods({ goods: item })
              }
            })
          }
          .columnsTemplate(
            new BreakPointType({
              sm: '1fr 1fr',
              md: '1fr 1fr 1fr',
              lg: '1fr 1fr 1fr 1fr'
            }).getValue(this.breakpoint)
          )
          .columnsGap(8)
          .rowsGap(10)

        }
        .padding({
          left: 8,
          right: 8,
          bottom: 10,
          top: 10
        })
      }
    }
    .scrollBar(BarState.Off)
  }
}


http://www.kler.cn/a/597992.html

相关文章:

  • Unity摄像机基本操作详解:移动、旋转与缩放
  • Java处理Markdown格式内容转换为Word文档
  • JavaScript 性能优化实战
  • 计算机网络的分类及其性能指标
  • Redis简单介绍和安装
  • Centos7搭建Zabbix4.x监控HCL模拟网络设备:zabbix-server搭建及监控基础02
  • 系统转换、系统维护、净室软件工程、构件软件工程(高软51)
  • c++有n个范围是-1e9~1e9的数,去统计每个数的个数?
  • 位示图大小的计算
  • 【递归、搜索和回溯算法】专题三 :穷举VS暴搜VS深搜VS回溯VS剪枝
  • 【ROS实战】02-ROS架构介绍
  • 如何使用SystemVerilog SVA检查跨时钟域信号?
  • [c语言日寄]数据输入
  • GEO与AISEO的关系解析:核心差异与协同逻辑
  • Qt-Q_ENUM宏和QMetaEnum类
  • java江湖系列——集合世家争霸(下)
  • MySQL 5.7升级8.0报异常:处理新增关键字
  • 在 macOS 上安装 coc.nvim(推荐方式)
  • Java-01-源码篇-并发编程-资源竞争
  • 表达式树和编译原理【10道经典面试题】(中英对照)