native自定义导航栏 react

2016-08-15 17:32:43 yulianlin 阅读数 3652

之前我们学习了可触摸组件和页面导航的使用的使用:
从零学React Native之09可触摸组件

从零学React Native之03页面导航

经过之前的学习, 我们可以完成一个自定义导航栏了, 效果如下:
这里写图片描述

我们需要创建一个 NaviBar.js 用来显示顶部的导航栏, 还需要四个界面(Page1.js,Page2.js,Page3.js,Page4.js)。 当然还需要修改index.android.js或者index.ios.js 用来处理4个界面的切换。

导航栏NaviBar 实现

这里, 我们就假设应用有4个栏目, 每个按钮的宽高比为 4:3
直接贴代码了:

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    View,
    Text,
    TouchableHighlight,
} from 'react-native';

var Dimensions = require("Dimensions");
var totalWidth = Dimensions.get('window').width;// 屏幕宽度
let naviButtonWidth = totalWidth / 4;    //计算导航条每个宽度
let naviButtonHeight = naviButtonWidth * 0.75;   // 导航条每个高度
export  default class NaviBar extends Component {
    // 构造
    constructor(props) {
        super(props);
        this._tab0Pressed = this._tab0Pressed.bind(this);
        this._tab1Pressed = this._tab1Pressed.bind(this);
        this._tab2Pressed = this._tab2Pressed.bind(this);
        this._tab3Pressed = this._tab3Pressed.bind(this);
    }

    //四个按钮 被按下时处理函数
    _tab0Pressed() {
        this.props.onNaviBarPress(0);
    }

    _tab1Pressed() {
        this.props.onNaviBarPress(1);
    }

    _tab2Pressed() {
        this.props.onNaviBarPress(2);
    }

    _tab3Pressed() {
        this.props.onNaviBarPress(3);
    }

    render() {
        //通过属性得知哪个导航按钮是当前导航页, 这个导航用灰色背景
        //利用JavaScript数组的map函数,从一个数组对应生成另一个数组buttonColors
        // 看不懂该函数的,看下面的解释
        var buttonColors = this.props.naviBarStatus.map(function (aNumber) {
            if (aNumber == 0) return 'white';
            return 'gray';
        });
        return (
            //根View
            <View style={styles.naviRow}>
                <TouchableHighlight onPress={this._tab0Pressed}>
                    <View style={[styles.button,{backgroundColor:buttonColors[0]}]}>
                        <Text style={styles.textStyle1}>
                            条目一
                        </Text>
                    </View>
                </TouchableHighlight>
                <TouchableHighlight onPress={this._tab1Pressed}>
                    <View style={[styles.button,{backgroundColor:buttonColors[1]}]}>
                        <Text style={styles.textStyle1}>
                            条目二
                        </Text>
                    </View>
                </TouchableHighlight>
                <TouchableHighlight onPress={this._tab2Pressed}>
                    <View style={[styles.button,{backgroundColor:buttonColors[2]}]}>
                        <Text style={styles.textStyle1}>
                            条目三
                        </Text>
                    </View>
                </TouchableHighlight>
                <TouchableHighlight onPress={this._tab3Pressed}>
                    <View style={[styles.button,{backgroundColor:buttonColors[3]}]}>
                        <Text style={styles.textStyle1}>
                            条目四
                        </Text>
                    </View>
                </TouchableHighlight>
            </View>
        );
    }
}
// 声明属性, 方便使用当前组件
NaviBar.propTypes = {
    naviBarStatus: React.PropTypes.arrayOf(React.PropTypes.number).isRequired,
    onNaviBarPress: React.PropTypes.func.isRequired
};

//样式 
const styles = StyleSheet.create({
    naviRow: {
        flexDirection: 'row'
    },
    button: {
        width: naviButtonWidth,
        height: naviButtonHeight,
        justifyContent: 'center'
    },
    textStyle1: {
        fontSize: 20,
        textAlign: 'center'
    }
});

上面用到了, Map函数 ,
Map函数的作用是按照某种关系从原数组”映射”出新数组. 如下面求平方的例子:

var data= [1,2,3,4];
var newArray=data.map((item)=>{return item*item});
console.log(newArray);  //输出[1,4,9,16]

统一处理四个界面的切换

我们需要在index.android.js 或者index.ios.js 这个代码比较简单, 只需要导入四个界面, 用Navigator组件切换就可以了。

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Navigator
} from 'react-native';

import Page1 from './Page1';
import Page2 from './Page2';
import Page3 from './Page3';
import Page4 from './Page4';
class AwesomeProject extends Component {
    //告知Navigator 模块切换时的效果
    configureScene() {
        return Navigator.SceneConfigs.FadeAndroid;
    }
    //根据传递的信息, 处理界面的切换
    renderScene(router, navigator) {
        this._navigator = navigator;
        switch (router.name) {
            case 'Page1':
                return <Page1 navigator={navigator}/>;
            case 'Page2':
                return <Page2 navigator={navigator}/>;
            case 'Page3':
                return <Page3 navigator={navigator}/>;
            case 'Page4':
                return <Page4 navigator={navigator}/>;
        }
    }
    render() {
        return (
            //根View
            <Navigator
                initialRoute={{name:'Page1'}}
                configureScene={this.configureScene}
                renderScene={this.renderScene}/>
        );
    }
}
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

界面

上面的代码需要引入Page1 - Page4, 这个四个界面非常相似, 我们只贴其中一个了.
Page1.js

import React, { Component } from 'react';
import {
    View,
    StyleSheet,
} from 'react-native';
import NaviBar from './NaviBar';

export  default class Page1 extends Component {
    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this.onNaviBarPress = this.onNaviBarPress.bind(this);
    }

    render() {
        // 不同的Page,需要修改下面的这个数组, 通过数组控制导航栏条目显示状态
        var naviStatus = [1, 0, 0, 0];
        return (
            <View style={styles.container}>
                <NaviBar naviBarStatus={naviStatus}
                         onNaviBarPress={this.onNaviBarPress}/>
                <View style={styles.whatLeft}/>
            </View>
        );
    }
    //不同的page需要修改返回值
    onNaviBarPress(aNumber) {
        switch (aNumber) {
            case 0:
                return;
            case 1:
                //通过replace切换
                this.props.navigator.replace({
                    name: 'Page2'
                });
                return;
            case 2:
                this.props.navigator.replace({
                    name: 'Page3'
                });
                return;
            case 3:
                this.props.navigator.replace({
                    name: 'Page4'
                });
                return;
        }
    }
}
const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    whatLeft: {  // 组件定义了一个上边框
        flex: 1,
        borderTopWidth: 1,
        borderColor: 'black',
        backgroundColor:'red' //每个界面背景颜色不一样
    }
});

顺便指出两点: 当根View没有指定背景色时, 默认值是一种灰色; 当子View没有指定背景色时,会继承父View的背景色。

更多精彩请关注微信公众账号likeDev,公众账号名称:爱上Android。
这里写图片描述

2016-12-22 13:45:17 xiangzhihong8 阅读数 4435

Navigator和NavigatorIOS

在开发中,需要实现多个界面的切换,这时候就需要一个导航控制器来进行各种效果的切换。在React Native中RN为我们提供了两个组件:Navigator和NavigatorIOS。
关于Navigator的介绍请查看之前的介绍文章:http://blog.csdn.net/xiangzhihong8/article/details/52624367
在使用Navigator导航器的时候需要重点掌握Navigator的几个方法:
getCurrentRoutes() 该进行返回存在的路由列表信息
jumpBack() 该进行回退操作 但是该不会卸载(删除)当前的页面
jumpForward() 进行跳转到相当于当前页面的下一个页面
jumpTo(route) 根据传入的一个路由信息,跳转到一个指定的页面(该页面不会卸载删除)
push(route) 导航切换到一个新的页面中,新的页面进行压入栈。通过jumpForward()方法可以回退过去
pop() 当前页面弹出来,跳转到栈中下一个页面,并且卸载删除掉当前的页面
replace(route) 只用传入的路由的指定页面进行替换掉当前的页面
replaceAtIndex(route,index) 传入路由以及位置索引,使用该路由指定的页面跳转到指定位置的页面
replacePrevious(route) 传入路由,通过指定路由的页面替换掉前一个页面
resetTo(route) 进行导航到新的界面,并且重置整个路由栈
immediatelyResetRouteStack(routeStack) 该通过一个路由页面数组来进行重置路由栈
popToRoute(route) 进行弹出相关页面,跳转到指定路由的页面,弹出来的页面会被卸载删除
popToTop() 进行弹出页面,导航到栈中的第一个页面,弹出来的所有页面会被卸载删除

<Navigator
    initialRoute={{ name: defaultName, component: defaultComponent }}
    configureScene={(route) => {
        return Navigator.SceneConfigs.HorizontalSwipeJumpFromRight;
    }}
    renderScene={(route, navigator) => {
        let Component = route.component;
        return <Component {...route.params} navigator={navigator} />
    }}
/>

常用方法:
push(route)
导航器跳转到一个新的路由。
pop()
回到上一页。
popN(n)
回到N页之前。当N=1的时候,效果和 pop() 一样。
replace(route)
替换当前页的路由,并立即加载新路由的视图。
replacePrevious(route)
替换上一页的路由/视图。
replacePreviousAndPop(route)
替换上一页的路由/视图并且立刻切换回上一页。
resetTo(route)
替换最顶级的路由并且回到它。
popToRoute(route)
一直回到某个指定的路由。
popToTop()
回到最顶层的路由。

示例小程序

这里写图片描述
1,定义外部组件

<TabBarIOS.Item
     icon={require('image!tabbar_home')}
     title="首页"
     selected={this.state.selectedItem == 'home'}
     onPress={()=>{this.setState({selectedItem:'home'})}}
 >
     <NavigatorIOS
          initialRoute ={ {
             component: Home, //具体的板块
             title:'网易新闻', //导航栏标题
          }}
    />
</TabBarIOS.Item>

Main主页面代码

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    TabBarIOS,
    NavigatorIOS
} from 'react-native';

// 引入外部的组件(此处注意是相当于了项目根目录)
var Home = require('../Component/Home');
var Message = require('../Component/Message');
var Find = require('../Component/Find');
var Mine = require('../Component/Mine');

var Main = React.createClass({
    // 初始化设置
    getInitialState(){
        return{
            // 设置默认选中的tabBarItem标识
            selectedItem:'home', // 默认让首页被选中
        }
    },

    render() {
        return (
            <TabBarIOS
                tintColor="orange"
            >
                {/*首页*/}
                <TabBarIOS.Item
                    icon={require('image!tabbar_home')}
                    title="首页"
                    selected={this.state.selectedItem == 'home'}
                    onPress={()=>{this.setState({selectedItem:'home'})}}
                >
                    <NavigatorIOS
                        initialRoute ={
                            {
                                component: Home, //具体的板块
                                title:'网易新闻', //导航栏标题
                            }
                        }
                    />
                </TabBarIOS.Item>
                {/*消息*/}
                <TabBarIOS.Item
                    icon={require('image!tabbar_message_center')}
                    title="消息"
                    selected={this.state.selectedItem == 'message'}
                    onPress={()=>{this.setState({selectedItem:'message'})}}
                >
                    <NavigatorIOS
                        initialRoute ={
                            {
                                component: Message, //具体的板块
                                title:'消息', //导航栏标题
                            }
                        }
                    />
                </TabBarIOS.Item>
                {/*发现*/}
                <TabBarIOS.Item
                    icon={require('image!tabbar_discover')}
                    title="发现"
                    selected={this.state.selectedItem == 'find'}
                    onPress={()=>{this.setState({selectedItem:'find'})}}
                >
                    <NavigatorIOS
                        initialRoute ={
                            {
                                component: Find, //具体的板块
                                title:'发现', //导航栏标题
                            }
                        }
                    />
                </TabBarIOS.Item>
                {/*我的*/}
                <TabBarIOS.Item
                    icon={require('image!tabbar_profile')}
                    title="我"
                    selected={this.state.selectedItem == 'mine'}
                    onPress={()=>{this.setState({selectedItem:'mine'})}}
                >
                    <NavigatorIOS
                        initialRoute ={
                            {
                                component: Mine, //具体的板块
                                title:'我', //导航栏标题
                            }
                        }
                    />
                </TabBarIOS.Item>
            </TabBarIOS>
        );
    }
});

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
});

// 输出类
module.exports = Main;
2017-11-15 15:07:01 darongzi1314 阅读数 1812

自定义NavigationBar的文件JS代码如下

/**
 * Created by lr on 2017/11/15.
 */
import React,{Component,PropTypes} from 'react'
import {
    View,
    Text,
    StyleSheet,
    Image,
    Platform,
    StatusBar
} from 'react-native';

const NAVBAR_HEIGHT_ANDROID=50;
const NAVBAR_HEIGHT_IOS=44;
const STATUS_BAR_HEIGHT=20;
const STATUS_BAR_SHAPE={
    backgroundColor:PropTypes.string,
    barStyle:PropTypes.oneOf(['default','light-content','dark-content']),
    hidden:PropTypes.bool
};
export default class NavigationBar extends Component{
    static propTypes={ 
        //设置约束
        style:View.propTypes.style,
        title:PropTypes.string,
        titleView:PropTypes.element,
        hide:PropTypes.bool,
        leftButton:PropTypes.element,
        rightButton:PropTypes.element,
        statusBar:PropTypes.shape(STATUS_BAR_SHAPE)
    }
    static defaultProps={
        statusBar:{
            barStyle:'light-content',
            hidden:false
        }
    }
    constructor(props){
        super(props);
        this.state={
            title:'',
            hide:false
        }
    }
    render(){
        let status=<View style={[styles.statusBar,this.props.statusBar]}>
            <StatusBar {...this.props.statusBar}/>
        </View>

        let titleView=this.props.titleView?this.props.titleView:<Text style={styles.title}>
            {this.props.title}
        </Text>

        let content=<View style={styles.navBar}>
            {this.props.leftButton}
            <View style={styles.titleViewContainer}>
                {titleView}
            </View>
            {this.props.rightButton}
        </View>

        return (
            <View style={[styles.container,this.props.style]}>
                {status}
                {content}
            </View>
        )
    }
}

const styles=StyleSheet.create({
    container:{
        flex:1,
        backgroundColor:'gray'
    },
    navBar:{
        justifyContent:'space-between',
        alignItems:'center',
        height:Platform.OS==='ios'?NAVBAR_HEIGHT_IOS:NAVBAR_HEIGHT_ANDROID,
        backgroundColor:'#93b300',
        flexDirection:'row'
    },
    titleViewContainer:{
        justifyContent:'center',
        alignItems:'center',
        position:'absolute',
        left:40,
        right:40,
        top:0,
        bottom:0
    },
    title:{
        fontSize:15,
        color:'white'
    },
    statusBar:{
        height:Platform==='ios'?STATUS_BAR_HEIGHT:20,
        // height:20
    }
});

使用方法:

var statusBar={
    backgroundColor:'#93b300',
    barStyle:'light-content'
}
//导航栏
let NavView=<View style={{height:64}}>
    <NavigationBar
        titleView={
            <View
                 style={{flexDirection:'row',height:44,justifyContent:'center', alignItems:'center'}}
            >
                 <Image
                      style={{width:62,height:13}}
                      source={require('../../res/images/1小时.png')}
                 />
                 <Text style={{color:'white',fontSize:15,marginLeft:7}}>张江路368号</Text>
                 <Image
                      style={{width:8,height:5,marginLeft:6}}
                      source={require('../../res/images/导航倒三角.png')}
                 />
            </View>
        }
        statusBar={statusBar}
    />
</View>

全页代码:

var {width,height}=Dimensions.get('window');

export default class PupularPage extends Component{
    constructor(props){
        super(props);
        this.dataRepository=new DataRepository();
        this.state={
            result:''
        }
    }
    render(){
        var statusBar={
            backgroundColor:'#93b300',
            barStyle:'light-content'
        }
        //导航栏
        let NavView=<View style={{height:64}}>
            <NavigationBar
                titleView={
                    <View
                         style={{flexDirection:'row',height:44,justifyContent:'center', alignItems:'center'}}
                    >
                         <Image
                              style={{width:62,height:13}}
                              source={require('../../res/images/1小时.png')}
                         />
                         <Text style={{color:'white',fontSize:15,marginLeft:7}}>张江路368号</Text>
                         <Image
                              style={{width:8,height:5,marginLeft:6}}
                              source={require('../../res/images/导航倒三角.png')}
                         />
                    </View>
                }
                statusBar={statusBar}
            />
        </View>
        return (
            <View style={styles.container}>
                {NavView}
                <ScrollableTableView
                    renderTabBar={()=><ScrollableTabBar/>}
                    tabBarUnderlineStyle={{backgroundColor:'#93b300'}}
                    tabBarTextStyle={{fontSize:14}}
                    tabBarActiveTextColor='#93b300'
                    tabBarInactiveTextColor='#333333'
                >
                    <PupularTab tabLabel="超级周三" category='c007'>超级周三</PupularTab>
                    <PupularTab tabLabel="蔬菜" category='c014'>水果</PupularTab>
                    <PupularTab tabLabel="肉禽" category='c016'>蔬菜</PupularTab>
                    <PupularTab tabLabel="水产" category='c018'>肉禽</PupularTab>
                    <PupularTab tabLabel="小厨" category='c022'>水产</PupularTab>
                </ScrollableTableView>
            </View>
        )
    }
}
class PupularTab extends Component{
    constructor(props){
        super(props);
        this.dataRepository=new DataRepository();
        this.state={
            result:'',
            dataSource:new ListView.DataSource({rowHasChanged:(r1,r2)=>r1!==r2})
        }
    }
    componentDidMount(){
        this.loadData();
    }
    loadData(){
        let url=URL
        let data={
            'app_id':'77U2GIRS',
            'category':this.props.category,
            'shop_id':'55b7aa5edc3041c3f068',
            'control_app_version':'340_iOS',
            'sign':'EC2CE717BCA2852DB14C3508746',
            'page_index':0,
            'page_size':150}
        let param=DataRepository.strDicToString(data)
        this.dataRepository.post(url,param)
            .then(result=>{
                this.setState({
                    dataSource:this.state.dataSource.cloneWithRows(result.data.skus)
                })
            })
            .catch(error=>{
                this.setState({
                    result:JSON.stringify(error)
                })
            })
    }
    renderRow(data){
        if (data.item_type==='sku'){
            return <RespositoryCell data={data} />
        }else {
            return <RespositorySectionCell data={data} />
        }
    }
    renderHeader(){
        return (
            <View style={{height:150}}>
                <Swiper
                    style={styles.wrapper}
                    height={150}
                    loop={true}
                    autoplay={true}
                    autoplayTimeout={3}
                    //轮播点未选中时候的样式
                    dot={
                        <View style={{width:8,height:8,backgroundColor:'#eee',borderRadius:5,margin:3}}></View>
                    }
                    //轮播点选中时候的样式
                    activeDot={
                        <View style={{width:8,height:8,backgroundColor:'#93b300',borderRadius:5,margin:3}}></View>
                    }
                    showsButtons={false}>
                    <Image source={{uri:'https://pos.xxx.cn/static/image/product/uf/uflpplrbfk.jpg'}} style={styles.img}/>
                    <Image source={{uri:'https://pos.xxx.cn/static/image/product/fv/fvdwrfvahh.jpg'}} style={styles.img}/>
                    <Image source={{uri:'https://pos.xxx.cn/static/image/product/pm/pmhskiwprn.jpg'}} style={styles.img}/>
                </Swiper>
            </View>
        )
    }
    render(){
        return (
            <View>
                <ListView
                    dataSource={this.state.dataSource}
                    renderRow={(data)=>this.renderRow(data)}
                    renderHeader={()=>this.renderHeader()}
                />
            </View>
        )
    }
}
const styles=StyleSheet.create({
    container:{
        flex:1
    },
    tips:{
        fontSize:30
    },
    wrapper:{
        backgroundColor:'cyan'
    },
    img:{
        width:width,
        height:150,
    }
});


效果图:




2019-03-19 14:31:50 qq_42720683 阅读数 1311

效果图:

                                                 

项目结构如下:

            

views和下面的页面手动创建

环境依赖如下:

  



步骤

  • 下载 react-native-scrollable-tab-view

$ npm install react-native-scrollable-tab-view --save
  • 编写代码

App.js

import React from 'react';
import { Button, Text, View,ScrollView} from 'react-native';
import { createStackNavigator, createBottomTabNavigator, createAppContainer } from 'react-navigation';
import ScrollableTabView, { ScrollableTabBar, } from 'react-native-scrollable-tab-view';

//导入自定义页面
import RedianPage from './views/RedianPage';
import ToutiaoPage from './views/ToutiaoPage';
import YulePage from './views/YulePage';

//主页、头部导航
class HomeScreen extends React.Component {
  static navigationOptions = {
    title: '网易新闻精选',
    headerTitleStyle:{
      paddingLeft:65,
      fontSize:40,
      color:'white'
    },
    headerStyle:{
      backgroundColor:'red',
    },
  };
  render() {
    return (
         <ScrollableTabView
            tabBarInactiveTextColor='red' // 没有被选中的文字颜色
            tabBarActiveTextColor='black'       // 选中的文字颜色
            tabBarBackgroundColor='white'     // 选项卡背景颜色
            tabBarUnderlineStyle={{backgroundColor:'white',height:1}}   //下划线的样式
            initialPage={0}
            renderTabBar={() => <ScrollableTabBar style={{height: 40,borderWidth:0,elevation:2}} tabStyle={{height: 39}}
                                                underlineHeight={2}/>}
          >
            <RedianPage tabLabel='热点' />
            <ToutiaoPage tabLabel='头条' />
            <YulePage tabLabel='娱乐' />
            <Text tabLabel='体育'>体育</Text>
            <Text tabLabel='新闻'>新闻</Text>
            <Text tabLabel='搞笑'>搞笑</Text>
            <Text tabLabel='头条'>头条</Text>
            <Text tabLabel='冷门'>冷门</Text>
            <Text tabLabel='体育'>体育</Text>
        </ScrollableTabView>
    );
  }
}

//视频页
class VideoScreen extends React.Component {
  render() {
    return (
      <View>
        <Text>22</Text>
      </View>
    );
  }
}

//我的页
class MineScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>我的</Text>  
      </View>
    );
  }
}

//注册导航
const HomeStack = createStackNavigator({
  Home: { screen: HomeScreen },
});

const SettingsStack = createStackNavigator({
  Video: { screen: VideoScreen },
});

const MineStack = createStackNavigator({
  Mine: { screen: MineScreen },
});

//底部导航栏
export default createAppContainer(createBottomTabNavigator(
  {
    Home: { screen: HomeStack },
    Video: { screen: SettingsStack },
    Mine:{ screen:MineStack }
  }
));

ToutiaoPage.js

import React from 'react';
import {
  Text,
  View,
  ScrollView,
} from 'react-native';
import ScrollableTabView, { ScrollableTabBar, } from 'react-native-scrollable-tab-view';

export default () => {
  return(
     <View>
        <Text>头条</Text>
      </View>
  );
}

YulePage.js

import React from 'react';
import {
  Text,
  View,
  ScrollView,
} from 'react-native';
import ScrollableTabView, { ScrollableTabBar, } from 'react-native-scrollable-tab-view';

export default () => {
  return(
     <View>
        <Text>娱乐</Text>
      </View>
  );
}

其他页面可以写入自己想要的布局

  • 启动项目

$ react-native run-android

 

 

                                 -----------本文完-----------

 

 

2018-06-18 00:24:30 u011272795 阅读数 3900

先上代码,配合代码讲解一下使用方法,
github地址:https://github.com/lizhuoyuan/react-native-navigationBar
我这有用到一个屏幕适配工具类,自己写的,使用方法和代码 请点这里

这里写图片描述
这里写图片描述

/**
 * Created by 李卓原 on 2018/7/6.
 * email: zhuoyuan93@gmail.com
 *
 */

import React from 'react';
import {
    Text,
    StyleSheet,
    View,
    Image,
    ImageBackground,
    TouchableOpacity,
    Platform,
    StatusBar,
    ViewPropTypes
} from 'react-native';

//ScreenUtil 为屏幕尺寸适配的工具类
import * as ScreenUtil from "../utils/ScreenUtil";
import PropTypes from 'prop-types';


const NAV_BAR_HEIGHT_ANDROID = 50;
const NAV_BAR_HEIGHT_IOS = 44;

const StatusBarShape = {
    barStyle: PropTypes.oneOf(['light-content', 'default', 'dark-content']),
    hidden: PropTypes.bool,
    backgroundColor: PropTypes.string,
};

class NavigationBar extends React.Component {

    static propTypes = {
        statusBar: PropTypes.shape(StatusBarShape),

        showBackgroundIMG: PropTypes.bool,

        style: ViewPropTypes.style,
        title: PropTypes.string,
        titleView: PropTypes.element,
        titleLayoutStyle: ViewPropTypes.style,

        showLeft: PropTypes.bool,
        leftText: PropTypes.string,
        leftTextStyle: ViewPropTypes.style,
        showleftImg: PropTypes.bool,
        leftButton: PropTypes.element,

        showRight: PropTypes.bool,
        rightText: PropTypes.string,
        rightTextStyle: ViewPropTypes.style,
        rightButton: PropTypes.element,
    };

    static defaultProps = {
        statusBar: {
            barStyle: 'default',
            hidden: false,
        },
        showLeft: true,
        showleftImg: true,  //是否显示返回箭头
        leftText: '',   //返回键位置的文字
        showRight: false,
        rightText: '更多',
        showBackgroundIMG: true  //是否有背景图片
    };

    constructor(props) {
        super(props);
    }

    render() {
        let leftButton = this._renderLeft();
        let rightButton = this._renderRight();
        let statusBar = <View>
            <StatusBar {...this.props.statusBar}/></View>;
        let titleView = this.props.titleView ? this.props.titleView :
            <Text style={[styles.titleStyle, this.props.titleLayoutStyle]}>{this.props.title}</Text>;
        let content = <View style={styles.content}>
            {leftButton}
            <View style={styles.titleView}>{titleView}</View>
            {rightButton}
        </View>;
        return (
            <ImageBackground source={this.props.showBackgroundIMG ? require('IMG/app_bar.png') : null}
                             style={[styles.container, this.props.style]}>
                {statusBar}
                {content}
            </ImageBackground>
        )
    }

    _renderLeft() {
        let {leftButton, leftTextStyle, showLeft, navigation, onLeftClick, leftText, showleftImg} = this.props;
        if (!showLeft) {
            return null;
        }
        if (leftButton == null) {
            return (
                <TouchableOpacity onPress={() => {
                    if (onLeftClick) {
                        onLeftClick();
                    } else {
                        if (navigation) navigation.goBack()
                    }
                }}>
                    <View style={styles.leftContainer}>
                        {
                            showleftImg ?
                                <Image source={require('IMG/back.png')}
                                       style={{
                                           width: ScreenUtil.scaleSize(25),
                                           height: ScreenUtil.scaleSize(25)
                                       }}/>
                                : null
                        }
                        <Text style={[styles.leftRightStyle, leftTextStyle]}>{leftText}</Text>
                    </View>
                </TouchableOpacity>)
        }
        return leftButton;
    }

    _renderRight() {
        let {rightButton, rightTextStyle, showRight, onRightClick, rightText} = this.props;
        if (!showRight) {
            return null;
        }
        if (rightButton == null) {
            return (
                <TouchableOpacity onPress={() => {
                    if (onRightClick) {
                        onRightClick()
                    }
                }}>
                    <View>
                        <Text style={[styles.leftRightStyle, rightTextStyle]}>{rightText}</Text>
                    </View>
                </TouchableOpacity>)
        }
        return rightButton;
    }
}

/**
 * //selector:这是你自己编写的一个函数。这个函数声明了你的组件需要整个 store 中的哪一部分数据作为自己的 props
 * 如果用不到redux这个方法可以删除
 * @param store
 * @returns {{color: *}}
 */
function changeColor(store) {
    return {
        color: store.changeColorReducer.color
    }
}


const styles = StyleSheet.create({
    container: {
        //backgroundColor:'green',
        width: ScreenUtil.screenW,
        height: ScreenUtil.scaleSize(58),
        justifyContent: 'center',
    },
    content: {
        justifyContent: 'space-between',
        flexDirection: 'row',
        alignItems: 'center',
        height: Platform.OS === 'ios' ? NAV_BAR_HEIGHT_IOS : NAV_BAR_HEIGHT_ANDROID,
    },
    titleView: {
        justifyContent: 'center',
        alignItems: 'center',
        position: 'absolute',
        left: 40,
        right: 40,
        top: 0,
        bottom: 0,
    },
    titleStyle: {
        fontSize: ScreenUtil.setSpText(18),
        color: 'white'

    },
    leftRightStyle: {
        color: 'white',
        fontSize: ScreenUtil.setSpText(14)
    },

    leftContainer: {
        marginLeft: ScreenUtil.scaleSize(17),
        flexDirection: 'row',
        alignItems: 'center'
    }
});

// 包装 component ,注入 dispatch 和 state 到其默认的 connect(selector)(App) 中;
/**
 * 把这个组件用connect包裹住就能拿到store。
 注意export default已经拿到下面来了,上面的class前面的导出要删掉
 用redux的话需要第一种方法导出,已注释掉
 */
//export default connect(changeColor)(withNavigation(NavigationBar));
//用不到redux推荐在定义类的时候直接导出,类定义为 export default class ...

//当这个组件拿不到this.props.navigation时,可使用withNavigation
//withNavigation是一个更高阶的组件,它将导航道具传递给一个包装组件。
//当您无法直接将导航道具传递到组件时,或者在深度嵌套子节点的情况下不希望传递导航道具时,它非常有用。
//导出方式:export default withNavigation(NavigationBar);
export default NavigationBar;

关于withNavigation的使用

在页面使用:

常规页面插入

    render() {
        return (
            <View style={{flex: 1}}>
                {this._renderNav()}
                ...
            </View>
        )
    }

    _renderNav() {
        return (
            <NavigationBar
                    title={'Main'}
                    //showLeft={false}
                    onLeftClick={()=>{
                                   alert('a')
                               }}
                    leftButton={()=><View><Text>自定义左侧按钮</Text></View>}
                    statusBar={{
                        barStyle: 'dark-content',
                        backgroundColor: 'white',
                        hidden: false,//true则隐藏
                    }}

                />
        )
    }

配合ReactNavigation使用

const RootStack = createStackNavigator({
    TaskList: {screen: TaskListPage},
    TaskDetails: {screen: TaskDetailsPage}
}, {
    navigationOptions: ({navigation}) => ({
        header: <NavigationBar title={navigation.state.routeName}
                               showLeft={navigation.state.routeName !== 'TaskList'}
                               navigation={navigation}
        />
    })
});

属性:

Prop Type Default Description
style ViewPropTypes.style - 标题栏的样式
showBackgroundIMG bool true 背景为一个图片
title string - 标题使用的字符串
titleLayoutStyle ViewPropTypes.style - 标题文字的样式
titleView PropTypes.element - 替换标题文字的组件
leftButton PropTypes.element - 自定义左侧按钮
leftText string - 左侧返回按钮的文字
leftTextStyle ViewPropTypes.style - 替换标题文字的组件
showLeft bool true 是否显示返回按钮
leftImg source的参数 ImageSourcePropType 左侧图片的地址
showleftImg bool true 是否显示标题的后退按钮,需自行替换成其他Image
showLeft bool true 是否显示返回按钮
onLeftClick func this.props.navigation.goBack() 左侧按钮的点击事件(默认为ReactNavigation的goback)
showRight bool false 是否显示右侧按钮
rightText string 更多 右侧按钮的文字
rightImg source的参数 ImageSourcePropType 右侧图片的地址,默认不传则不显示
rightTextStyle ViewPropTypes.style - 是否显示返回按钮
rightButton PropTypes.element - 自定义右侧按钮
onRightClick PropTypes.func - 右侧按钮的点击事件
statusBar { barStyle: PropTypes.oneOf([‘light-content’, ‘default’, ‘dark-content’]), hidden: PropTypes.bool, backgroundColor: PropTypes.string,} { barStyle: ‘default’, hidden: false,} 自定义状态栏

可以自己传入statusBar,默认值为:

 statusBar: {
            barStyle: 'default',
            hidden: false, //true则隐藏statusBar
        }

样式大概如此,颜色可以自己定义,
左侧按钮,中间文字,和右侧按钮都可以自己传入一个自己写的布局。
后续会完善功能和方法。

github地址

react-native 导航栏

阅读数 2836