HarmonyOS 鸿蒙Next 请教大家,这种需求应该怎么布局?

HarmonyOS 鸿蒙Next 请教大家,这种需求应该怎么布局?

需求是:在纵向横向列表联动的基础上,可以根据某一个字段决定,是否显示自定义的这一行内容。

这个“大列表”是可以上下滑动,也可以左右滑动,左右滑动时,前面第一列[直播标题]是不可以动的,也就是第一列只可以上下滑动

针对这种需求我应该如何布局?

cke_9949.png

cke_156.png


更多关于HarmonyOS 鸿蒙Next 请教大家,这种需求应该怎么布局?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

11 回复
[@Entry](/user/Entry)
[@ComponentV2](/user/ComponentV2)
struct Index {
[@Local](/user/Local) listLeft: number[] = []

aboutToAppear() {
for (let i = 0; i < 10; i++) {
this.listLeft.push(i)
}
this.listLeft[4] = -1
}

build() {
Scroll() {
Column() {
Row() {
Column() {
ForEach(this.listLeft, (item: number) => {
if (item >= 0) {
Row() {
Text(item.toString() + ' = = 1233123')
.height('100vp')
.width('100%')
.backgroundColor(Color.Blue)
.margin({ top: 4 })
}
} else {
Row() {
Text(item.toString() + ' = = 1233123')
.height('100vp')
.width('500%')
.backgroundColor(Color.Grey)
.margin({ top: 4 })
}
}
})
}.width('20%')
.backgroundColor(Color.Green)
.zIndex(2)

Scroll() {
Column() {
ForEach(this.listLeft, (item: number) => {
Row() {
Text(item.toString())
.height('100vp')
.width('50%')
.backgroundColor(Color.Pink)
.margin({ top: 4 })
Text(item.toString())
.height('100vp')
.width('50%')
.backgroundColor(Color.Pink)
.margin({ top: 4 })
Text(item.toString())
.height('100vp')
.width('50%')
.backgroundColor(Color.Pink)
.margin({ top: 4 })
}
})
}
.backgroundColor(Color.Brown)
.zIndex(1)
}.width('90%')
.scrollable(ScrollDirection.Horizontal)
}
}
}.size({ width: '100%', height: '100%' })
}
}

更多关于HarmonyOS 鸿蒙Next 请教大家,这种需求应该怎么布局?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


用zIndex 把层级的提起来。这样插入的广告可以盖住右边的数据,展示一行。然后两个Scroll分别控制横向滑动、竖向滑动。

[@Entry](/user/Entry)
[@ComponentV2](/user/ComponentV2)
struct Index {
[@Local](/user/Local) listTop: number[] = []
[@Local](/user/Local) listBottom: number[] = []
[@Local](/user/Local) listHorizontal: number[] = []

aboutToAppear(): void {
for (let i = 0; i < 3; i++) {
this.listTop.push(i)
}
for (let i = 0; i < 10; i++) {
this.listBottom.push(i)
}
for (let i = 0; i < 10; i++) {
this.listHorizontal.push(i)
}
}

private scroller: Scroller = new Scroller()
private scrollerTop: Scroller = new Scroller()
private scrollerBottom: Scroller = new Scroller()

build() {
Row() {
Scroll(this.scroller) {
Column() {
Row() {
Column() {
ForEach(this.listTop, (item: number) => {
Text(item.toString())
.height('100vp')
.width("100%")
.backgroundColor(Color.Blue)
.margin({ top: 4 })
})
}.width('20%')

Scroll(this.scrollerTop) {
Column() {
ForEach(this.listTop, (item: number) => {
Row() {
ForEach(this.listHorizontal, (child: number) => {
Text(item.toString() + '-' + child.toString())
.height('20vp')
.width('30vp')
})
}.height('100vp')
.backgroundColor(Color.Orange)
.margin({ top: 4 })
})
}
}.scrollBar(BarState.Off)
.scrollable(ScrollDirection.Horizontal)
.width('80%')
.onWillScroll((x: number, _y: number, _state: ScrollState, source: ScrollSource) => {
if (source == ScrollSource.SCROLLER || source == ScrollSource.SCROLLER_ANIMATION) {
return
}
if (x != 0) {
this.scrollerBottom.scrollBy(x, 0)
}
})
}

Text("我是一个横幅")
Row() {
Column() {
ForEach(this.listBottom, (item: number) => {
Text(item.toString())
.height('100vp')
.width("100%")
.backgroundColor(Color.Blue)
.margin({ top: 4 })
})
}.width('20%')

Scroll(this.scrollerBottom) {
Column() {
ForEach(this.listBottom, (item: number) => {
Row() {
ForEach(this.listHorizontal, (child: number) => {
Text(item.toString() + '-' + child.toString())
.height('20vp')
.width('30vp')
})
}.height('100vp')
.backgroundColor(Color.Orange)
.margin({ top: 4 })
})
}
}.width('80%')
.scrollBar(BarState.Off)
.scrollable(ScrollDirection.Horizontal)
.onWillScroll((x: number, _y: number, _state: ScrollState, source: ScrollSource) => {
if (source == ScrollSource.SCROLLER || source == ScrollSource.SCROLLER_ANIMATION) {
return
}
if (x != 0) {
this.scrollerTop.scrollBy(x, 0)
}
})
}
}
}.scrollBar(BarState.Off)

}.size({ width: '100%', height: '100%' })
}
}

感谢老哥~确实可以实现这种效果,但是需求是这样的,这些列表里,可能有的数据是带直播的,如果直播这个字段有效,就要在列表的下面展示这个横幅,现在老哥的这个布局,确实可以实现,但是右侧的列表用了两个scroller绑定,如果有n个横幅,那么右侧就要有n+1个scroller,这代码冗余太严重了~

楼下,横滑一个scroll ,竖向一个scroll.不管几个横幅,都没问题了

下面的这种布局可以实现 在某一行插入一条自定义布局,但是右侧列表横向滑动时,只能滑动自身,不能把右侧所有的列表都关联起来一起滑动,大家有什么其他的办法吗~~~

[@Entry](/user/Entry)
[@Component](/user/Component)
struct TestPage {
  private dataSource: QuestionData[] = QUESTION_DATA

build() { List() { ForEach(this.dataSource, (item: QuestionData, index: number) => { ListItem() { QuestionItemComponent({ item: item, itemWidth: 120, itemHeight: 100 }) } }, (item: QuestionData, index: number) => ${index}_${item.questCode}_${item.id}) }.width(‘100%’).height(‘100%’).listDirection(Axis.Vertical) } }

@Reusable @Component export struct QuestionItemComponent { @Prop item: QuestionData; @Prop itemWidth: number; @Prop itemHeight: number;

build() { Row() { Text(this.item.questName) .fontColor(Color.Black) .fontWeight(FontWeight.Medium) .fontSize(16) .width(this.itemWidth) .height(‘100%’) .textAlign(TextAlign.Center)

  Scroll() {
    Row() {
      Text(<span class="hljs-keyword">this</span>.item.questCode.toString())
        .width(<span class="hljs-keyword">this</span>.itemWidth)
        .height(<span class="hljs-string">'100%'</span>)
        .textAlign(TextAlign.Center)

      Text(<span class="hljs-keyword">this</span>.item.questName + `%`)
        .fontColor(Color.Black)
        .fontWeight(FontWeight.Medium)
        .fontSize(<span class="hljs-number">16</span>)
        .width(<span class="hljs-keyword">this</span>.itemWidth)
        .height(<span class="hljs-string">'100%'</span>)
        .textAlign(TextAlign.Center)

      Text(<span class="hljs-keyword">this</span>.item.volume)
        .fontColor(Color.Black)
        .fontWeight(FontWeight.Medium)
        .fontSize(<span class="hljs-number">16</span>)
        .width(<span class="hljs-keyword">this</span>.itemWidth)
        .height(<span class="hljs-string">'100%'</span>)
        .textAlign(TextAlign.Center)

      Text(<span class="hljs-keyword">this</span>.item.amount + `%`)
        .fontColor(Color.Black)
        .fontWeight(FontWeight.Medium)
        .fontSize(<span class="hljs-number">16</span>)
        .width(<span class="hljs-keyword">this</span>.itemWidth)
        .height(<span class="hljs-string">'100%'</span>)
        .textAlign(TextAlign.Center)

      Text(<span class="hljs-keyword">this</span>.item.open.toString())
        .fontColor(Color.Black)
        .fontWeight(FontWeight.Medium)
        .fontSize(<span class="hljs-number">16</span>)
        .width(<span class="hljs-keyword">this</span>.itemWidth)
        .height(<span class="hljs-string">'100%'</span>)
        .textAlign(TextAlign.Center)

      Text(<span class="hljs-keyword">this</span>.item.low.toString() + <span class="hljs-string">'%'</span>)
        .fontColor(Color.Black)
        .fontWeight(FontWeight.Medium)
        .fontSize(<span class="hljs-number">16</span>)
        .width(<span class="hljs-keyword">this</span>.itemWidth)
        .height(<span class="hljs-string">'100%'</span>)
        .textAlign(TextAlign.Center)

      Text(<span class="hljs-keyword">this</span>.item.close.toString())
        .fontColor(Color.Black)
        .fontWeight(FontWeight.Medium)
        .fontSize(<span class="hljs-number">16</span>)
        .width(<span class="hljs-keyword">this</span>.itemWidth)
        .height(<span class="hljs-string">'100%'</span>)
        .textAlign(TextAlign.Center)

      Text(<span class="hljs-keyword">this</span>.item.prevClose.toString())
        .fontColor(Color.Black)
        .fontWeight(FontWeight.Medium)
        .fontSize(<span class="hljs-number">16</span>)
        .width(<span class="hljs-keyword">this</span>.itemWidth)
        .height(<span class="hljs-string">'100%'</span>)
        .textAlign(TextAlign.Center)

    }
  }.layoutWeight(<span class="hljs-number">1</span>).height(<span class="hljs-number">50</span>).scrollable(ScrollDirection.Horizontal)
}
.height(<span class="hljs-keyword">this</span>.itemHeight)

} }

class QuestionData { id: number; questCode: string; questName: string; volume: string; amount: string; open: number; low: number; close: number = Number.MIN_VALUE; prevClose: number;

constructor(id: number, questCode: string, questName: string, volume: string, amount: string, open: number, low: number, close: number, prevClose: number) { this.id = id this.questCode = questCode this.questName = questName this.volume = volume this.amount = amount this.open = open this.low = low this.close = close this.prevClose = prevClose } }

const QUESTION_DATA: QuestionData[] = [ new QuestionData(0, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 0, 1), new QuestionData(1, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 1, 1), new QuestionData(2, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), new QuestionData(3, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), new QuestionData(4, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), new QuestionData(5, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), new QuestionData(6, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), new QuestionData(7, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), new QuestionData(8, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), new QuestionData(9, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), new QuestionData(10, “问题代码”, “直播标题”, “成交量”, “成交额”, 1, 1, 2, 1), ] <button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

[@Entry](/user/Entry)
[@ComponentV2](/user/ComponentV2)
struct Index {
[@Local](/user/Local) list: number[] = []
private scrollerLeft: Scroller = new Scroller()
private scrollerRight: Scroller = new Scroller()

aboutToAppear(): void {
for (let i = 0; i < 15; i++) {
this.list.push(i)
}
}

build() {
Row() {
Scroll(this.scrollerLeft) {
Column() {
ForEach(this.list, (item: number) => {
Text(item.toString())
.height('100vp')
.width("100%")
.backgroundColor(Color.Blue)
.margin({ top: 4 })
})
}
}.width('20%')
.scrollBar(BarState.Off)
.onWillScroll((x: number, y: number, state: ScrollState, source: ScrollSource) => {
if (source == ScrollSource.SCROLLER || source == ScrollSource.SCROLLER_ANIMATION) {
return
}
if (y != 0) {
this.scroll(this.scrollerRight, y)
}
})

Scroll(this.scrollerRight) {
Scroll() {
Column() {
ForEach(this.list, (item: number) => {
Row() {
ForEach(this.list, (child: number) => {
Text(item.toString() + '-' + child.toString())
.height('20vp')
.width('30vp')
})
}.height('100vp')
.backgroundColor(Color.Orange)
.margin({ top: 4 })
})
}
}
.scrollBar(BarState.Off)
.scrollable(ScrollDirection.Horizontal)
}.width('80%')
.scrollBar(BarState.Off)
.scrollable(ScrollDirection.Vertical)
.onWillScroll((x: number, y: number, state: ScrollState, source: ScrollSource) => {
if (source == ScrollSource.SCROLLER || source == ScrollSource.SCROLLER_ANIMATION) {
return
}
if (y != 0) {
this.scroll(this.scrollerLeft, y)
}
})
}.size({ width: '100%', height: '100%' })
}

scroll(follow: Scroller, yOffset: number) {
follow.scrollBy(0, yOffset)
}
}

右边因为嵌套,一次触摸,只能横滑或者竖滑。有点傻,但整体需求是实现了的

这个滑动没问题,是这个效果,这个功能我也实现了,但是,难点是如果在某一行插入一条自定义布局(插入的这一行,要从屏幕的最左边开始,在右边scroll左右滑动的时候,插入的这一行不可滚动)

看楼下代码,根据不能滑动的Item分成上下两部分,通过scroller 来联动一起横滑

针对HarmonyOS鸿蒙Next的布局需求,以下是一些专业建议:

首先,需明确布局的具体要求,如设备类型(手机、平板、折叠屏等)、屏幕尺寸、分辨率以及交互方式等。根据这些要求,可以选择合适的布局方式。

对于多端页面布局,鸿蒙Next支持自适应布局和响应式布局。自适应布局能根据设备环境动态调整布局结构、图片尺寸和字体大小等;响应式布局则利用断点、媒体查询等技术,使页面元素能自动调整样式。

在实现布局时,可借助鸿蒙Next提供的布局组件,如Flex、Row、Column、StackLayout等。这些组件提供了丰富的布局能力和对齐方式,能满足复杂的布局需求。

例如,若要实现一个包含可滚动头部、粘性头部和可滚动内容区域的协调布局,可以使用CoordinatorLayout作为主布局容器,配合ScrollableHeaderBuilder、StickyHeaderBuilder和ContentBuilder等自定义Builder来实现。

如果问题依旧没法解决,请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部