APP首页常用的底部Button切换显示页。
IOS有官方的TabBarIOS可以实现,而Android暂时没有提供官方UI。
这里分享一个自己的简单实现,没有使用第三方库。
目录:
一. 效果图
二. 实现逻辑及顺序
三. 主要组件源码:
四. 可运行源码地址
一.效果图
二.实现逻辑及顺序
我们从图中可以明显看到,主要有两部分:
· 主视图内容(红、绿、蓝)
· 底部三个Tab按钮布局
所以我们只要实现上面两部分,再把他们组合起来。
那怎么让他们彼此产生联系呢?方式是通过给每一个Tab赋予点击事件,点击后刷新界面,目的是让主视图显示不同的内容。
1. 定义好外部基础框架,便于包含主视图和底部Tab
(1) 让外层View从底部开始放置控件
-设置Style为 flexDirection : 'column-reverse'
(2) 再实现底部Tab布局(第二点会讲实现),剩下的空间留个主视图
-设置主视图Style为 flex : 1
WmzyNavigator.js
render() {
return (
<View style={styles.container}>
//底部Tab布局
<WmTab callbackParent={(val) => {this.onTabChange(val)}}/>
//主视图内容
<View style = {styles.content}>
{this.renderContent()}
</View>
</View>
);
}
const styles = StyleSheet.create({
container:{
flex : 1,
width : width,
flexDirection : 'column-reverse', //从下往上绘制
},
content:{
flex : 1 //占据剩下空间
}
});
完整代码见最下方
2. 实现底部三按钮Tab布局(WmTab)
(1)先定义好资源。
const image = [ require('./img/icon_tab_home.png'),
require('./img/icon_tab_info.png'),
require('./img/icon_tab_me.png')];
const imageSelect = [ require('./img/icon_tab_home_pressed.png'),
require('./img/icon_tab_info_pressed.png'),
require('./img/icon_tab_me_pressed.png')];
const str = [ '报志愿','高考攻略','我的'];
(2)实现Tab按钮,三个样式一样,只要使用不同资源内容就可,使用数组下标0,1,2。
WmTab.js
render() {
//上述资源内容...
return (
//第一个按钮
<TouchableWithoutFeedback
style={styles.tabItem}
onPress={this.onTabSelect.bind(this, 'home-first')}>
<View style={styles.tabItem}>
<Image
style={styles.image}
source={this.state.tab === 'home-first'?imageSelect[0]:image[0]}/>
<Text
style = {
styles.text,
{color : this.state.tab === 'home-first'?'#00aff0':'#9E9EAE'}
}>
{str[0]}
</Text>
</View>
</TouchableWithoutFeedback>
//第二个按钮
<TouchableWithoutFeedback
...
</TouchableWithoutFeedback>
//第三个按钮
<TouchableWithoutFeedback
...
</TouchableWithoutFeedback>
)
};
const styles = StyleSheet.create({
container:{
flex : 1,
width : width,
flexDirection : 'column-reverse',
},
content:{
flex : 1
},
text:{
color : 'white',
}
});
完整代码见最下方
3. 主视图布局:
主视图就是一个简单的View,赋予了一个背景颜色(红、绿、黑)。
为了后面可扩展,把每一个View单独成一个组件,下面给出红主视图,其他的只要更换背景颜色即可。
Home.js
import React,{ Component } from 'react';
import{
View,
StyleSheet
} from 'react-native';
export default class Home extends Component{
render(){
return(
<View style={styles.container}></View>
);
}
}
const styles = StyleSheet.create({
container : {
flex : 1,
backgroundColor : 'red' //其他主视图更换这个颜色即可,原样复制代码。
}
});
4. 用事件将各个组件串通起来
这里需要先明白如何让父子组件之间通信,让底部按钮事件传递出来,让父组件收到通知,更换主视图内容。
因为外部基础框架(WmzyNavigator.js),包含着底部Tab布局(WmTab.js),那么就是要让WmzyNavigator.js和WmTab.js实现通信。
方法是从WmzyNavigator往WmTab里传递一个事件callbackParent。
当WmTab里面的Tab点击时,触发callbackParent,并把点击了哪个按钮的信息传递出来,WmzyNavigator收到这个信息后刷新页面,根据这个信息决定要加载那一个主视图。
WmzyNavigator
定义全局变量,用于记录当前显示的是哪个主视图的内容
constructor(props) {
super(props);
this.state = { tab : 'home-first' };
}
那么首先给WmzyNavigator一个刷新事件,用于点击时触发。
onTabChange(tab : string){
if (this.state.tab !== tab) {
this.setState({tab:tab});
}
}
再把这个事件往WmTab里面传递。
<WmTab callbackParent={(val) => {this.onTabChange(val)}}/>
当WmTab触发了callbackParent
WmzyNavigator收到通知就会调用onTabChange(),重新setState(),WmzyNavigator就会重新渲染,调用renderContent()重新去渲染主视图。
<View style = {styles.content}>
{this.renderContent()}
</View>
renderContent() {
switch(this.state.tab){
case 'home-first':{
return <Home/>
}
case 'home-second':{
return <GkInfo/>
}
case 'home-third':{
return <MyRoom/>
}
}
}
WmTab
也定义一个tab,用来记录当前点击了那一个按钮。
constructor(props) {
super(props);
this.state = { tab: 'home-first' };
}
声明一个点击方法,用于点击Tab时触发,目的:
1.改变当前变量tab。
2.触发setState(),重新渲染布局,改变按钮和文字的颜色。
3.触发从父组件获取到的方法callbackParent(),把tab信息传递出去。
onTabSelect (tab : string){
if (this.state.tab !== tab) {
this.setState({tab: tab});
this.props.callbackParent(tab);
}
}
Tab按钮的点击事件
<TouchableWithoutFeedback
onPress={this.onTabSelect.bind(this, 'home-first')}
<TouchableWithoutFeedback/>
由此便可把底部Tab的点击事件传递出去,并让主视图重新渲染。
三. 主要组件源码:
WmzyNavigator.js
import React,{ Component } from 'react';
import {Dimensions} from "react-native";
const {width, height} = Dimensions.get('window');
import WmTab from './common/WmTab';
import Home from './home/Home';
import GkInfo from './info/GkInfo';
import MyRoom from './me/MyRoom';
import{
View,
Text,
StyleSheet
} from 'react-native';
export default class WmzyNavigator extends Component{
constructor(props) {
super(props);
this.state = { tab : 'home-first' };
}
onTabChange(tab : string){
if (this.state.tab !== tab) {
this.setState({tab:tab});
}
}
renderContent() {
switch(this.state.tab){
case 'home-first':{
return <Home/>
}
case 'home-second':{
return <GkInfo/>
}
case 'home-third':{
return <MyRoom/>
}
}
}
render() {
return (
<View style={styles.container}>
<WmTab callbackParent={(val) => {this.onTabChange(val)}}/>
<View style = {styles.content}>
{this.renderContent()}
</View>
</View>
);
}
};
const styles = StyleSheet.create({
container:{
flex : 1,
width : width,
flexDirection : 'column-reverse',
},
content:{
flex : 1
}
});
WmTab.js: (建议复制到IDE中查看,简书这个换行简直了。)
import React,{ Component,PropTypes } from 'react';
import{
View,
Text,
Image,
StyleSheet,
TouchableHighlight
} from 'react-native';
import WmLine from './WmLine';
export default class WmTab extends Component{
constructor(props) {
super(props);
this.state = { tab: 'home-first' };
}
onTabSelect (tab : string){
if (this.state.tab !== tab) {
this.setState({tab: tab});
this.props.callbackParent(tab);
}
}
render(){
const image = [ require('../img/icon_tab_home.png'),
require('../img/icon_tab_info.png'),
require('../img/icon_tab_me.png')];
const imageSelect = [ require('../img/icon_tab_home_pressed.png'),
require('../img/icon_tab_info_pressed.png'),
require('../img/icon_tab_me_pressed.png')];
const str = [ '报志愿','高考攻略','我的'];
return(
<View style = {styles.container} key={this.state.tab}>
<WmLine/>
<View style = {styles.tab} >
<TouchableHighlight style={styles.tabItem}
onPress={this.onTabSelect.bind(this, 'home-first')}>
<View style={styles.tabItem}>
<Image style={styles.image}
source={this.state.tab === 'home-first'?imageSelect[0]:image[0]} />
<Text style = {styles.text,{color : this.state.tab === 'home-first'?'#00aff0':'#9E9EAE'}}>
{str[0]}
</Text>
</View>
</TouchableHighlight>
<TouchableHighlight style={styles.tabItem}
onPress={this.onTabSelect.bind(this, 'home-second')}>
<View style={styles.tabItem}>
<Image style={styles.image}
source={this.state.tab === 'home-second'?imageSelect[1]:image[1]}/>
<Text style = {styles.text,{color : this.state.tab === 'home-second'?'#00aff0':'#9E9EAE'}}>
{str[1]}
</Text>
</View>
</TouchableHighlight>
<TouchableHighlight style={styles.tabItem}
onPress={this.onTabSelect.bind(this, 'home-third')}>
<View style={styles.tabItem}>
<Image style={styles.image}
source={this.state.tab === 'home-third'?imageSelect[2]:image[2]}/>
<Text style = {styles.text,{color : this.state.tab === 'home-third'?'#00aff0':'#9E9EAE'}}>
{str[2]}
</Text>
</View>
</TouchableHighlight>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
height : 50
},
tab:{
height : 49,
flexDirection : 'row',
backgroundColor : 'white'
},
image:{
width : 30,
height : 30
},
tabItem:{
flex : 1,
justifyContent: 'center',
alignItems : 'center'
},
text:{
color : '#9E9EAE',
fontSize : 11
}
});