HarmonyOS 鸿蒙Next List如何实现关联滑动

发布于 1周前 作者 gougou168 最后一次编辑是 5天前 来自 鸿蒙OS

父组件

@Component
export struct ToolStartPage {
  @StorageProp('bottomRectHeight')
  bottomRectHeight: number = 0;
  @StorageProp('topRectHeight')
  topRectHeight: number = 0;
  @State selectNumber:number=0;
  build(){
    Row(){
      Column(){
          ToolsLeftListView({onItemClick:(index: number)=>{
            this.selectNumber=index;
            MyToastUtils.showToast("选择了"+index)
          },selectNumber:this.selectNumber});
      }
      .width(SizeUtil.getVp(76))
      .height('100%')
      .backgroundColor('#F2F3F5')
      .borderRadius({topRight:SizeUtil.getVp(16)})
      Column(){
        Row(){
          Image($r('app.media.tools_search'))
            .width(SizeUtil.getVp(16))
            .margin({left:SizeUtil.getVp(12)})
          Text('搜索关键词进行查询')
            .fontSize(SizeUtil.getFp(13))
            .fontColor('#C9CDD4')
            .margin({left:SizeUtil.getVp(8)})
        }
        .width('100%')
        .height(SizeUtil.getVp(40))
        .backgroundColor('#F7F8FA')
        .borderRadius(SizeUtil.getVp(12))
        .alignItems(VerticalAlign.Center)
        .margin({left:SizeUtil.getVp(16),right:SizeUtil.getVp(16)})
        .onClick(()=>{

        })
        ToolsRightListView({onItemClick:(index: number)=>{
          this.selectNumber=index;
          MyToastUtils.showToast("滑动到了"+index)
        },scrollNumber:this.selectNumber});
      }
      .layoutWeight(1)
      .height('100%')
      .padding({left:SizeUtil.getVp(16),right:SizeUtil.getVp(16)})
    }
    .height('100%')
    .width('100%')
    .backgroundColor('#fff')
    .alignItems(VerticalAlign.Top)
    .padding({top:this.topRectHeight+SizeUtil.getVp(8)})
  }
}

子组件 ToolsLeftListView

@Component
export struct ToolsLeftListView {
  @State toolsList: ToolsItemClass[] = [];
  @State selectNumber: number=0
  private listScroller: Scroller = new Scroller();
  onItemClick?: (index: number) => void;
  build() {
      List({scroller: this.listScroller}){
        ForEach(this.toolsList,(item:ToolsItemClass,index:number)=>{
          ListItem() {
            ToolsLeftListItem({toolsClass:item,index:index,onItemClick:(index: number)=>{
              this.selectNumber=index;
              this.onItemClick?.(index)
            },selectNumber:this.selectNumber})
          }
        },(index:number)=>index.toString())
      }
      .width('100%')
      .scrollBar(BarState.Off)
      .padding({top:SizeUtil.getVp(15),bottom:SizeUtil.getVp(15)})
      .height('100%')
      .edgeEffect(EdgeEffect.None)
  }
  getListData(){
    getContext(this).resourceManager.getRawFileContent('ToolsData.json').then(value => {
      // 转换为字符串
      let res: string =  MyUtils.bufferToString(value);
      // 解析为数据结构
      this.toolsList = JSON.parse(res) as ToolsItemClass[];
    })
  }
  aboutToAppear(): void {
    this.getListData();
  }
}

@Component
struct ToolsLeftListItem {
  @Prop toolsClass: ToolsItemClass;
  @Prop index:number=0
  onItemClick?: (index: number) => void;
  @Prop selectNumber:number=0;
  build() {
    RelativeContainer(){
      Row(){

      }
      .width(SizeUtil.getVp(3))
      .height(SizeUtil.getVp(16))
      .backgroundColor(this.selectNumber==this.index?'#2C6BE8':'#F2F3F5')
      .borderRadius({topLeft:SizeUtil.getVp(2),bottomLeft:SizeUtil.getVp(2)})
      .alignRules({
        right:{anchor: "__container__",align:HorizontalAlign.End}
      })
      .id('titleText')
      Text(this.toolsClass.title)
        .fontColor(this.selectNumber==this.index?'#2C6BE8':'#1D2129')
        .fontSize(SizeUtil.getFp(12))
        .alignRules({
          center:{anchor: "titleText",align:VerticalAlign.Center},
          middle:{anchor: "__container__",align:HorizontalAlign.Center}
        })
    }
    .width('100%')
    .height('auto')
    .padding({top:SizeUtil.getVp(15),bottom:SizeUtil.getVp(15)})
    .onClick(()=>{
      //不判断的undefined的时候可以用这个
      // this.onItemClick?.(this.index)
      if(this.onItemClick!=undefined){
        this.onItemClick(this.index)
      }
    })
  }
}

子组件ToolsRightListView


@Component
export struct ToolsRightListView {
  @State toolsList: ToolsItemClass[] = [];
  @State selectNumber: number=0
  private listScroller: Scroller = new Scroller();
  onItemClick?: (index: number) => void;
  @Prop scrollNumber:number=0;
  build() {
      List({scroller: this.listScroller,initialIndex:this.scrollNumber}){
        ForEach(this.toolsList,(item:ToolsItemClass,index:number)=>{
          ListItem() {
            ToolsRightListItem({toolsClass:item})
          }
        },(index:number)=>index.toString())
      }
      .width('100%')
      .scrollBar(BarState.Off)
      .divider({strokeWidth:SizeUtil.getVp(1),color:'#E4E7ED',startMargin:SizeUtil.getVp(4)})
      .padding({top:SizeUtil.getVp(15),bottom:SizeUtil.getVp(15)})
      .height('100%')
      .onScrollIndex((firstIndex: number) =>{
        if(firstIndex!=this.scrollNumber){
          this.onItemClick?.(firstIndex);
          this.scrollNumber=firstIndex;
        }
      })
      .edgeEffect(EdgeEffect.None)
      .contentEndOffset(510)
  }
  getListData(){
    getContext(this).resourceManager.getRawFileContent('ToolsData.json').then(value => {
      // 转换为字符串
      let res: string =  MyUtils.bufferToString(value);
      // 解析为数据结构
      this.toolsList = JSON.parse(res) as ToolsItemClass[];
    })
  }
  aboutToAppear(): void {
    this.getListData();
  }
}

@Component
struct ToolsRightListItem {
  @Prop toolsClass: ToolsItemClass;
  build() {
    Column(){
      if(this.toolsClass.type=="1"){
        List(){
          ForEach(this.toolsClass.data,(item:ToolsDataBean1,index:number)=>{
            ListItem() {
              ToolsRightListItem3({toolsClass:item})
            }
          },(index:number)=>index.toString())
        }
        .width('100%')
        .scrollBar(BarState.Off)
        .divider({strokeWidth:SizeUtil.getVp(1),color:'#E4E7ED',startMargin:SizeUtil.getVp(4)})
        .edgeEffect(EdgeEffect.None)
      }else {
        Text(this.toolsClass.title)
          .fontColor('#1D2129')
          .fontSize(SizeUtil.getFp(14))
          .fontWeight(FontWeight.Medium)
          .margin({top:SizeUtil.getVp(32)})
        Grid(){
          ForEach(this.toolsClass.data,(item:ToolsDataBean1,index:number)=>{
            GridItem() {
              ToolsRightListItem2({toolsClass:item})
            }
          },(index:number)=>index.toString())
        }
        .columnsTemplate('1fr 1fr 1fr')
        .layoutDirection(GridDirection.Row)
        .rowsGap(SizeUtil.getVp(16))
        .columnsGap(SizeUtil.getVp(12))
        .padding({top:SizeUtil.getVp(16),bottom:SizeUtil.getVp(32)})
      }
    }
    .width('100%')
    .height('auto')
    .alignItems(HorizontalAlign.Start)
  }
}

@Component
struct ToolsRightListItem2 {
  @Prop toolsClass: ToolsDataBean1;
  build() {
    Column(){
      ImageTextView({
        imageSrc: $r('app.media.components_pdftoword'),
        imageWidth: SizeUtil.getVp(40),
        title: this.toolsClass.title,
        viewType:OrientationEnum.top,
        marginSize:SizeUtil.getVp(2),
        textSize:SizeUtil.getFp(10)
      })
    }
    .width('100%')
    .height('auto')
    .alignItems(HorizontalAlign.Center)
  }
}

@Component
struct ToolsRightListItem3 {
  @Prop toolsClass: ToolsDataBean1;
  build() {
    Column(){
      Text(this.toolsClass.title)
        .fontColor('#4E5969')
        .fontSize(SizeUtil.getFp(12))
        .margin({top:SizeUtil.getVp(32)})
      Grid(){
        ForEach(this.toolsClass.data,(item:ToolsDataBean2,index:number)=>{
          GridItem() {
            ToolsRightListItem4({toolsClass:item})
          }
        },(index:number)=>index.toString())
      }
      .columnsTemplate('1fr 1fr 1fr')
      .layoutDirection(GridDirection.Row)
      .rowsGap(SizeUtil.getVp(16))
      .columnsGap(SizeUtil.getVp(12))
      .padding({top:SizeUtil.getVp(16),bottom:SizeUtil.getVp(32)})
    }
    .width('100%')
    .height('auto')
    .alignItems(HorizontalAlign.Start)
  }
}
@Component
struct ToolsRightListItem4 {
  @Prop toolsClass: ToolsDataBean2;
  build() {
    Column(){
      ImageTextView({
        imageSrc: $r('app.media.components_pdftoword'),
        imageWidth: SizeUtil.getVp(40),
        title: this.toolsClass.title,
        viewType:OrientationEnum.top,
        marginSize:SizeUtil.getVp(2),
        textSize:SizeUtil.getFp(10)
      })
    }
    .width('100%')
    .height('auto')
    .alignItems(HorizontalAlign.Center)
  }
}

我需要实现ToolsLeftListView与ToolsRightListView 关联 如点击ToolsLeftListView的item让ToolsRightListView的List滚动到对应的下标位置 然后手动滑动ToolsRightListView同样修改ToolsLeftListView的选中下标 我的代码中测试执行了父组件中的onItemClick 但是 两边的List组件没有任何反应

还需要实现每个item都能滚动到顶部也就是列表的最后一个item后面要补一段空白高度 要怎么处理


更多关于HarmonyOS 鸿蒙Next List如何实现关联滑动的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

6 回复

1楼回的测试是可以达到效果,楼主你贴的代码缺少一些文件,如果必须要使用你的代码,麻烦提供更完善的demo。

列表关联联动,有个官方的实践可以参考:

https://developer.huawei.com/consumer/cn/forum/topic/0203163366510487708

更多关于HarmonyOS 鸿蒙Next List如何实现关联滑动的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


深色代码主题
复制
[@Entry](/user/Entry)
[@ComponentV2](/user/ComponentV2)
struct SideBySideListIndex {
  [@Local](/user/Local) titles: string[] = []
  [@Local](/user/Local) allContent: Array<string | number> = []
  private titlePositionInAll: number [] = []
  [@Local](/user/Local) leftOffset: number = 0
  [@Local](/user/Local) rightOffset: number = 0
  [@Local](/user/Local) titleSelect: number = 0
  private rightScroller: ListScroller = new ListScroller()
  private leftScroller: ListScroller = new ListScroller()
  private leftStart: number = 0
  private leftEnd: number = 0

aboutToAppear(): void { let titleInAll = 0 for (let i = 0; i < 20; i++) { this.titles.push(‘标题’ + i) this.allContent.push(‘标题’ + i) this.titlePositionInAll.push(titleInAll) let child = 15 * Math.random() for (let j = 0; j < child; j++) { this.allContent.push(j) } titleInAll = this.allContent.length } }

build() { Row() { List({ scroller: this.leftScroller }) { ForEach(this.titles, (item: string, index: number) => { ListItem() { Text(item) .width(‘100%’) .height(‘49vp’) .fontColor(this.titleSelect === index ? Color.White : Color.Black) .textAlign(TextAlign.Center) .onClick(() => { if (this.titleSelect == index) { return } this.titleSelect = index this.rightScroller.scrollToIndex(this.titlePositionInAll[index], false) }) } }) } .width(‘35%’) .height(‘100%’) .backgroundColor(Color.Green) .divider({ strokeWidth: ‘1vp’, color: Color.Gray }) .contentEndOffset(this.leftOffset) .onSizeChange((_, newValue) => { if (this.leftOffset == 0 && newValue.height) { this.leftOffset = (newValue.height as number) - 49 hilog.debug(0x000000, ‘rainrain’, 'leftOffset == ’ + this.leftOffset) } }) .onScrollVisibleContentChange((start, end) => { this.leftStart = start.index this.leftEnd = end.index })

  Column() {
    Text(<span class="hljs-string">'搜索'</span>)
      .width(<span class="hljs-string">'100%'</span>)
      .height(<span class="hljs-string">'50vp'</span>)
      .textAlign(TextAlign.Center)
      .backgroundColor(Color.Blue)
    List({ scroller: <span class="hljs-keyword">this</span>.rightScroller }) {
      ForEach(<span class="hljs-keyword">this</span>.allContent, (item: string | number) =&gt; {
        ListItem() {
          <span class="hljs-keyword">if</span> (typeof item == <span class="hljs-string">'string'</span>) {
            Text(item)
              .width(<span class="hljs-string">'100%'</span>)
              .height(<span class="hljs-string">'49vp'</span>)
              .textAlign(TextAlign.Center)
          } <span class="hljs-keyword">else</span> {
            Text(item.toString())
              .width(<span class="hljs-string">'100%'</span>)
              .height(<span class="hljs-string">'25vp'</span>)
              .textAlign(TextAlign.Center)
              .backgroundColor(Color.White)
          }
        }
      })
    }
    .width(<span class="hljs-string">'100%'</span>)
    .layoutWeight(<span class="hljs-number">1</span>)
    .backgroundColor(Color.Pink)
    .contentEndOffset(<span class="hljs-keyword">this</span>.rightOffset)
    .onSizeChange((_, newValue) =&gt; {
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.rightOffset == <span class="hljs-number">0</span> &amp;&amp; newValue.height) {
        <span class="hljs-keyword">this</span>.rightOffset = (newValue.height <span class="hljs-keyword">as</span> number) - <span class="hljs-number">25</span>
      }
    })
    .onScrollVisibleContentChange((start) =&gt; {
      hilog.debug(<span class="hljs-number">0x000000</span>, <span class="hljs-string">'rainrain'</span>, <span class="hljs-string">'start == '</span> + start.index)
      let titlePosition = <span class="hljs-number">0</span>
      <span class="hljs-keyword">for</span> (let i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-keyword">this</span>.titlePositionInAll.length; i++) {
        let index = <span class="hljs-keyword">this</span>.titlePositionInAll[i]
        <span class="hljs-keyword">if</span> (index == start.index) {
          titlePosition = i
          <span class="hljs-keyword">break</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (start.index &gt; index) {
          titlePosition = i
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">break</span>;
        }
      }
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.titleSelect != titlePosition) {
        <span class="hljs-keyword">this</span>.titleSelect = titlePosition
        <span class="hljs-keyword">if</span> (titlePosition &lt; <span class="hljs-keyword">this</span>.leftStart || titlePosition &gt; <span class="hljs-keyword">this</span>.leftEnd) {
          <span class="hljs-keyword">this</span>.leftScroller.scrollToIndex(titlePosition, <span class="hljs-literal">true</span>)
        }
      }
    })
  }.width(<span class="hljs-string">'65%'</span>)
}

} }

你这个是同一个页面 我的是拆分的 有点区别

onScrollVisibleContentChange 配合 scroller.scrollToIndex来实现并联滑动。 onSizeChange计算留白,contentOffset来使用留白。 同一个页面,是因为简单方便逻辑清晰,需要的人用的时候直接一键复制就好了。不需要在意其他逻辑。 咱是数据传递有些困难吗?我到是可以分类的版本。

.onSizeChange((_, newValue) => { if (this.rightOffset == 0 && newValue.height) { this.rightOffset = (newValue.height as number) - 25 } })

这段代码计算留白没理解 放代码里也没有效果

HarmonyOS 鸿蒙Next List实现关联滑动,通常通过列表组件的联动属性设置和数据同步机制来完成。以下是实现关联滑动的基本步骤:

  1. 定义列表组件: 使用ListRecyclerView组件来定义需要关联滑动的列表。确保每个列表的id属性唯一。

  2. 设置联动属性: 在布局文件中或通过代码设置列表的联动属性。鸿蒙系统提供了相应的API来设置组件间的联动关系。

  3. 数据同步: 确保不同列表间的数据同步。当一个列表滑动时,通过事件监听器获取滑动位置,并根据该位置更新其他列表的显示内容。

  4. 事件监听: 为每个列表添加滑动事件监听器,监听滑动事件并触发数据更新和联动操作。

  5. 优化性能: 在大数据量或复杂布局下,使用分页加载和懒加载等技术优化性能,确保滑动流畅。

  6. 调试与测试: 在不同设备和屏幕尺寸下进行测试,确保关联滑动效果一致且稳定。

如果上述步骤未能完全解决您的问题,可能是特定场景下的实现细节有所差异。此时,建议直接查阅鸿蒙官方文档或示例代码,获取更具体的实现方法。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部