基于RN实现顶部tab导航切换
来源:互联网 发布:美国劳工部就业数据 编辑:程序博客网 时间:2024/05/21 19:25
效果图
在实现这个需求的时候,我的第一想法就是看看官网有没有类似的组件,然后我找到了TabNavigator,官方文档介绍 使用这个组件,需要事先导入react-navigation包.TabNavigator是Navigation库中提供的3个组件中的其中一个,不仅可以实现顶部导航,也还可以实现底部导航,另外2个组件分别是StackNavigator和DrawerNavigator,前者是实现页面栈的导航,比如当前页面和下级页面的切换,后者是实现侧滑菜单导航的.
但是,TabNavigator虽好,我却没有找到有什么办法可以动态添加场景的方式,文档也没有给出说明,这就很尴尬啊.因为我这个需求的顶部tab数量和内容都是根据接口字段返回确定的,而TabNavigator都是事先写死的几个tab名称和对应场景.
既然官方的库没办法实现,我就随便搜索下别人写好的第三方库,找到一个react_native_scrollable_tab_view库,可以满足我这个需求,对于android平台,该库底层其实是用ViewPager来实现页面切换效果的,然而我在使用的时候发现当我的页面是ListView组件的时候,会出现从其他页面切换回来的时候当前页面的数据会消失,而且没有请求接口,查看了下代码发现ListView的randerRow方法的rowData既然是空的,这就很神奇了,发现控制台输出的log是gc在回收.到底是什么鬼原因,我也没找到.
没办法,最后还是得自己写一个类似的组件,下面就说说我这个自定义的组件实现方式吧,整个页面分为上下2部分,都是用ScrollView来实现的.
顶部tab导航单独封装了一个组件叫TabIndicator,默认最大显示5个tab,超出支持滑动显示,指示器用了Animated来实现水平方向的移动动画,tab选中和未选中的color,是根据当前点击的index与tab属性的index作比较,如果相同表示选中的是当前tab,然后通过setState来修改color,比较难的地方就是实现点击屏幕左右边界时自动滚出上一个或者下一个tab效果了,这里用到了ScrollView的scrollTo方法:
scrollTo(y: number | { x?: number, y?: number, animated?: boolean }, x: number, animated: boolean)
滚动到指定的x, y偏移处。第三个参数为是否启用平滑滚动动画。
使用示例:
scrollTo({x: 0, y: 0, animated: true})
剩下的工作就是找出x和当前index的变化规律了,这个要多测试才好理解,我也是弄了好久才找到规律的.
ok,下面贴出TabIndicator的详细代码
import React, { Component } from 'react';import { View, Text, ScrollView, StyleSheet, Dimensions, TouchableHighlight, Animated,} from 'react-native';const screenWidth = Dimensions.get('window').width;const defaultVisiableCount = 5; //默认显示的tab数量export default class TabIndicator extends Component { static propTypes = { tabBarActiveTextColor: React.PropTypes.string,//tab选中时显示的颜色 tabBarInactiveTextColor: React.PropTypes.string,//tab未选中时的颜色 tabNames: React.PropTypes.array,//tab名称集合 tabTextStyle: React.PropTypes.any,//tab文本的样式 tabBarUnderlineStyle: React.PropTypes.any,//tab下划线的样式 tabHeight: React.PropTypes.number,//tab的高度 }; static defaultProps = { tabBarActiveTextColor: '#686868', tabBarInactiveTextColor: '#cccccc', tabNames: [], tabHeight: 50, } constructor(props) { super(props) this.state = { transAnimX: new Animated.Value(0), //指示器平移动画 }; this.tabManager = []; this.tabRef = []; this.tabWidth = 0;//单个tab的宽度 this.currPosition = 0;//当前位置 this.totalTabWidth = 0;//所有tab宽度之和 if (this.props.tabNames) { //计算单个tab的宽度 this.tabWidth = this.props.tabNames.length > defaultVisiableCount ? screenWidth / defaultVisiableCount : screenWidth / this.props.tabNames.length; this.totalTabWidth = this.tabWidth * this.props.tabNames.length; //创建所有tabView for (let index = 0; index < this.props.tabNames.length; index++) { this.tabManager.push( <Tab ref={(r) => this.tabRef[index] = r} key={index} index={index} title={this.props.tabNames[index]} onTabPress={(index) => { this.clickToTrans(index); }} tabWidth={this.tabWidth} tabHeight={this.props.tabHeight} style={{ ...this.props.tabTextStyle }} selectedTextColor={this.props.tabBarActiveTextColor} unselectedTextColor={this.props.tabBarInactiveTextColor} /> ) } } } //点击tab时触发 clickToTrans = (index) => { this.currPosition = index; if (this.props.onTabPress) { this.props.onTabPress(index);//通知外部点击的位置 } else { this.swipeToTrans(index) //外部没有设置关联时自己处理 } } //关联的ScrollView滚动时触发 swipeToTrans = (index) => { this.currPosition = index; console.log("swipeToTrans:index=" + index) if (index >= (defaultVisiableCount - 1)) { this.scrollToNext() } else if (index >= 0) { console.log(index - (defaultVisiableCount - 1) > 0) this.scrollToPre() } //移动指示器 this.state.transAnimX.setValue(index) //遍历所有tab,根据当前选中的index修改颜色 this.tabRef.map((ref, key) => { ref.changeState(index) }); } //滚动下一个tab scrollToNext = () => { console.log("scrollToNext") let x = (this.currPosition - (defaultVisiableCount - 1) + 1) * this.tabWidth this.sv.scrollTo({ x: x, y: 0, animated: true }) } //滚动上一个tab scrollToPre = () => { console.log("scrollToPre") let x = - this.tabWidth this.sv.scrollTo({ x: x, y: 0, animated: true }) } render() { return ( <View style={{ height: this.props.tabHeight, width: screenWidth, backgroundColor: 'white' }}> <ScrollView horizontal={true} keyboardDismissMode='on-drag' showsHorizontalScrollIndicator={false} overScrollMode='never' ref={(r) => this.sv = r} > {/* 所有tab */} {this.tabManager} {/* 指示器 */} <Animated.View style={[styles.tabLine, { width: this.tabWidth }, { transform: [ { translateX: this.state.transAnimX.interpolate({ inputRange: [0, this.props.tabNames.length], //0~tab个数 outputRange: [0, this.totalTabWidth] //0~所有tab的总宽度 }) } ] } ]} /> </ScrollView> {/* 默认线 */} <View style={styles.line} /> </View> ) }}/** * 单个tab */class Tab extends Component { constructor(props) { super(props) this.state = { selected: 0 //选中的下标 } } render() { return ( <TouchableHighlight underlayColor='white' onPress={() => { this.props.onTabPress && this.props.onTabPress(this.props.index) }} style={[{ height: this.props.tabHeight, width: this.props.tabWidth }, styles.tabStyle]}> <Text style={{ textAlign: 'center', color: this.state.selected === this.props.index ? this.props.selectedTextColor : this.props.unselectedTextColor, ...this.props.style, }}> {this.props.title} </Text> </TouchableHighlight> ) } //修改选中的index changeState = (index) => { this.setState({ selected: index }) }}const styles = StyleSheet.create({ line: { backgroundColor: '#cccccc', width: screenWidth, height: 1 }, tabLine: { position: 'absolute', bottom: 0, height: 2, backgroundColor: '#FFE97A', }, tabStyle: { justifyContent: 'center', alignContent: 'center' }})
接着是继续封装集成了顶部tab与底部ScrollView的组件,我命名为ScrollTabView
import React, { Component } from 'react';import { View, StyleSheet, ScrollView, Dimensions} from 'react-native';import TabIndicator from './TabIndicator'const screenWidth = Dimensions.get('window').width;const screenHeight = Dimensions.get('window').height;//集成TabIndicator和需要滚动的子视图export default class ScrollTabView extends Component { //这里的属性基本就是TabIndicator的除了titles,这个是所有tab的名称,当然最后也是传给TabIndicator的 static propTypes = { tabBarActiveTextColor: React.PropTypes.string, tabBarInactiveTextColor: React.PropTypes.string, titles: React.PropTypes.array, tabTextStyle: React.PropTypes.any, tabBarUnderlineStyle: React.PropTypes.any, tabHeight: React.PropTypes.number, }; static defaultProps = { tabBarActiveTextColor: '#686868', tabBarInactiveTextColor: '#cccccc', titles: [], tabHeight: 50, }; render() { return ( <View style={styles.container}> <TabIndicator ref={(r) => this.tab = r} tabNames={this.props.titles} tabBarUnderlineStyle={this.props.tabBarUnderlineStyle} tabHeight={this.props.tabHeight} tabTextStyle={this.props.tabTextStyle} tabBarActiveTextColor={this.props.tabBarActiveTextColor} tabBarInactiveTextColor={this.props.tabBarInactiveTextColor} onTabPress={(index) => { this.sv.scrollTo({ x: index * screenWidth, y: 0, animated: true }) }} /> <ScrollView ref={(r) => this.sv = r} pagingEnabled horizontal onScroll={(e) => { this.tab.swipeToTrans(e.nativeEvent.contentOffset.x / screenWidth) }} > {/* 要展示的滚动视图 */} {this.props.children} </ScrollView> </View> ) }}const styles = StyleSheet.create({ container: { width: screenWidth, height: screenHeight, }})
最后就是测试代码了
//tab名称const titles = ['科技', '热点', '饮食', '交通', '城市', '热点', '军事', '人文']//tab对应的viewconst views = [] componentDidMount() { views.push(<View style={{ width: screenWidth, backgroundColor: '#dfdfdf' }} />) views.push(<View style={{ width: screenWidth, backgroundColor: '#green' }} />) views.push(<View style={{ width: screenWidth, backgroundColor: '#yellow' }} />) views.push(<View style={{ width: screenWidth, backgroundColor: '#blue' }} />) views.push(<View style={{ width: screenWidth, backgroundColor: '#pink' }} />) views.push(<View style={{ width: screenWidth, backgroundColor: '#red' }} />) views.push(<View style={{ width: screenWidth, backgroundColor: '#gray' }} />) views.push(<View style={{ width: screenWidth, backgroundColor: '#purple' }} />) } render() { return ( <ScrollableTabView tabHeight={40} tabBarActiveTextColor='#686868' tabBarInactiveTextColor='#cccccc' titles={titles} > {/* 展示页面 */} {views} </ScrollableTabView> ) }
- 基于RN实现顶部tab导航切换
- Android TabLayout、ViewPager实现顶部和底部Tab导航 点击滑动切换Tab页面
- Android之新闻客服端顶部导航栏Tab点击和左右滑动实现切换界面
- fragment+Radiobutton实现顶部导航切换
- 安卓顶部tab导航栏的实现
- TabLayout实现仿今日头条顶部tab导航效果
- ViewPager、Fragment、RadioButton实现顶部切换Tab页面
- Fragment 多tab切换,顶部tab,顶部tab+viewpager
- 顶部多Tab切换效果
- ios基于UITabBarController实现tab页面切换
- Tablayout+viewpager+fragment实现tab导航以及滑动切换
- CSS实现导航条Tab切换的三种方法
- android基于Fragment实现底部导航切换
- tab切换导航内容制作
- TabLayout轻松实现仿今日头条顶部tab导航效果
- TabLayout轻松实现仿今日头条顶部tab导航效果
- 首页-底部&顶部Tab导航(菜单栏)的实现:TabLayout+ViewPager+Fragment
- Android ViewPager和Fragment实现顶部导航界面滑动效果、标签下的tab位置
- 题目94-cigarettes
- HDU
- 前端一些记不太住的知识点加深印象
- jquery Validator 错误显示的方法
- Java中字符串的大小写转换
- 基于RN实现顶部tab导航切换
- 文本相似度算法----动态规划求子串
- Action Recognition
- 1110:排列
- 游戏中服务端与客户端分别承担怎样的计算才即安全又可靠呢?
- 06-javascript 循环
- Java EE开发第五章:JDBC-API详解(了解)
- 【软考之旅】用例图——包含、扩展关系
- tensorflow之变量(Variable)与常量(constant)体会