【HarmonyOS实战开发】鸿蒙 容器组件解析

33 篇文章 0 订阅
33 篇文章 0 订阅

Column 和 Row

Column 垂直布局 Row水平,主轴默认是Start,交叉轴默认是Center

Column({ space: 10 }) {
  Column() {
    Text('文本1').bordStyle()
    Text('文本2').bordStyle()
    Text('文本3').bordStyle()
    Text('文本3').bordStyle()
  }
  .bordStyle()
  .width(200)
  .height(200)
  // 主轴方向 默认是Start
  // FlexAlign.Center 中间,
  // FlexAlign.Start 开始的位置,
  // FlexAlign.End 结束的位置,
  // FlexAlign.SpaceBetween 上下贴边,其他平分
  // FlexAlign.SpaceAround 上下剩余,是其他剩余的一半
  // FlexAlign.SpaceEvenly 全部平分
  .justifyContent(FlexAlign.SpaceEvenly)
  // 交叉轴方向 默认是Center,End,Center
  .alignItems(HorizontalAlign.End)

Stack

堆叠容器,类似于FramLayout,默认是Center

// 参数 alignContent默认是Center,其他是8个方向加一个Center
Stack({ alignContent: Alignment.TopStart }) {
  Text('文本').margin({left:100,top:100})
  // 也可以用通用属性zIndex ,来控制再Z轴的方向
  Button('点我').zIndex(1)
}.bordStyle().width(200).height(200)

Flex

以弹性方式布局子组件的容器组件。Flex组件在渲染时存在二次布局过程,因此在对性能有严格要求的场景下建议使用
必须设置成 Wrap,否则跟row ,colom 没啥区别

Flex({
  // 主轴
  direction: FlexDirection.Row,
  // 是否换行,必须换呀,否则用这个就没啥意义了
  wrap: FlexWrap.Wrap,
  // 主轴方向
  justifyContent: FlexAlign.Start,
  // 所有子组件在Flex容器交叉轴上的对齐格式。
  alignItems: ItemAlign.End,
  // 交叉轴中有额外的空间时,多行内容的对齐方式
  alignContent: FlexAlign.Start
})

RelativeContainer

相对布局,android中的 ConstraintLayout ,用于复杂布局使用
anchor锚点,如果是父亲__container__,每个View都有id
top: {anchor: “container”, align: VerticalAlign.Top},

RelativeContainer() {
  Row().width(100).height(100)
    .backgroundColor("#ffec0a0a")
    .alignRules({
      top: {anchor: "__container__", align: VerticalAlign.Top},
      left: {anchor: "__container__", align: HorizontalAlign.Start}
    })
    .id("row1")
    }

Scroll

可滚动的容器组件,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。切记,子不能设置高度,得自己铺满父,nestedScroll api 10 可以实现滑动前套问题

Scroll(this.scroller) {
  Column() {
    ForEach(this.arr, (item: number) => {
      Text(item.toString())
        .width('90%')
        .height(150)
        .backgroundColor(0xFFFFFF)
        .borderRadius(15)
        .fontSize(16)
        .textAlign(TextAlign.Center)
        .margin({ top: 10 })
    }, (item: string) => item)
  }.width('100%')
}
.scrollable(ScrollDirection.Vertical) // 滚动方向纵向
.scrollBar(BarState.Off) // 滚动条常驻显示
.scrollBarColor(Color.Gray) // 滚动条颜色
.scrollBarWidth(10) // 滚动条宽度
.edgeEffect(EdgeEffect.None) // 是否像弹簧一样,根ios一样的效果
.friction(0.6) // api10 摩擦系数
.onScroll((xOffset: number, yOffset: number) => { //滚动事件回调, 返回滚动时水平、竖直方向偏移量,单位vp。
  // console.info(xOffset + ' ' + yOffset)
})
.onScrollEdge((side: Edge) => { //滚动到边缘事件回调。
  console.info(`To the edge ${side}` )
})

.onScrollStart(() => {
  console.info('Scroll onScrollStart')
})
.onScrollStop(() => {
  console.info('Scroll onScrollStop')
})
.onReachStart(() => {// api 11 Scroll到达起始位置时触发。
  //Scroll初始化时会触发一次,滚动到起始位置时触发一次。Scroll边缘效果为弹簧效果时,划动经过起始位置时触发一次,回弹回起始位置时再触发一次
  console.info('Scroll onReachStart')
})
.onReachEnd(() => {// api 11 Scroll到达起始位置时触发。
  //Scroll初始化时会触发一次,滚动到起始位置时触发一次。Scroll边缘效果为弹簧效果时,划动经过起始位置时触发一次,回弹回起始位置时再触发一次
  console.info('Scroll onReachEnd')
})

Swiper

可以认为是 Banner

 Swiper(this.controller) {
    LazyForEach(this.data, (item: number, index: number) => {
      Text(item.toString())
        .width('90%')
        .height(200)
        .textAlign(TextAlign.Center)
        .fontSize(40)
        .fontColor(Color.Red)
        .fontWeight(FontWeight.Bold)
        .border({ color: Color.Red, width: 5, radius: 20 })
        .onClick(() => {
          console.log(`点击了 ${item}`)
        })
    })
  }
  .index(0) // 初始化位置,默认是0
  .autoPlay(true) // 是否轮播 默认是false
  .interval(2000) //自动播放事件,停留的时间
  // api 10 之后 可以设置 DotIndicator 圆点
  // DigitIndicator 数字指示器
  // boolean:是否启用导航点指示器。默认是true
  .indicator(
    // new DotIndicator()
    // .itemWidth(10)
    // .itemHeight(10)
    // .selectedItemWidth(15)
    // .selectedItemHeight(15)
    // .selectedColor(Color.Red)// 指示器是选择的颜色
    // .color(Color.Black)// 指示器默认颜色
    // .mask(true)// 给指示器 添加阴影
    new DigitIndicator()
      .fontColor(Color.Black)
      .selectedFontColor(Color.Red)
      .digitFont({ size: 40, weight: FontWeight.Bold })
      .selectedDigitFont({ size: 20, weight: FontWeight.Medium })
  ) //设置可选导航点指示器样式。
  .loop(true) // ,滑动到最后,下一个是否是第一个,是否开启循环。设置为true时表示开启循环,在LazyForEach懒循环加载模式下,加载的组件数量建议大于5个。默认值:true
  // .duration(1000) //子组件切换的动画时长,单位为毫秒。400ms,从1切换到2 需要的时间
  .vertical(false) // 是否是纵向滑动,默认false
  .itemSpace(10) //设置子组件与子组件之间间隙。默认值:0

  // .displayMode(SwiperDisplayMode.STRETCH) //主轴方向上元素排列的模式,优先以displayCount设置的个数显示,displayCount未设置时本属性生效。
  .cachedCount(10) //设置预加载子组件个数。默认值:1
  .disableSwipe(false) // 禁止手动滑动
  .curve(Curve.Linear) // 动画插值器
  // .displayCount(10) // 显示个数,就是一页能显示多少,咱们一半就是写1个
  // .effectMode(EdgeEffect.Spring) // 滑动的最后的 Spring,当loop是false的时候
  // .displayArrow(true) //api 10 设置导航点箭头样式
  .nextMargin(10) //api 10 后边距,用于露出后一项的一小部分。仅当SwiperDisplayMode为STRETCH模式时生效
  .prevMargin(10) //api 10 前边距,用于露出前一项的一小部分。仅当SwiperDisplayMode为STRETCH模式时生效
  // .nestedScroll()// api 11 前套滚动
  .onChange((index: number) => {
    console.log(`滚动到了 index = ${index}`)
  })
  .onAnimationStart((index: number, targetIndex: number, extraInfo: SwiperAnimationEvent) =>{
    console.log(`开始滚动 从index = ${index} 到 ${targetIndex}  ${JSON.stringify(extraInfo)}` )
  })
  .onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) =>{
    console.log(`手动开始滚动 从index = ${index}   ${JSON.stringify(extraInfo)}` )
  })
}
.height('100%')

List

就是个ListView 可以设置横轴纵轴个数,变成表格布局,也有scrollSnapAlign,根snaphelper似的,也可以做悬浮,吸顶效果
列表包含一系列相同宽度的列表项。适合连续、多行呈现同类数据,例如图片和文本。

●if/else语句中,只有条件成立的分支内的子组件会参与索引值计算,条件不成立的分支内子组件不计算索引值。
●ForEach/LazyForEach语句中,会计算展开所有子节点索引值。
●if/else、ForEach和LazyForEach发生变化以后,会更新子节点索引值。
●ListItemGroup作为一个整体计算一个索引值,ListItemGroup内部的ListItem不计算索引值。●ListItemGroup中的所有ListItem会一次性全部加载出来。
●List子组件visibility属性设置为Hidden或None依然会计算索引值。
●List子组件的visibility属性设置为None时不显示,但该子组件上下的space还会生效。
●ForEach的第三个,代表什么数据不一样的时候 刷新item

List({ space: 20, initialIndex: 0, scroller: new ListScroller() }) {
  ForEach(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"],
    (item: string) => {
      ListItem() {
        Text('' + item)
          .width('100%')
          .height(100)
          .fontSize(16)
          .textAlign(TextAlign.Center)
          .borderRadius(10)
          .backgroundColor(0xFFFFFF)
      }
      .border({ width: 2, color: Color.Green })
    }, (item: string) => item)
}
// .lanes(2)// 在交叉轴的个数,相当于表格布局
.listDirection(Axis.Vertical) // 方向 默认垂直
// 分割线 ,可以设置左右边距
// .divider({ strokeWidth: 2, color: Color.Red, startMargin: 20, endMargin: 20 })
.scrollBar(BarState.Off) // 是否显示进度条
.cachedCount(2) // 缓存个数只在LazyForEach中生效,其中ListItemGroup将作为一个整体进行计算
.edgeEffect(EdgeEffect.Spring) // 边缘滚动效果
.chainAnimation(true) // 设置当前List是否启用链式联动动效,开启后 divider 会变没了
.sticky(StickyStyle.Header) // 悬浮,根ListItemGroup配合使用,
// .scrollSnapAlign(ScrollSnapAlign.END)//设置列表项滚动结束对齐效果。
// .nestedScroll() 前套滚动
.scrollBar(BarState.Auto) // 是否显示进度条
.scrollBarWidth(2) // api 11
.scrollBarColor(Color.Red) // api 11
.contentStartOffset(0) // api 11 设置头部pading// 相当于padding
.contentEndOffset(0) // api 11 设置下部pading
.onScroll((scrollOffset: number, scrollState: ScrollState) => {
  // 列表滑动时触发。
})
.onScrollFrameBegin((offset: number, state: ScrollState) => {
  // 列表开始滑动时触发,事件参数传入即将发生的滑动量,
  // 事件处理函数中可根据应用场景计算实际需要的滑动量并作为事件处理函数的返回值返回,
  // 列表将按照返回值的实际滑动量进行滑动。
  // 列表滑动时触发。
  return { offsetRemain: 20 }
})
.onScrollIndex((start: number, end: number, center: number) => {
  // 有子组件划入或划出List显示区域时触发。从API version 10开始,List显示区域中间位置子组件变化时也会触发
  console.log(`start = ${start} end = ${end} ,center = ${center}`)
})
.onReachStart(() => {
  // 列表到达起始位置时触发。会触发多次
  //List初始化时如果initialIndex为0会触发一次,
  // List滚动到起始位置时触发一次。
  // List边缘效果为弹簧效果时,划动经过起始位置时触发一次,
  // 回弹回起始位置时再触发一次。
})
.onReachEnd(() => {
})
.onItemMove((from: number, to: number) => {
  //列表元素发生移动时触发。from: 移动前索引值。to: 移动后索引值。
  return true
})
.onItemDragStart((event: ItemDragInfo, itemIndex: number) => {
  //开始拖拽列表元素时触发。event: 见ItemDragInfo对象说明。itemIndex: 被拖拽列表元素索引值
})

.width("90%")
.height('90%')
.friction(0.6)
.border({ width: 3, color: Color.Red })

Grid

网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。

●iff/else语句中,只有条件成立分支内的子组件会参与索引值计算,条件不成立分支内的子组件不计算索引值。

●ForEach/LazyForEach语句中,会计算展开所有子节点索引值。

●if/else、ForEach和LazyForEach发生变化以后,会更新子节点索引值。

●Grid子组件的visibility属性设置为Hidden或None时依然会计算索引值。

●Grid子组件的visibility属性设置为None时不显示,但依然会占用子组件对应的网格。

●Grid子组件设置position属性,会占用子组件对应的网格,子组件将显示在相对Grid左上角偏移position的位置。该子组件不会随其对应网格滚动,在对应网格滑出Grid显示范围外后不显示。

●当Grid子组件之间留有空隙时,会根据当前的展示区域尽可能填补空隙,因此GridItem可能会随着网格滚动而改变相对位置。

Panel

跟 Android BottomSheetDialog 很像

Panel(this.show) { 
  Column() {
    Text('Today Calendar')
  }
}
// PanelType.CUSTOM Api 10 不可手动滑下去,需要设置.customHeight(600)
// .customHeight(PanelHeight.WRAP_CONTENT)// 自适应
// PanelType.Minibar 大小两种状态
// PanelType.Foldable 大中小
// PanelType.Temporary 大中
.type(PanelType.Foldable)//设置可滑动面板的类型。
.mode(PanelMode.Mini)// 当显示的时候的状态,设置可滑动面板的初始状态。 Minibar类型默认值:PanelMode.Mini;其余类型默认值:PanelMode.Half
.dragBar(true) // 默认开启拖拽的小图标 设置是否存在dragbar,true表示存在,false表示不存在。
.showCloseIcon(false) //api 10 显示关闭图标
// .show(true)// 是否显示
// .customHeight(PanelHeight.WRAP_CONTENT)// 自适应
.fullHeight(1000) // 不支持设置百分比
.halfHeight(500)
.miniHeight(100)
.backgroundMask('#a40638')// 显示的时候,相当于他的背景
.onHeightChange((value: number)=>{
  // console.info(`onHeightChange:${value}`)
})
.onChange((width: number, height: number, mode: PanelMode) => {
  console.info(`width:${width},height:${height},mode:${mode}`)
  // 大中小变化的时候,但是,show 隐藏掉,就不会再变化了,下次显示再变化
})

GridRow

栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题,保证不同设备上各个模块的布局一致性。

●columns:默认一个屏幕12个格子,如果没设置就按照12格子,可以按照不同的分辨率设置不同的格子
●GridCol.span :默认占1个格子,也可以按照不同的屏幕分辨率设置不同的格子

GridRow({
  // 列数
  columns: 5,
  // 设置子中间距
  gutter: { x: 5, y: 10 },
  // 方向
  direction: GridRowDirection.Row,
  breakpoints: { value: ["400vp", "600vp", "800vp"],
    reference: BreakpointsReference.WindowSize },
}) {
  ForEach(this.bgColors, (color: Color) => {
    GridCol({ span: { xs: 1, sm: 2, md: 3, lg: 4 }, offset: 0, order: 0 }) {
      Row().width("100%").height("20vp")
    }.borderColor(color).borderWidth(2)
  })
}.width("100%").height("100%")
.onBreakpointChange((breakpoint) => {
  this.currentBp = breakpoint
})

Refresh

下拉组件 ,api 10 之后可以定义下拉样式,但是下拉的高度 不能控制

●refreshing: this.isRefreshing双向绑定使用this.isRefreshing 双向绑定使用this.isRefreshing双向绑定使用

Refresh({ refreshing: $$this.isRefreshing,builder:this.customRefreshComponent()}) {
  List() {
    ForEach(this.arr, (item: string) => {
      ListItem() {
        Text('' + item)
          .width('100%').height(100).fontSize(16)
          .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
      }
    }, (item: string) => item)
  }
  .width('100%')
  .height('100%')
  .divider({strokeWidth:1,color:Color.Yellow,startMargin:10,endMargin:10})
  .scrollBar(BarState.Off)
}
.onStateChange((refreshStatus: RefreshStatus) => {
  console.info('Refresh onStatueChange state is ' + refreshStatus)
})
.onRefreshing(() => {
  setTimeout(() => {
    this.isRefreshing = false
  }, 2000)
  console.log('onRefreshing test')
})

Tabs

相当于tabbar 通过页签进行内容视图切换的容器组件,每个页签对应一个内容视图。

Tabs({ barPosition: BarPosition.End, index: 0, controller: this.tabController }) {
  TabContent() {
    Text('首页')
      .width('100%')
      .height('100%')
      .backgroundColor(Color.Blue)
  }.tabBar(this.tabBuilder($r('app.media.img4'), '一'))


  TabContent() {
    Text('第二页')
  }.tabBar(this.tabBuilder($r('app.media.icon'), '二'))

  TabContent() {
    Text('第三页')
  }.tabBar(this.tabBuilder($r('app.media.img3'), '三'))
}
.vertical(false) // 是否是垂直,默认是false
.barPosition(BarPosition.End) // bar的位置 默认是Start
.scrollable(true) // 是否可以滑动,默认true
// .barMode(BarMode.Scrollable) //abBar布局模式
// .barWidth(100) //TabBar的宽度值
// .barHeight(100)
// .divider({ strokeWidth: 2 })
.barBackgroundColor('#9960cb60') //
.barOverlap(true) //默认false ,是否把设置TabBar放在TabContent之上。// 需要配合bar的颜色
.barBackgroundBlurStyle(BlurStyle.Regular) //api 11 好像是 tabbar高斯模糊效果
.fadingEdge(true) //设置页签超过容器宽度时是否渐隐消失。
.onChange((index: number) => {
  // 选择第几个
})
.onTabBarClick((index: number) => {
  // api 10 tabbar 点击
})
.onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
  //api 11 切换动画开始时触发该回调。
})
.onAnimationEnd((index: number, event: TabsAnimationEvent) => {
  //api 11 切换动画结束时触发该回调。
})
.onGestureSwipe((index: number, event: TabsAnimationEvent) => {
  // api 11 在页面跟手滑动过程中,逐帧触发该回调。
})
// .customContentTransition((from: number, to: number) => {
//   自定义动画效果
//   return null
// })
.width('100%')
.height('100%')
.animationDuration(500) //在api不一样 有不同的默认值
.border({ width: 2, color: '#ffbe0e0e', radius: 10 })

写在最后

●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我两个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing ,不定期分享原创知识。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值