- 记录
React Native
中如何实现下拉自动切换分类
- 针对 IOS 和 Android 平台差异,给出不同的切换分类触发条件
需求描述
如图所示,整体为 FlatList
,顶部分类栏吸顶,底部为 feed
流。要实现下拉商品列表到底后,继续下拉,自动切换到一下个分类的效果。
实现原理
代码层面,可以在 FlatList
的 onScrollEndDrag
中添加自动 Tab 切换函数,借助 FlatList
实例的内容区高度 contentLength
,滑动偏移量 offset
和可视区高度 visibleLength
三者关系,实现下拉自动切换Tab功能。
代码实现
IOS 平台实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <FlatList data = {[{banner:[]},{tab:[]},{goodList:[]}]} renderItem={this.renderItem} stickyHeaderIndices={(Platform.OS !== 'web')?[1]:null} ListFooterComponent={this._renderFooter} onScroll={this._onScroll} ref={this._setScrollRef} keyExtractor = {(item, index) => { `hScrollView-${index}` }} refreshing={this.state.isRefreshing} onRefresh={this._onRefresh.bind(this)} getItemLayout={(data, index) => ( {length: 305, offset: 305 * index, index} )} onScrollEndDrag = {()=>{ if(Platform.OS != 'web'){ this._onScrollEndDragFun(); } }} />
_setScrollRef = (ref) => { this._secondGoodFlatListRef = ref; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import isEmpty from "lodash/isEmpty"; import {Platform} from "react-native"; import {JDDevice} from "@jdreact/jdreact-core-lib";
_onScrollEndDragFun = (e) => { let scrollMetrics = (this._secondGoodFlatListRef && this._secondGoodFlatListRef._listRef && this._secondGoodFlatListRef._listRef._scrollMetrics) || null;
let {contentLength = 0, offset = 0, visibleLength = 0} = scrollMetrics;
if (contentLength && offset && visibleLength) { let {selectedIndex = 0} = this.state; let {tabListData = []} = this.props;
if (!isEmpty(tabListData) && (selectedIndex + 1) < tabListData.length) { let item = tabListData[selectedIndex + 1]; if (Platform.OS === 'ios') { if (offset + visibleLength > contentLength + JDDevice.getRpx(100)) { this._secondGoodFlatListRef.scrollToIndex({ animated: false, index: 0, viewOffset: 1, viewPosition: 0 }); this.ItemCategory._clickCategoryTab2 && this.ItemCategory._clickCategoryTab2(item, selectedIndex + 1); } } else { if (offset + visibleLength > contentLength - JDDevice.getRpx(10)) { this._secondGoodFlatListRef.scrollToIndex({ animated: false, index: 0, viewOffset: 1, viewPosition: 0 }); this.ItemCategory._clickCategoryTab2 && this.ItemCategory._clickCategoryTab2(item, selectedIndex + 1); } } }
} };
|
其中,contentLength
为内容区高度,offset
为滑动偏移量,visibleLength
为可视区高度。
关于三种高度定义,可参考 React Native中ListView和ScrollView实现上拉加载
1 2 3 4 5 6 7 8
| let scrollMetrics = (this._secondGoodFlatListRef && this._secondGoodFlatListRef._listRef && this._secondGoodFlatListRef._listRef._scrollMetrics) || null;
let { contentLength = 0, offset = 0, visibleLength = 0 } = scrollMetrics;
|
Android 平台实现
对于 Android 平台,当 offset + visibleLength = contentLength
时,表示滑动到底部。为了以前进行切换,对条件进行修正,当滑动到距离底部 10px 时,触发切换 Tab 函数,如下代码所示
1 2 3 4 5 6 7 8 9 10 11
| if (offset + visibleLength > contentLength - JDDevice.getRpx(10)) { this._secondGoodFlatListRef.scrollToIndex({ animated: false, index: 0, viewOffset: 1, viewPosition: 0 }); this.ItemCategory._clickCategoryTab2 && this.ItemCategory._clickCategoryTab2(item, selectedIndex + 1); }
|
对于 IOS 平台,因为 IOS 系统存在弹性上拉,如下图所示。因此对滑动到底条件修正为 offset + visibleLength > contentLength + JDDevice.getRpx(100)
。
其中,JDDevice.getRpx(100)
表示弹性上拉的高度,即下图中红色框的高度。
1 2 3 4 5 6 7 8 9 10 11
| if (Platform.OS === 'ios') { if (offset + visibleLength > contentLength + JDDevice.getRpx(100)) { this._secondGoodFlatListRef.scrollToIndex({ animated: false, index: 0, viewOffset: 1, viewPosition: 0 }); this.ItemCategory._clickCategoryTab2 && this.ItemCategory._clickCategoryTab2(item, selectedIndex + 1); } }
|