HarmonyOS flex容器布局

图片中的搜索历史使用flex弹性布局自动换行,当内容过多默认最多显示3行,并显示折叠按钮问题:若第三行最后一个item刚好显示到最右侧,此时添加折叠按钮第三行显示不下会自动在第四行显示按钮,但要求此种情况下自动隐藏第三行的最后一个item,让折叠按钮在第三行显示,如何实现此效果?

HarmonyOS flex容器布局-鸿蒙开发者社区

HarmonyOS
2024-09-27 13:43:21
浏览
收藏 0
回答 2
待解决
回答 2
按赞同
/
按时间
鱼弦CTO
1

在 HarmonyOS 中使用 Flex 容器布局来实现自动换行,并且在内容过多时显示折叠按钮的效果,需要一些自定义逻辑来控制最后一行的展示和折叠按钮的位置。你可以通过监听布局的变化,动态调整最后一行的元素和折叠按钮的位置。

### 实现思路

  1. 使用 Flex 布局:设置容器为 Flex 布局,使其子元素自动换行。
  2. 限制显示行数:限制最多显示 3 行内容。
  3. 添加折叠按钮:在第三行末尾添加一个折叠按钮。
  4. 动态调整:如果第三行的最后一个 item 超出宽度,则隐藏它并确保折叠按钮能正确显示在第三行。

### 示例代码

下面是一个示例代码,展示如何实现这一效果:

// Import necessary modules
import { Column, Flex, Text, Button, View, Stack } from 'ohos_component';
import { Rect } from 'ohos_struct';

@Entry
@Component
struct MainComponent {
  private items: string[] = ['Item1', 'Item2', 'Item3', 'Item4', 'Item5', 'Item6', 'Item7'];
  private maxVisibleRows: number = 3;
  private showAll: boolean = false;
  private containerWidth: number = 0;

  build() {
    Column() {
      // Container for search history items and fold button
      View() {
        Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {  
          this.items.map((item, index) => {
            if (this.showAll || this.isWithinMaxVisibleRows(index)) {
              Text(item)
                .padding(10)
                .backgroundColor(Color.Gray)
                .margin({ right: 10, bottom: 10 })
                .width('auto')
                .onLayout((event: LayoutEvent) => {
                  // Capture the width of the container
                  if (index === 0) {
                    this.containerWidth = event.layout.width;
                  }
                });
            }
          });
          
          // Only show fold button when necessary
          if (!this.showAll && this.moreItemsThanMaxVisibleRows()) {
            Button('Fold')
              .onClick(() => {
                this.showAll = true;
              })
              .margin({ right: 10, bottom: 10 })
              .width('auto');
          }
        }
        .width('100%')
        .onLayout((event: LayoutEvent) => {
          this.adjustLastRow();
        });
      }
    }
    .padding(16);
  }

  private isWithinMaxVisibleRows(index: number): boolean {
    const itemsPerRow = Math.floor(this.containerWidth / 100); // Assuming each item ~100px wide
    return Math.floor(index / itemsPerRow) < this.maxVisibleRows;
  }

  private moreItemsThanMaxVisibleRows(): boolean {
    const itemsPerRow = Math.floor(this.containerWidth / 100); // Assuming each item ~100px wide
    return this.items.length > itemsPerRow * this.maxVisibleRows;
  }

  private adjustLastRow() {
    const itemsPerRow = Math.floor(this.containerWidth / 100); // Assuming each item ~100px wide
    const visibleItemsCount = itemsPerRow * this.maxVisibleRows;
    if (this.items.length > visibleItemsCount) {
      // Hide the last item in the third row
      const lastVisibleIndex = visibleItemsCount - 1;
      this.items[lastVisibleIndex].visible = false; 
    }
  }
}

### 详细解释

  1. Flex 布局
  • 使用​​Flex​​ 布局管理搜索历史项的排列和换行。
  • 设置方向为​​Row​​,并启用​​wrap​​ 使其自动换行。
  1. 限制显示行数
  • 通过计算每行能够容纳的 item 数量,使用​​maxVisibleRows​​ 限制显示的行数。
  1. 添加折叠按钮
  • 如果超过了最大显示行数,则在第三行末尾添加折叠按钮,设置其​​onClick​​ 事件以切换​​showAll​​ 状态。
  1. 动态调整
  • 在布局完成后,通过​​onLayout​​ 事件获取容器的宽度,计算每行可以容纳的 item 数量。
  • 动态检查是否需要隐藏第三行最后一个 item,以便显示折叠按钮。

### 注意事项

  • 样式调整:根据实际项目需求调整样式、间距等。
  • 动态计算宽度:假设每个 item 的宽度大致相同,可以根据实际情况调整宽度计算方法。
  • 性能优化:对于大数据量,可以考虑优化渲染逻辑。

通过上述步骤,可以实现搜索历史项在平板设备上 using Flex 布局自动换行,并在必要时显示折叠按钮,同时确保最后一行的 item 和折叠按钮布局合理。如果有更多问题或特别需求,请随时提问。

分享
微博
QQ
微信
回复
2024-09-27 16:06:35
superinsect

需要通过Flex布局,并且将折叠按钮根据布局情况存放到第三行末尾的应用场景。以下是实现思路:

1、通过onAreaChange回调,收集各子项宽度和Flex组件自身宽度。

2、收集完成后,通过计算得出最大行数能容纳的子项个数。

3、查看最后一行是否能防止折叠按钮,若不能,则削减子项个数,直到能为止样例代码如下:

@Entry  
@Component  
struct FlexTest {  
  @State message: string = 'Hello World';  
  @State allDataList: string[] = ['1', '222', '33333', '44', '1', '222', '33333', '44', '1', '222', '33333', '44']  
  @State showDataList: string[] = []  
  @State isShowAll: boolean = true  
  private cutIndex: number = 0  
  private flexWidth: number = 0  
  private itemWidthList: number[] = []  
  private isInit: boolean = false  
  aboutToAppear(): void {  
    this.initItemList()  
  }  
  initItemList() {  
    this.showDataList = this.allDataList  
    this.itemWidthList = new Array<number>(this.allDataList.length)  
    for (let i = 0; i < this.itemWidthList.length; i++) {  
      this.itemWidthList[i] = 0  
    }  
    this.isInit = false  
  }  
  isDrawFinish() {  
    let isFinish = true;  
    for (let i = 0; i < this.itemWidthList.length; i++) {  
      if (this.itemWidthList[i] == 0) {  
        isFinish = false  
      }  
    }  
    if (this.flexWidth == 0) {  
      isFinish = false;  
    }  
if (isFinish) {  
  this.isInit = true  
  this.calculateCutIndex(3)  
  this.transformExpendList(false)  
}  
  }  
  transformExpendList(showAll: boolean) {  
    if (showAll) {  
      this.isShowAll = true  
      this.showDataList = this.allDataList  
    } else {  
      this.isShowAll = false  
      this.showDataList = this.allDataList.slice(0, this.cutIndex)  
    }  
  }  
  // maxLine指布局需要保留的最大行数  
  calculateCutIndex(maxLine: number) {  
    let layer = 1;  
    let curLength = 0;  
    for (let i = 0; i < this.itemWidthList.length; i++) {  
      curLength += this.itemWidthList[i]  
      if (curLength > this.flexWidth) {  
        if (layer == maxLine) {  
          curLength -= this.itemWidthList[i]  
          this.cutIndex = i  
          break  
        } else {  
          layer += 1  
          curLength = this.itemWidthList[i]  
        }  
      }  
    }  
let buttonWidth = 40  
let empty = this.flexWidth - curLength  
while (empty <= buttonWidth) {  
  empty += this.itemWidthList[this.cutIndex]  
  this.cutIndex--  
}  
  }  
  build() {  
    RelativeContainer() {  
      Flex({wrap: FlexWrap.Wrap, alignItems: ItemAlign.Center}) {  
        ForEach(this.showDataList, (item: string, index: number) => {  
          Text(item)  
            .fontSize(20)  
            .fontWeight(FontWeight.Bold)  
            .fontColor(Color.Black)  
            .backgroundColor(Color.Gray)  
            .borderRadius(10)  
            .padding(8)  
            .margin(5)  
            .id(item_${index})  
            .onAreaChange((oldValue: Area, newValue: Area) => {  
              if (!this.isInit && newValue.width != 0) {  
                this.itemWidthList[index] = Number(newValue.width) + 10 // item宽度加上两边margin  
                this.isDrawFinish()  
              }  
            })  
        })  
    Button() {  
      Text(this.isShowAll ? '^' : 'v')  
        .padding(10)  
        .fontColor(Color.White)  
        .borderRadius(5)  
    }  
    .width(40)  
    .onClick(() => {  
      this.transformExpendList(!this.isShowAll)  
    })  
  }  
  .onAreaChange((oldValue: Area, newValue: Area) => {  
    if (!this.isInit && newValue.width != 0) {  
      this.flexWidth = Number(newValue.width)  
      this.isDrawFinish()  
    }  
  })  
  .backgroundColor(Color.Red)  
  .id("flex")  
  .width("50%")  
}  
.height('100%')  
.width('100%')  
  }  
}
分享
微博
QQ
微信
回复
2024-09-27 17:26:43
相关问题
HarmonyOS Flex 布局设置问题
453浏览 • 1回复 待解决
如何阻止Flex容器鼠标事件穿透
2014浏览 • 1回复 待解决
如何优化Flex布局性能
537浏览 • 1回复 待解决
Flex布局与w3c中的flex是否有差异
949浏览 • 1回复 待解决