React Native带你实现scrollable-tab-view(五)
来源:互联网 发布:excel密码破解软件 编辑:程序博客网 时间:2024/06/08 18:00
上一节React Native带你实现scrollable-tab-view(三)中我们最后实现了我们scrollable-tab-view的效果为:
比如有很多页面,我们的tabview需要跟随scrollview的滑动而滑动:
我们现在用的还是默认的tabview,比如我们多加一些页面,
这样肯定不行,我们需要的是上面的那种效果,所以每个view的flex=1这种模式肯定不适合了,我们需要用一个scrollview去包涵tab,使它也能滑动,所以也算是scrollable-tab-view的一个难点了,好啦~ 我们来一步一步实现一下scrollable-tab-view中的ScrollableTabBar.js。
我们先创建一个view叫ScrollableTabBar.js:
/** * @author YASIN * @version [React-Native Pactera V01, 2017/9/7] * @date 17/2/23 * @description ScrollableTabBar */import React, { Component} from 'react';import { View, Text, StyleSheet,} from 'react-native';export default class ScrollableTabBar extends Component { // 构造 constructor(props) { super(props); // 初始状态 this.state = {}; } render() { return ( <View> <Text>ScrollableTabBar</Text> </View> ); }}const styles = StyleSheet.create({ container: {}});
然后替换掉index文件中的DefaultTabBar组件:
/** * 渲染tabview * @private */ _renderTabView() { let tabParams = { tabs: this._children().map((child)=>child.props.tabLabel), activeTab: this.state.currentPage, scrollValue: this.state.scrollValue, containerWidth: this.state.containerWidth, }; return ( <ScrollableTabBar {...tabParams} style={[{width: this.state.containerWidth}]} onTabClick={(page)=>this.goToPage(page)} /> ); }
然后我们开始实现ScrollableTabBar组件:
我们用一个scrollview去包裹tab,然后其它的东西直接copy一份DefaultTabBar的内容:
/** * @author YASIN * @version [React-Native Pactera V01, 2017/9/7] * @date 17/2/23 * @description ScrollableTabBar */import React, { Component} from 'react';import { View, Text, StyleSheet, Dimensions, TouchableOpacity, ScrollView, Animated,} from 'react-native';const screenW = Dimensions.get('window').width;const screenH = Dimensions.get('window').height;export default class ScrollableTabBar extends Component { // 构造 constructor(props) { super(props); // 初始状态 this.state = {}; } render() { let {containerWidth, tabs, scrollValue}=this.props; //给传过来的动画一个插值器 const left = scrollValue.interpolate({ inputRange: [0, 1,], outputRange: [0, containerWidth / tabs.length,], }); let tabStyle = { width: 50, position: 'absolute', bottom: 0, left, }; return ( <View style={[styles.container, this.props.style]}> <ScrollView automaticallyAdjustContentInsets={false} ref={(scrollView) => { this._scrollView = scrollView; }} horizontal={true} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} directionalLockEnabled={true} bounces={false} scrollsToTop={false} > <View style={styles.tabContainer} > {tabs.map((tab, index)=> { return this._renderTab(tab, index, index === this.props.activeTab); })} <Animated.View style={[styles.tabLineStyle, tabStyle]} /> </View> </ScrollView> </View> ); } /** * 渲染tab * @param name 名字 * @param page 下标 * @param isTabActive 是否是选中的tab * @private */ _renderTab(name, page, isTabActive) { let tabTextStyle = null; //如果被选中的style if (isTabActive) { tabTextStyle = { color: 'green' }; } else { tabTextStyle = { color: 'red' }; } let self = this; return ( <TouchableOpacity key={name + page} style={[styles.tabStyle]} onPress={()=>this.props.onTabClick(page)} > <Text style={[tabTextStyle]}>{name}</Text> </TouchableOpacity> ); }}const styles = StyleSheet.create({ container: { width: screenW, height: 50, }, tabContainer: { flexDirection: 'row', alignItems: 'center', }, tabLineStyle: { height: 1, backgroundColor: 'navy', }, tabStyle: { height: 49, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 20, },});
然后我们运行代码:
可以看到,有点那种感觉了,但是我们目前看到的东西需要解决的就是:
1、底部那个线条长度计算(目前是定死了一个值)
render() { .... let tabStyle = { width: 50, position: 'absolute', bottom: 0, left, }; ...
我们线条的长度应该等于当前tab的宽度。
2、我们页面滑动的时候,线条指向有问题。
好了~ 我们一个一个的解决,我们把下标线view的left跟宽度用两个单独的动画控制,然后监听内容scrollview的scrollValue动画滑动改变控制left和控制width的动画值:
export default class ScrollableTabBar extends Component { // 构造 constructor(props) { super(props); // 初始状态 this.state = { _leftTabUnderline: new Animated.Value(0), _widthTabUnderline: new Animated.Value(0), }; } render() { let {containerWidth, tabs, scrollValue}=this.props; //给传过来的动画一个插值器 let tabStyle = { width: this.state._widthTabUnderline, position: 'absolute', bottom: 0, left: this.state._leftTabUnderline, }; return ( <View style={[styles.container, this.props.style]}> <ScrollView .... > <View style={styles.tabContainer} > ... <Animated.View style={[styles.tabLineStyle, tabStyle]} /> </View> </ScrollView> </View> ); }
然后跟前一节我们实现的DefaultTabBar.js一样,监听scrollValue动画:
componentDidMount() { this.props.scrollValue.addListener(this._updateView); }
/** * 根据scrollview的滑动改变底部线条的宽度跟left * @param value * @private */ _updateView = ({value = 0}) => { //因为value 的值是[0-1-2-3-4]变换 const position = Math.floor(value); //取小数部分 const offset = value % 1; const tabCount = this.props.tabs.length; const lastTabPosition = tabCount - 1; //如果没有tab||(有bounce效果)直接return if (tabCount === 0 || value < 0 || value > lastTabPosition) { return; } console.log('position==>' + position + ' offset==>' + offset); }
我们运行然后从第一页滑动到第二页:
可以看到,我们position是从(0-1)而offset为0–>1的一个小数,所以我们:
1、底部线条view的宽度应该为(第position页tab宽度)*(1-offset)+(第positon+1页tab的宽度)*offset。
2、底部线条view的left应该为(第position页tab的x轴)+(第position+1页tab的宽度)*offset。
所以我们接下来要做的就是通过o nLayout方法计算出每个tab的x轴跟宽度然后保存起来。
我们在渲染tabbar的时候提供view o nLayout方法去测量view:
/** * 渲染tab * @param name 名字 * @param page 下标 * @param isTabActive 是否是选中的tab * @private */ _renderTab(name, page, isTabActive) { .... let self = this; return ( <TouchableOpacity ..... onLayout={(event)=>this._onMeasureTab(page, event)} > <Text style={[tabTextStyle]}>{name}</Text> </TouchableOpacity> ); }
每次测量完毕后去更新tab底部线view的宽度:
/** * 测量tabview * @param page 页面下标 * @param event 事件 * @private */ _onMeasureTab(page, event) { let {nativeEvent:{layout:{x, width}}}=event; this._tabsMeasurements[page] = {left: x, right: width + x, width: width}; this._updateView({value: this.props.scrollValue._value}) }
按照我们前面所说的“1、底部线条view的宽度应该为(第position页tab宽度)*(1-offset)+(第positon+1页tab的宽度)*offset。”所以我们必须要拿到tab的测量值,并且有下一个tab,或者特殊情况就是到最后一个tab了,最后才去做改变width的操作。
/** * 根据scrollview的滑动改变底部线条的宽度跟left * @param value * @private */ _updateView = ({value = 0}) => { //因为value 的值是[0-1-2-3-4]变换 const position = Math.floor(value); //取小数部分 const offset = value % 1; const tabCount = this.props.tabs.length; const lastTabPosition = tabCount - 1; //如果没有tab||(有bounce效果)直接return if (tabCount === 0 || value < 0 || value > lastTabPosition) { return; } if (this._necessarilyMeasurementsCompleted(position, position === tabCount - 1)) { this._updateTabLine(position, offset); } }
/** * 判断是否需要跟新的条件是否初始化 * @param position * @param isLast 是否是最后一个 * @private */ _necessarilyMeasurementsCompleted(position, isLast) { return ( this._tabsMeasurements[position] && (isLast || this._tabsMeasurements[position + 1]) ); }
然后套去我们前面说的公式:
(第position页tab宽度)*(1-offset)+(第positon+1页tab的宽度)*offset
但是有一种特殊情况,就是position+1不存在,所以我们要做好判断:
/** * 更新底部线view的left跟width * @param position * @param offset * @private */ _updateTabLine(position, offset) { //当前tab的测量值 const currMeasure = this._tabsMeasurements[position]; //position+1的tab的测量值 const nextMeasure = this._tabsMeasurements[position + 1]; let width = currMeasure.width * (1 - offset); if (nextMeasure) { width += nextMeasure.width * offset; } this.state._widthTabUnderline.setValue(width); }
然后我们运行代码:
可以看到,我们滑动的时候tab底部线条的宽度再改变,宽度=我们的tab的宽度,好啦!! 我们接着只要使线条view的left跟着scrollview滑动而改变到对应的tab位置就可以了。
我们继续套入公式:
(第position页tab的x轴)+(第position+1页tab的宽度)*offset
/** * 更新底部线view的left跟width * @param position * @param offset * @private */ _updateTabLine(position, offset) { //当前tab的测量值 const currMeasure = this._tabsMeasurements[position]; //position+1的tab的测量值 const nextMeasure = this._tabsMeasurements[position + 1]; let width = currMeasure.width * (1 - offset); let left= currMeasure.left; if (nextMeasure) { width += nextMeasure.width * offset; left+=nextMeasure.width*offset; } this.state._leftTabUnderline.setValue(left); this.state._widthTabUnderline.setValue(width); }
然后运行代码:
好啦! 可以看到,我们底部线条view可以跟随了,到这我们还差最后一步,那就是当我们点击tab的时候如果后面的tab被遮住了就得向后移动。
有点晚了,该洗洗睡啦~~
欢迎入群,欢迎交流,大牛勿喷,下一节见!
- React Native带你实现scrollable-tab-view(五)
- React Native带你实现scrollable-tab-view(一)
- React Native带你实现scrollable-tab-view(二)
- React Native带你实现scrollable-tab-view(三)
- React Native带你实现scrollable-tab-view(四)
- React Native带你实现scrollable-tab-view(完结)
- React:react-native-scrollable-tab-view
- ReactNative组件-react-native-scrollable-tab-view
- react-native-scrollable-tab-view 使用总结
- react-native-scrollable-tab-view详解
- 选项卡react-native-scrollable-tab-view
- [React Native]react-native-scrollable-tab-view(入门篇)
- [React Native]react-native-scrollable-tab-view(进阶篇)
- React Native之react-native-scrollable-tab-view详解
- [React Native]react-native-scrollable-tab-view(入门篇)
- [React Native]react-native-scrollable-tab-view(入门篇)
- 选项卡react-native-scrollable-tab-view(入门篇)
- 选项卡react-native-scrollable-tab-view(进阶篇)
- POJ 3281 Dining (拆点+ 网络流)
- SpringMVC自定义返回XML/JSON数据
- java复习-基础内容部分
- 为什么微信能超越QQ?
- 快速排序(code)
- React Native带你实现scrollable-tab-view(五)
- 自然语言处理扫盲·第二天——白话机器翻译原理
- Android开发 之 OpenGL ES系列(4--添加颜色)
- Hibernate入门(3):属性映射 & 主键映射
- e的科学计数法
- Android EventLog
- doxygen生成PDF文档
- POJ 2299 Ultra-QuickSort (树状数组+离散化)
- 2017 Multi-University Training Contest