HarmonyOS 鸿蒙Next List如何实现关联滑动
父组件
@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
[@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) => {
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) => {
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.rightOffset == <span class="hljs-number">0</span> && 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) => {
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 < <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 > 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 < <span class="hljs-keyword">this</span>.leftStart || titlePosition > <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实现关联滑动,通常通过列表组件的联动属性设置和数据同步机制来完成。以下是实现关联滑动的基本步骤:
-
定义列表组件: 使用
List
或RecyclerView
组件来定义需要关联滑动的列表。确保每个列表的id
属性唯一。 -
设置联动属性: 在布局文件中或通过代码设置列表的联动属性。鸿蒙系统提供了相应的API来设置组件间的联动关系。
-
数据同步: 确保不同列表间的数据同步。当一个列表滑动时,通过事件监听器获取滑动位置,并根据该位置更新其他列表的显示内容。
-
事件监听: 为每个列表添加滑动事件监听器,监听滑动事件并触发数据更新和联动操作。
-
优化性能: 在大数据量或复杂布局下,使用分页加载和懒加载等技术优化性能,确保滑动流畅。
-
调试与测试: 在不同设备和屏幕尺寸下进行测试,确保关联滑动效果一致且稳定。
如果上述步骤未能完全解决您的问题,可能是特定场景下的实现细节有所差异。此时,建议直接查阅鸿蒙官方文档或示例代码,获取更具体的实现方法。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html