react--动态添加tab和tab对应的界面
情景描述
产品丢过来一个分类的功能:分几个类不固定,分类中的数据是一个列表,可分页、刷新。分析一下,数据不固定,就需要动态获取,大分类数据(有多少个分类)和分类中的列表数据都需要从接口中获取,这是一个比较常用的功能。
先上效果图:
图中横向滑动的数据、分类下面的列表数据都是从接口得到的,要达到这样的效果,我们先分析一下,上面横向滑动的分类是一个scrollTab,react中有这样的开源项目,react-native-scrollable-tab-view(如果该项目可以满足你的需求,请忽略下面的内容)。下面的列表可以用FlatList来做,因为数据结构、页面样式相似,我们可以用一个界面就能完成。
分析完毕,下面我们一步一步来做,最终达成我们想要的效果。
第一步:导入react-native-scrollable-tab-view
使用npm将该控件导入到项目中:
npm install --save react-native-scrollable-tab-view
运行项目(运行Android为例):
react-native run-android
第二步:使用react-native-scrollable-tab-view
下面的代码是对ScrollableTabView的简单使用,更多属性见 react-native-scrollable-tab-view,其中
renderTabBar={() => <ClassificationTabBar tabNames={this._renderTabTitle()}/>}
和{this._renderDynamicView()}
会作具体说明,以下是ScrollableTabView的简单使用代码示例:
render() {
return <View style={styles.container}>
<ScrollableTabView
ref={(ScrollableTabView) => {
_scrollableTabView = ScrollableTabView;
}}//将该控件提取出来,_scrollableTabView指向该控件
renderTabBar={() => <ClassificationTabBar tabNames={this._renderTabTitle()}/>}//也可以使用DefaultTabBar,ScrollableTabBar
initialPage={0}//初始化哪一个界面
locked={false}//是否不允许滑动
scrollWithoutAnimation={true}
showsHorizontalScrollIndicator={false}
prerenderingSiblingsNumber={1}
tabBarActiveTextColor={'#cba000'}
tabBarInactiveTextColor={'#777'}>
{this._renderDynamicView()}
</ScrollableTabView>
</View>
}
上述代码中使用到了ClassificationTabBar ,ScrollableTabView中提供了DefaultTabBar,ScrollableTabBar,如果这两个可以满足需求,也可以使用这两个控件,跳过该步骤。ClassificationTabBar 中可自行定义tab样式风格,更具扩展性,以下是ClassificationTabBar 的全部代码,可直接使用:
/**
* @author zhousx
*
* description:可滑动的动态 tabView
*/
import React, {Component} from 'react';
import {
AppRegistry, Dimensions,
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
import PropTypes from 'prop-types'
import {DEFAULT_FONT} from "../utils/FontUtils";
let {width, height} = Dimensions.get('window');
export default class ClassificationTabBar extends Component {
static propTypes = {
activeTab: PropTypes.number, // 当前被选中的tab下标
tabs: PropTypes.array, // 所有tabs集合
tabNames: PropTypes.array, // 保存Tab名称
}; // 注意这里有分号
render() {
return (
<View style = {styles.parent}>
<View style={styles.tabs}>
{/*遍历。系统会提供一个tab和下标 调用一个自定义的方法*/}
{this.props.tabs.map((tab, i) => this.renderTabOption(tab, i))}
</View>
<View style = {{backgroundColor:'#fafafa',width:width,height:1}}/>
</View>
);
}
/// 处理tabbar的颜色和字体及图标
renderTabOption(tab, i) {
let color = this.props.activeTab === i ? "#cba000" : "#aaaaaa"; // 判断i是否是当前选中的tab,设置不同的颜色
let color2 = this.props.activeTab === i ? "#cba000" : "#fff"; // 判断i是否是当前选中的tab,设置不同的颜色
return (
//因为要有点击效果 所以要引入可触摸组件
<TouchableOpacity onPress={() => this.props.goToPage(i)} style={styles.tab} key={tab}>
<View style={styles.tabItem}>
<Text style={{color: color, fontFamily: DEFAULT_FONT, marginTop: 15}}>
{this.props.tabNames[i]}
</Text>
<Text
style={{backgroundColor: color2, width: 40, height: 2, marginTop: 10}}/>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
parent:{
elevation: 2,
shadowOffset: {
width: 1, height: 2
},
},
tabs: {
flexDirection: 'row',
height: 45,
},
tab: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
tabItem: {
flexDirection: 'column',
alignItems: 'center',
width: 100
},
});
ClassificationTabBar 类中用到了prop-types,这是一个属性校验的类,在react老的版本中不需要导入,在新版本中被分离出来,是一个呗广泛使用的工具,如果项目中导入了请忽略,如果没导入请使用npm下载:
npm install --save prop-types
其中
static propTypes = {
activeTab: PropTypes.number, // 当前被选中的tab下标
tabs: PropTypes.array, // 所有tabs集合
tabNames: PropTypes.array, // 保存Tab名称
}; // 注意这里有分号
tabNames为传递过来的tab的标题数组,也就是ScrollableTabView中传递过去的值:
renderTabBar={() => <ClassificationTabBar tabNames={this._renderTabTitle()}/>}
this._renderTabTitle()
方法:
_renderTabTitle() {
const texts = [];
for (let i = 0; i < this.state.dataArray.length; i++) {
const labelName = this.state.dataArray[i].typeName;
texts.push(labelName);
}
return texts;
}
this.state.dataArray
是在请求完成之后set的值。
到此,tab已动态设置完成,接下来需要动态添加列表页面。
第三步:动态添加列表页面
动态添加的类表页面的个数需要根据请求返回的分类的个数来确定,所以需要遍历分类个数来动态添加列表界面,因为我这边列表界面的样式都相同,所以把列表界面封装为一个Compoent,然后将参数传给列表界面,多个样式道理也相同。
下面是动态添加界面的代码:
_renderDynamicView() {
const _views = [];
for (let i = 0; i < this.state.dataArray.length; i++) {
const labelName = this.state.dataArray[i].typeName;
const classificationId = this.state.dataArray[i].id;
console.log('labelName = ' + labelName);
_views.push(<View
style={styles.container} tabLabel={labelName} key={i}>
<SecondClassificationView classificationId={classificationId} {...this.props}/>
</View>);
}
return _views;
}
将分类id传递给Component,在这里也就是SecondClassificationView ,然后通过ID去获取不同的数据,SecondClassificationView 是一个简单的继承Component的控件:
以下是SecondClassificationView 的部分代码:
export default class SecondClassificationView extends Component<props> {
static propTypes = {
classificationId: PropsTypes.number,
};
constructor(props) {
super(props);
this.state = {
dataArray: [],
isRefreshing: false
}
}
render() {
const {navigation} = this.props;
return <View
style={styles.container}>
<FlatList
data={this.state.dataArray}
renderItem={
(data) =>
<TouchableOpacity activeOpacity={0.5}
onPress={() => {
}}>
<View style={styles.item}>
<Image
style={{marginLeft: 20, height: 18, width: 18,}}
source={require('../../res/images/pic_book_head.png')}/>
<Text style={styles.text}>{data.item.bookName}</Text>
</View>
</TouchableOpacity>
}
ItemSeparatorComponent={
() => this._renderSeparator()
}
ListEmptyComponent={
() => this._renderEmpty()
}
refreshing={this.state.isRefreshing}
onRefresh={
() => {
this._onRefresh()
}
}
/>
</View>
}
}
至此,动态添加tab和tab对应界面的所有步骤就完成了,如果有问题的地方,请在下方留言,看到我会第一时间回复!
如果对你有帮助,点个赞呗!