React Native03

2019-12-19  本文已影响0人  LM林慕

此文项目代码:https://github.com/bei-yang/I-want-to-be-an-architect
码字不易,辛苦点个star,感谢!

引言


此篇文章主要涉及以下内容:

react navigation介绍

image.png

源于React Native社区对基于Javascript的可扩展且使用简单的导航解决方案的需求

react-natvigation自开源以来。在短短不到3个月的时间,github上星数已达4000+。Fb推荐使用库,并且在React Native当前最新版本0.44中将Navigator删除。react-navigation据称有原生般的性能体验效果。可能会成为未来React Native导航组件的主流军

安装

https://reactnavigation.org/docs/zh-Hans/getting-started.html

导航器

导航器也可以看成是一个普通的React组件,你可以通过导航器来定义你的APP中的导航结构。导航器还可以渲染通用元素,例如可以配置的标题栏和选项卡栏。

在react-navigation中有以下类型的导航器:

你可以通过以上几种导航器来创建你的APP,可以是其中一个也可以多个组合,这个可以根据具体的应用场景并结合每一个导航器的特性进行选择。

相关概念

在开始学习导航器之前,我们需要了解两个和导航有关的概念:

导航器所支持的Props

const SomeNav = createStackNavigator/createBottomTabNavigator/createMaterialTopTabNavigator({
  // config
});
<SomeNav
    screenProps = {xxx} //向子屏幕传递额外的数据,子屏幕可以通过this.props.screenProps获取到该数据
    ref = {nav => {navigation = nav;}} //可以通过ref属性获取到navigation;
    onNavigationStateChange=(prevState,newState,action)=>{
    //onNavigationStateChange:每次当导航器所管理的state发生改变时,都会回调该方法
    //prevState:变化之前的state
    //newState:新的state
    //action:导致state变化的action
    }
/>

Screen Navigation Prop(屏幕的navigation props)

当导航器中的屏幕被打开时,它会接收到一个navigation prop,它是整个导航环节的关键一员,接下来详细的讲解一下navigation prop的作用

navigation:包含以下功能

注意:一个navigation有可能没有navigate、setParams以及goBack,只有state与dispatch,所以在使用navigate时要进行判断,如果没有navigate可以使用navigation去dispatch一个新的action。如:

const {navigation,theme,selectedTab} = this.props;
const resetAction = StackActions.reset({
  index:0,
  action:[
    NavigationActions.navigate({
      routeName: 'HomePage',
      params:{
        theme:theme,
        selectedTab:selectedTab
      }
    })
  ]
})
navigation.dispatch(resetAction)

提示:这里的reset在2.0及以后的版本中,从NavigationActions中移到了StackActions中了,使用时记得留意。

StackNavigator的navigation的额外功能:

当且仅当当前navigator是stackNavigator时,this.props.navigation上有一些附加功能。这些函数是navigate和goBack的替代方法,你可以使用任何你喜欢的方法。这些功能是:

使用navigate进行界面之间的跳转

export const AppStackNavigator = createStackNavigator({
  HomeScreen: {
    screen: HomeScreen
  },
  Page1: {
    screen: Page1
  }
})

class HomeScreen extends React.Component {
  render(){
    const {navigate} = this.props.navigation;
    return (
        <View>
            <Text>This is HomeScreen</Text>
            <Button
                onPress={()=> navigate('Page1',{
            name: 'David'
                })}
                title = "Go to Page1"
            />
        </View>
    )
  }
}

使用state的params

可以通过this.props.state.params来获取通过setParams(),或navigation.navigate()传递的参数。

<Button
    title={params.mode === 'edit' ? '保存' : '编辑'}
    onPress={()=>
        setParams({
      mode: params.mode === 'edit'? '' : 'edit'
        })
    }
/>
<Button
    title="Go to Page1"
    onPress={()=>{
    navigation.navigate('Page1',{name:'David'})
    }}
/>
const {navigation} = this.props
const {state,setParams} = navigation
const {params} = state
const showText = params.mode === 'edit'? '正在编辑':'编辑完成'

使用setParams改变route Params

class ProfileScreen extends React.Component {
  render(){
    const {setParams} = this.props.navigation
    return(
        <Button
            onPress={()=>setParams({name: 'Lucy'})}
            title="set title name to 'Lucy'"  
        />
    )
  }
}

注意navigation.setParams改变的是当前页面的Params,如果要改变其他页面的Params可以通过NavigationActions.setParams完成,下文会讲到

使用goBack返回上一页面或指定页面

navigation.state {params: {…},key:"id-1517035332238-4",routeName:"Page1"}

export default class Page1 extends React.ComPonent {
  render(){
    const {navigation} = this.props;
    return <View>
        <Text>欢迎来到Page1</Text>
        <Button
            title="Go Back"
            onPress={()=>{
          navigation.goBack()
            }}
        />
    </View>
  }
}

通过dispatch 发送一个action

const resetAction = StackActions.reset({
  index:0,
  actions:[
    NavigationActions.navigate({
      routeName: 'HomePage',
      params:{
        theme:theme,
        selectedTab:selectedTab
      }
    })
  ]
})
navigation.dispatch(resetAction)

NavigationActions

Navigate

navigate action 会使用 navigate action的结果来更新当前的state

navigate({routeName,params,action,key})
import {NavigationActions} from 'react-navigation'
const navigateAction = NavigationActions.navigate({
  routeName: 'Profile',
  params: {},
  action: NavigationActions.navigate({
    routeName: 'SubProfileRoute'
  })
})
this.props.navigation.dispatch(navigationAction)

Back

返回到前一个screen并且关闭当前screen.backaction creator 接受一个可选的参数:

back(key)

import { NavigationActions } from 'react-navigation'
const backAction = NavigationActions.back();
this.props.navigation.dispacth(backAction)

SetParams

通过SetParams我们可以修改指定页面的Params

import {NavigationActions} from 'react-navigation'
const setParamsAction = NavigationActions.setParams({
  params:{title:'HomePage'},
  key:'id-1517035332238-4'
})

有很多小伙伴可能会问:navigation中有setParams为什么还要有NavigationActions.setParams?

从两方面来回答一下这个问题:

StackActions

Reset

Reset action删掉所有的navigation state并且使用这个actions的结果来代替

createStackNavigator-普通导航 堆栈

createStackNavigator提供APP屏幕之间切换的能力,它是以栈的形式还管理屏幕之间的切换,新切换到的屏幕会放在栈的顶部。

createStackNavigator API

createStackNavigator(RouteConfigs, StackNavigatorConfig):

RouteConfigs

RouteConfigs支持三个参数screenpath以及navigationOptions

StackNavigatorConfig

react-navigation源码中可以看出StackNavigatorConfig支持配置的参数有10个。

function createStackNavigator(routeConfigMap, stackConfig = {}) {
  const {
    initialRouteKey,
    initialRouteName,
    initialRouteParams,
    paths,
    defaultNavigationOptions,
    disableKeyboardHandling,
    getCustomActionCreators
  } = stackConfig;
  ...

这7个参数可以根据作用不同分为路由配置、视图样式配置两类,首先看用于路由配置的参数:

用于路由配置的参数:

用于导航样式配置的参数:

navigationOptions(屏幕导航选项)

支持一下参数:

创建一个StackNavigator类型的导航器

export const AppStackNavigator = createStackNavigator({
    HomePage: {
        screen: HomePage
    },
    Page1: {
        screen: Page1,
        navigationOptions: ({navigation}) => ({
            title: `${navigation.state.params.name}页面名`//动态设置navigationOptions
        })
    },
    Page2: {
        screen: Page2,
        navigationOptions: {//在这里定义每个页面的导航属性,静态配置
            title: "This is Page2.",
        }
    },
    Page3: {
        screen: Page3,
        navigationOptions: (props) => {//在这里定义每个页面的导航属性,动态配置
            const {navigation} = props;
            const {state, setParams} = navigation;
            const {params} = state;
            return {
                title: params.title ? params.title : 'This is Page3',
                headerRight: (
                    <Button
                        title={params.mode === 'edit' ? '保存' : '编辑'}
                        onPress={() =>
                            setParams({mode: params.mode === 'edit' ? '' : 'edit'})}
                    />
                ),
            }
        }
    },
}, {
    defaultNavigationOptions: {
        // header: null,// 可以通过将header设为null 来禁用StackNavigator的Navigation Bar
    }
});

配置navigationOptions:

步骤一的代码中通过两种方式配值了navigationOptions:

静态配置:

对Page2的navigationOptions配置是通过静态配置完成的:

Page2: {
    screen: Page2,
    navigationOptions: {//在这里定义每个页面的导航属性,静态配置
        title: "This is Page2.",
    }
},

这种方式被称为静态配置,因为navigationOptions中的参数是直接Hard Code的不依赖于变量。

动态配置:

对Page3的navigationOptions配置是通过动态配置完成的:

Page3: {
    screen: Page3,
    navigationOptions: (props) => {//在这里定义每个页面的导航属性,动态配置
        const {navigation} = props;
        const {state, setParams} = navigation;
        const {params} = state;
        return {
            title: params.title ? params.title : 'This is Page3',
            headerRight: (
                <Button
                    title={params.mode === 'edit' ? '保存' : '编辑'}
                    onPress={() =>
                        setParams({mode: params.mode === 'edit' ? '' : 'edit'})}
                />
            ),
        }
    }
},

从上述代码中可以看出Page3的navigationOptions依赖于props这个变量所以是动态的,当props中的内容发生变化时,navigationOptions也会跟着变化;

提示:除了在创建createStackNavigator时配置navigationOptions外,在StackNavigator之外也可以配置navigationOptions;

createStackNavigator之外也可以配置navigationOptions

方式一:

Page2.navigationOptions = {
     title: "This is Page2.",
};

方式二:

export default class Page1 extends React.Component {
    //也可在这里定义每个页面的导航属性,这里的定义会覆盖掉别处的定义
    static navigationOptions = {
        title: 'Page1',
    };
    ...

第三步:界面跳转

export default class HomePage extends React.Component {
    //在这里定义每个页面的导航属性
    static navigationOptions = {
        title: 'Home',
        headerBackTitle:'返回哈哈',//设置返回此页面的返回按钮文案,有长度限制
    }

    render() {
        const {navigation} = this.props;
        return <View style=>
            <Text style={styles.text}>欢迎来到HomePage</Text>
            <Button
                title="Go To Page1"
                onPress={() => {
                    navigation.navigate('Page1', {name: '动态的'});
                }}
            />
            <Button
                title="Go To Page2"
                onPress={() => {
                    navigation.navigate('Page2');
                }}
            />
            <Button
                title="Go To Page3"
                onPress={() => {
                    navigation.navigate('Page3',{ name: 'Devio' });
                }}
            />
        </View>
    }
}

代码解析:

页面跳转可分为两步:

第四步:更新页面Params与返回

export default class Page3 extends React.Component {
    render() {
        const {navigation} = this.props;
        const {state, setParams} = navigation;
        const {params} = state;
        const showText = params.mode === 'edit' ? '正在编辑' : '编辑完成';
        return <View style=>
            <Text style={styles.text}>欢迎来到Page3</Text>
            <Text style={styles.showText}>{showText}</Text>
            <TextInput
                style={styles.input}
                onChangeText={text=>{
                    setParams({title:text})
                }}
            />
            <Button
                title="Go Back"
                onPress={() => {
                    navigation.goBack();
                }}
            />
        </View>
    }
}

代码解析:

在上述代码中通过:

 <TextInput
    style={styles.input}
    onChangeText={text=>{
        setParams({title:text})
    }}
/>

将输入框中内容的变化,通过setParams({title:text})更新到页面的标题上,你会看到当输入框中内容发生变化时,标题也会跟着变。

当用户单击Go Back按钮时,通过:

navigation.goBack();

实现了返回上一页;

createBottomTabNavigator-底部导航

相当于iOS里面的TabBarController

createBottomTabNavigator API

createBottomTabNavigator(RouteConfigs, BottomTabNavigatorConfig):

从createBottomTabNavigator API上可以看出createBottomTabNavigator支持通过RouteConfigsBottomTabNavigatorConfig两个参数来创建createBottomTabNavigator导航器。

RouteConfigs

RouteConfigs支持三个参数screenpath以及navigationOptions

BottomTabNavigatorConfig

tabBarOptions(tab配置)

navigationOptions(屏幕导航选项)

createBottomTabNavigator支持的屏幕导航选项的参数有:

案例

export const AppTabNavigator = createBottomTabNavigator({
    Page1: {
        screen: Page1,
        navigationOptions: {
            tabBarLabel: 'Page1',
            tabBarIcon: ({tintColor, focused}) => (
                <Ionicons
                    name={focused ? 'ios-home' : 'ios-home-outline'}
                    size={26}
                    style=
                />
            ),
        }
    },
    Page2: {
        screen: Page2,
        navigationOptions: {
            tabBarLabel: 'Page2',
            tabBarIcon: ({tintColor, focused}) => (
                <Ionicons
                    name={focused ? 'ios-people' : 'ios-people-outline'}
                    size={26}
                    style=
                />
            ),
        }
    },
    Page3: {
        screen: Page3,
        navigationOptions: {
            tabBarLabel: 'Page3',
            tabBarIcon: ({tintColor, focused}) => (
                <Ionicons
                    name={focused ? 'ios-chatboxes' : 'ios-chatboxes-outline'}
                    size={26}
                    style=
                />
            ),
        }
    },
}, {
    tabBarComponent: TabBarComponent,
    tabBarOptions: {
        activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff',
    }
});

在上述代码中使用了react-native-vector-icons的矢量图标作为Tab的显示图标,tabBarIcon接收一个React 组件,大家可以根据需要进行定制:

react-native-vector-icons

安装

yarn add react-native-vector-icons

react-native link react-native-vector-icons

图标库地址:

https://oblador.github.io/react-native-vector-icons/

createMaterialTopTabNavigator-顶部导航

createMaterialTopTabNavigator API

createMaterialTopTabNavigator(RouteConfigs, TabNavigatorConfig):

从createMaterialTopTabNavigator API上可以看出createMaterialTopTabNavigator支持通过RouteConfigsTabNavigatorConfig两个参数来创建createMaterialTopTabNavigator导航器。

RouteConfigs

RouteConfigs支持三个参数screenpath以及navigationOptions

TabNavigatorConfig

tabBarOptions(tab配置)

navigationOptions(屏幕导航选项)

createMaterialTopTabNavigator支持的屏幕导航选项的参数有:

createDrawerNavigator-抽屉导航

createDrawerNavigator API

createDrawerNavigator(RouteConfigs, DrawerNavigatorConfig):

从createDrawerNavigator API上可以看出createDrawerNavigator支持通过RouteConfigsDrawerNavigatorConfig两个参数来创建createDrawerNavigator导航器。

RouteConfigs

RouteConfigs支持三个参数screenpath以及navigationOptions

DrawerNavigatorConfig

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

import { createDrawerNavigator } from 'react-navigation';

import Ionicons from 'react-native-vector-icons/Ionicons';


const HomeScreen=({navigation})=>(
        <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <Text>HomeScreen</Text>
            <Button
                 onPress={()=>(navigation.toggleDrawer())} 
                 title="Open Drawer"
            ></Button>
        </View>
    );

const ProfileScreen=()=>(
        <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}><Text>ProfileScreen</Text></View>
    );

const RootDrawer =createDrawerNavigator({
    Home:{
        screen:HomeScreen,
        navigationOptions:{
            drawerLabel:'MyHome',
            drawerIcon:({tintColor, focused })=>(
                <Ionicons                   
                      name={'ios-home'}
                      size={20}
                      style={{ color: tintColor }}
                ></Ionicons>
                ),
        },
    },
    Profile:{
        screen:ProfileScreen,
        navigationOptions:{
            drawerLabel:'MyProfile',
            drawerIcon:({tintColor})=>(
                <Ionicons 
                    name={'ios-person'}
                    size={20}
                    style={{color:tintColor}}
                ></Ionicons>
                ),
        },
    }
});

export default RootDrawer;

自定义侧边栏(contentComponent)

DrawerNavigator有个默认的带滚动的侧边栏,你也可以通过重写这个侧边栏组件来自定义侧边栏:

contentComponent:(props) => (
    <ScrollView style=>
        <SafeAreaView forceInset=>
            <DrawerItems {...props} />
        </SafeAreaView>
    </ScrollView>
)

DrawerItems的contentOptions

contentOptions主要配置侧滑栏item的属性,只对DrawerItems,例如我们刚才写的例子,就可以通过这个属性来配置颜色,背景色等。其主要属性有:

eg:

contentOptions: {
  activeTintColor: '#e91e63',
  itemsContainerStyle: {
    marginVertical: 0,
  },
  iconContainerStyle: {
    opacity: 1
  }
}

navigationOptions(屏幕导航选项)

DrawerNavigator支持的屏幕导航选项的参数有:

侧边栏操作(打开、关闭、切换)

侧边栏支持以下几种操作方式:

navigation.openDrawer();
navigation.closeDrawer();
navigation.toggleDrawer();
//或
navigation.dispatch(DrawerActions.openDrawer());
navigation.dispatch(DrawerActions.closeDrawer());
navigation.dispatch(DrawerActions.toggleDrawer());

其中路由名openDrawer对应这打开侧边栏的操作,DrawerClose对应关闭侧边栏的操作,toggleDrawer对应切换侧边栏操作,要进行这些操作我么还需要一个navigationnavigation可以从props中获取;

createSwitchNavigator-开关导航

SwitchNavigator 的用途是一次只显示一个页面。 默认情况下,它不处理返回操作,并在你切换时将路由重置为默认状态。

createSwitchNavigator API

createSwitchNavigator(RouteConfigs, SwitchNavigatorConfig):

SwitchNavigatorConfig

几个被传递到底层路由以修改导航逻辑的选项:

const AppStack = createStackNavigator({
    Home: {
        screen: HomePage
    },
    Page1: {
        screen: Page1
    }
});
const AuthStack = createStackNavigator({
    Login: {
        screen: Login
    },
},{
    navigationOptions: {
        // header: null,// 可以通过将header设为null 来禁用StackNavigator的Navigation Bar
    }
});

export default createSwitchNavigator(
    {
        Auth: AuthStack,
        App: AppStack,
    },
    {
        initialRouteName: 'Auth',
    }
);

你的赞是我前进的动力

求赞,求评论,求分享...

上一篇下一篇

猜你喜欢

热点阅读