最近临时做下react native的项目开发,有个功能点是这样:在一个选项卡组件里(用了ScrollView,其中放置Tab组件),做一个纵向时间轴效果(用Tab组件里放置Flatlist组件)。点击不同的选项卡,定位到具体时间轴上的数据。示意图如下:
一般时间轴可以用Flatlist组件来实现,github上这个老哥开源的组件就相当好用:https://github.com/thegamenicorus/react-native-timeline-listview。不过他这个是基于ListView的,可以找下Flatlist的相关实现。
各个组件之间层级示意图:
说回正题,准确跳转的这个难点在于,ScrollView和Flatlist是同一方向的滚动(由上往下滚动),所以直接调用Flatlist的 scrollToItem、scrollToIndex可能没效果。即使有效果,也是在Flatlist上滚动,外层的ScorllView可能无法滚动到准确位置。
想了个解决方法,在时间轴组件里,每行数据渲染的时候,将该行高度存起来。当选项卡组件点击不同的选项卡时,调用时间轴里自定义的方法,将要滚动的总高度(0..n行总共高度)回传给选项卡组件,调用Scrollview的scrollTo进行滚动。
具体各个组件改动如下。
CustomTimelineFlat(自定义的时间轴组件)
this.state.flatListHeight //在state里声明一个用来存储每行index和该行最终高度的关系的Map
//每行render的时候,在onLayout方法里,将高度和该行index存入
return <View key={rowID}
onLayout = {(event) => {
const {
x,
y,
width,
height
} = event.nativeEvent.layout;
let old = this.state.flatListHeight
console.warn('onLayout '+JSON.stringify(old))
old[index] = height
this.setState({
flatListHeight:old
})
}}
>{content}</View>;
//自定义一个方法供外部调用,返回0..n行的总高度
scrollTo(index){
console.warn('in CusttomTimeLineFlat index '+index)
let all = 0
//使用比index小的index对应的高度进行叠加
for(let i = 0; i < index; i++) {
all += this.state.flatListHeight[i]
}
console.warn('after flatListRef scorll,all is '+all)
return all
}
ScorllView和Tab的页面里的代码示例如下:
//在SrollView初始化时声明该组件的ref
<ScrollView
ref={c => (this.detailScrollRef = c)}
stickyHeaderIndices={[0]}
nestedScrollEnabled={true}
>
其中放置了Tab组件。
…
</ScorllView>
//初始化时间轴组件,声明该组件的ref供外部调用
<CustomTimelineFlat
ref={c => (this.detailTimelineRef = c)}
data={eventList}
/>
//当点击选项卡时,调用下面这个方法,使页面滚动到具体的某行
handleSubTabChange(subTab){
let y = 0
switch(subTab){
case 21:
y = this.detailTimelineRef.scrollTo(0)
break;
case 22:
y = this.detailTimelineRef.scrollTo(1)
break
default:
y = height/2
break;
}
//调用ScrollView的ref进行滚动
this.detailScrollRef.scrollTo({x:0,y:y,animated:true})
}
至此大功告成。