react-navigation文档翻译

一. Hello Mobile Navigation

让我们使用React Naviation来创建一个简单的聊天会话应用,可以跨平台运行在安卓和iOS客户端。

Setup and Installation- 配置和安装

首先,确定你已经配置好了React Native开发环境;然后,创建一个新的项目;安装react-navigation包:

# Create a new React Native App
react-native init SimpleApp
cd SimpleApp

# Install the latest version of react-navigation from npm
npm install --save react-navigation

# Run the new app
react-native run-android # or:
react-native run-ios

检查你的iOS或者安卓设备是否成功显示出了ReactNative的初始App界面。
我们想要在iOS和Android平台共享一份代码,让我们删除index.ios.jsindex.android.js文件的内容,使用import './App'; 的写法。
现在为我们app的实现创建一个新的文件:App.js

Introducing Stack Navigator -简介

对于我们的app,我们想要实现一个概念性的栈导航器,在这个导航器中,每一个新的场景组件都被放入到栈的顶部,并且返回时同样移除栈顶的那个场景组件。我们可以使用StackNavigator。让我们从一个组件场景的情况开始。

注意:场景组件既为一般的组件,该组件被react-navigation渲染成一个路由页面。

import React from 'react';
import {
  AppRegistry,
  Text,
} from 'react-native';
import { StackNavigator } from 'react-navigation';

class HomeScreen extends React.Component {
  static navigationOptions = {
    title: 'Welcome',
  };
  render() {
    return <Text>Hello, Navigation!</Text>;
  }
}

const SimpleApp = StackNavigator({
  Home: { screen: HomeScreen },
});

AppRegistry.registerComponent('SimpleApp', () => SimpleApp);

这个场景的标题static navigationOptions中是可以配置的。那里有很多关于导航的选项都可以被配置。
现在同一个路由场景可以在iPhone和Android应用中出现。

Adding a New Screen-添加一个新的组件场景

App.js文件中,让我们添加一个新的组件场景,叫做ChatScreen

class ChatScreen extends React.Component {
  static navigationOptions = {
    title: 'Chat with Lucy',
  };
  render() {
    return (
      <View>
        <Text>Chat with Lucy</Text>
      </View>
    );
  }
}

我们在HomeScreen组件中添加一个按钮,使用Chat的路由名称链接到ChatScreen

class HomeScreen extends React.Component {
  static navigationOptions = {
    title: 'Welcome',
  };
  render() {
    const { navigate } = this.props.navigation;
    return (
      <View>
        <Text>Hello, Chat App!</Text>
        <Button
          onPress={() => navigate('Chat')}
          title="Chat with Lucy"
        />
      </View>
    );
  }
}

我们使用的navigate函数来跳转到ChatScreen组件场景。该函数可以从当前场景(HomeScreen)的navigation属性中找到。但是想要实现跳转,我们必须将ChatScreen组件添加到我们的StackNavigator中:

const SimpleApp = StackNavigator({
  Home: { screen: HomeScreen },
  Chat: { screen: ChatScreen },
});

现在你可以使用navigate函数跳转到一个新的组件场景,然后返回。

first-navigation-iphone.gif

Passing params-传递参数

ChatScreen组件场景强制固定所有参数的做法并不可取,如果我们可以传递一个参数到ChatScreen组件场景,这将比较有用。让我们开始做吧。在我们调用navigate函数,添加一个指定的路由名字的时候,我们可以传递参数pasrams到新的路由中。首先,我们编辑HomeScreen组件场景,传递一个user的参数到新的路由。

class HomeScreen extends React.Component {
  static navigationOptions = {
    title: 'Welcome',
  };
  render() {
    const { navigate } = this.props.navigation;
    return (
      <View>
        <Text>Hello, Chat App!</Text>
        <Button
          onPress={() => navigate('Chat', { user: 'Lucy' })}
          title="Chat with Lucy"
        />
      </View>
    );
  }
}

我们可以编辑ChatScreen组件场景,使用路由传过来的user参数:

class ChatScreen extends React.Component {
  // Nav options can be defined as a function of the screen's props:
  static navigationOptions = ({ navigation }) => ({
    title: `Chat with ${navigation.state.params.user}`,
  });
  render() {
    // The screen's current route is passed in to `props.navigation.state`:
    const { params } = this.props.navigation.state;
    return (
      <View>
        <Text>Chat with {params.user}</Text>
      </View>
    );
  }
}

现在你可以看到,当你使用navigate函数跳转到ChatScreen路由页面时传递的name参数。尝试修改HomeScreen组件场景,看看将会发生什么。

first-navigation-iphone.png

二. Nesting Navigators

由多种形式导航构成的移动应用是很常见的。在React Navigation中,路由和导航都是存在的,允许你为应用构建复杂的导航结构。在我们的聊天会话应用中,我们想要在一个组件场景中放入一个tab标签,来浏览最近的会话记录或者所有的联系人。

Introducing Tab Navigator-tab导航简介

让我们在App.js中创建一个新的tab导航(TabNavigation):

import { TabNavigator } from "react-navigation";

class RecentChatsScreen extends React.Component {
  render() {
    return <Text>List of recent chats</Text>
  }
}

class AllContactsScreen extends React.Component {
  render() {
    return <Text>List of all contacts</Text>
  }
}

const MainScreenNavigator = TabNavigator({
  Recent: { screen: RecentChatsScreen },
  All: { screen: AllContactsScreen },
});

如果**MainScreenNavigator **场景组件作为导航的顶层组件,应用将会看起来像这样:


simple-tabs-iphone.png

Nesting a Navigator in a screen-一个场景中嵌套一个导航

我们想要这些tab标签在应用中的一个场景中出现,但是导航栈中新的场景可以覆盖这些tab标签。
让我们将tabs导航作为一个顶层场景组件添加到我们之前创建的StackNavigator导航中。

const SimpleApp = StackNavigator({
  Home: { screen: MainScreenNavigator },
  Chat: { screen: ChatScreen },
});

由于MainScreenNavigator已经成为SimpleApp的一个场景组件,我们可以设置navigationOptions

MainScreenNavigator.navigationOptions = {
  title: 'My Chats',
};

注意:MainScreenNavigator本质上是一个React组件类,可以设置静态方法 navigationOptions。

让我们给每一个tab标签也添加一个按钮来链接到一个聊天会话场景。

<Button
  onPress={() => this.props.navigation.navigate('Chat', { user: 'Lucy' })}
  title="Chat with Lucy"
/>

现在我们已经将一个导航器(navigator)嵌套到另外一个中。我们可以在导航器之间使用navigate方法。


nested-iphone.png.gif

注意: 导航器的分类 、嵌套与跳转

  • 分类:StackNavigator为screen路由切换,TabNavigator为tab路由切换。
  • 嵌套:StackNavigator与TabNavigator可以相互多层嵌套。
  • 跳转:多层嵌套时,不同层级的组件场景都可以通过navigate(title)方法进行跳转。

三. Configuring the Header

只有StackNavigator导航系统支持设置头部。

在之前的例子中,我们在App中创建了一个StackNavigation来展示几个界面。
当导航到一个会话界面的时候,我们可以通过向navigate方法中传递参数来向新的路由传递。在这个时候,我们希望在会话界面中提供人的名字(Lucy)。

this.props.navigation.navigate('Chat', { user:  'Lucy' });

user参数可以被会话界面所接收:

class ChatScreen extends React.Component {
  render() {
    const { params } = this.props.navigation.state;
    return <Text>Chat with {params.user}</Text>;
  }
}

Setting the Header Title - 设置头部标题

Next, the header title can be configured to use the screen param:
下面,可以使用参数来定义头部标题:

class ChatScreen extends React.Component {
  static navigationOptions = ({ navigation }) => ({
    title: `Chat with ${navigation.state.params.user}`,
  });
  ...
}

Adding a Right Button - 添加一个右侧按钮

接下来我们添加一个 头部导航选项 来允许我们添加一个定制的右侧按钮:

static navigationOptions = {
  headerRight: <Button title="Info" />,
  ...

导航器选项可以根据导航器属性来定义。让我们根据路由参数来渲染一个不同的按钮,并且按钮当按下时执行navigation.setParams方法。

static navigationOptions = ({ navigation }) => {
  const {state, setParams} = navigation;
  const isInfo = state.params.mode === 'info';
  const {user} = state.params;
  return {
    title: isInfo ? `${user}'s Contact Info` : `Chat with ${state.params.user}`,
    headerRight: (
      <Button
        title={isInfo ? 'Done' : `${user}'s info`}
        onPress={() => setParams({ mode: isInfo ? 'none' : 'info'})}
      />
    ),
  };
};

现在,头部可以影响视图的路由/状态:
看其余的头部选项信息, 到头部选项文档

四. NavigationPlayground

App.js - StackNavigator路由栈

  1. StyleSheet定义最细的线
//这一常量定义了当前平台上的最细的宽度。可以用作边框或是两个元素间的分隔线。
StyleSheet.hairlineWidth 
  1. StackNavigator方法参数
const AppNavigator = StackNavigator({
  ...ExampleRoutes,
  Index: {
    screen: MainScreen,
  },
}, {
  //初始页面: 如果不设置该项,则显示位置在第一个的页面组件(SimpleStack)。
  initialRouteName: 'Index',
    //头部什么时候重新渲染:
    //float: 只有一个head头部,页面切换的时候有动画,ios里常用。
    //screen: 每一个屏幕都有一个头部,安卓里常用。
    //none: 不渲染head头部(该AppNavigator路由就没有头部)
  headerMode: 'none',
    // 路由切换动画
    // card: 使用安卓ios默认切换方式。ios-左滑动出现,android-向上渐变。
    // modal: 从屏幕底部向上弹出,只在ios上生效。
  mode: Platform.OS === 'ios' ? 'modal' : 'card',
});
  1. 遍历对象
//routeName表示key值
Object.keys(ExampleRoutes).map(
     (routeName: string) =>{
                //Todo
      }

SimpleStack.js - StackNavigator路由栈

  1. ES6中的变量与字符串的拼接方法
//``内部防止字符串和变量
//${}内放变量,${}外放字符串
//变量与变量之间没有连接符(${}${});字符串与变量之间(${}字符串)没有连接符
   `${params.name}'s Profile!`;
`${navigation.state.params.name}'s Photos`;
//可以换行
`${navigation.state.params.mode === 'edit' ? 'Now Editing ' : '' }${navigation.state.params.name}'s Profile`
  1. 使用navigate方法可以推入哪些路由页面。
AppNavigator  = StackNavigator (
      A: {screen: A}, 
      B: {screen: B}
)
B = StackNavigator({
       B1 : {screen: B1},
       B2: {screen: B2},
       B3: {screen: B3}
});

分析: ①同一级路由页面之间可以使用navigator方法跳转: A、B之间;B1、B2、B3之间。②不同级路由之间可以相互跳转(不推荐这样使用),A可以直接跳转到B1、B2、B3;B1、B2、B3可以直接跳转到A。
注意: A.navigator(B) 与A.navigator(B1)的对比:因为B路由栈的初始路由页面是B1,所以两者都是显示B1页面;但是前者是A-B1的过程,而后者是A-B1-B1的过程。

  1. navigation.goBack(null) 返回到哪里
    不管有多层嵌套,navigation.goBack(null)都返回加载此页面的地方。
    即: navigation.goBack(null)既有可能返回到当前路由栈的上一个页面,又可能返回多上一级路由栈
  2. 一个路由栈嵌套在另一个路由栈中,父路由栈展示子路由栈的时候如何隐藏头部
//SimpleStack路由栈嵌套在AppNavigator路由栈中
//设置AppNavigator展示SimpleStack时无header头部。
//SimpleStack.navigationOptions这种写法与static navigationOptions写法对比
SimpleStack.navigationOptions = {
    header: null
};
  1. React无状态组件特点
  • 不需要声明类,可以避免大量的譬如extends或者constructor这样的代码;
  • 不需要显示声明this关键字 ,可以不用绑定this;
  • 支持设置默认的Props类型与值;
  • 可以访问Prop和Context的
const Text = (props, context) =>
        <p style={context}>props.children</p>;
Text.contextTypes = {
       fontFamily: React.PropTypes.string
};
Text.propTypes = { children: React.PropTypes.string };
Text.defaultProps = { children: 'Hello World!' };
class App extends React.Component {
  static childContextTypes = {
        fontFamily: React.PropTypes.string
  }
  getChildContext() {
         return {
           fontFamily: 'Helvetica Neue'
         };
   }
  render() {
         return <Text>Hello World</Text>;
  }
}

注意:我们会使用函数式无状态组件,除非需要本地 state 或生命周期函数的场景 。无状态组件输出方式完全取决于两个参数。
扩展: Context,React中隐藏的秘密!

SimpleTabs.js - TabNavigator路由栈

  1. 如何设置TabNavigator的被选中tab的颜色
const SimpleTabs = TabNavigator({
      Home: {screen: MyHomeScreen},
      People: {screen: MyPeopleScreen},
      Chat: {screen: MyChatScreen},
      Settings: {screen: MySettingsScreen}
    }, {
      tabBarOptions: {
          //被选中tab的tintColor颜色值
          activeTintColor: Platform.OS === 'ios' ? '#e91e63' : '#fff',
      },
      //tab在页面的顶部(top)还是底部(bottom)
      tabBarPosition: 'bottom',
      //是否有切换动画
      animationEnabled: true,
      //是否可以手指滑动切换
      swipeEnabled: true,
});
  1. 如何配置每一个tab的一些信息
MySettingsScreen.navigationOptions = {
        //设置tab文字
      tabBarLabel: 'Settings',
        //设置tab图标
      tabBarIcon: ({ tintColor, focused }) => (
        //tintyColor在被选中时和未被选中时颜色值不同
        //focused是一个是否被选中的布尔值
        <Ionicons
            name={focused ? 'ios-settings' : 'ios-settings-outline'}
            size={26}
            style={{ color: tintColor }}
        />
      ),
};

Drawer.js - DrawerNavigator

  1. DrawerNavigator方法
const DrawerExample = DrawerNavigator({
      Inbox: {screen: InboxScreen},
      Drafts: {screen: DraftsScreen},
  }, {
      //初始展示项
      initialRouteName: 'Drafts',
      contentOptions: {
        //被选中drawer的tintColor颜色值
        activeTintColor: '#e91e63'
  },
});
  1. DraftScreen组件
const DraftsScreen = ({ navigation }) => (
      <MyNavScreen
        banner={'Drafts Screen'}
        navigation={navigation}
      />
);
DraftsScreen.navigationOptions = {
      //drawer显示的文字
      drawerLabel: 'Drafts',
      drawerIcon: ({ tintColor }) => (
      //tintColor在被选中和未被选中时的颜色不同。
          <MaterialIcons
            name="drafts"
            size={24}
            style={{ color: tintColor }}
        />
    ),
};
  1. MyNavScreen组件
const MyNavScreen = ({ navigation, banner }) => (
      <ScrollView style={styles.container}>
        <SampleText>{banner}</SampleText>
        <Button
          //注意:打开drawer的方法
          onPress={() => navigation.navigate('DrawerOpen')}
          title="Open drawer"
        />
        <Button
          onPress={() => navigation.goBack(null)}
          title="Go back"
        />
      </ScrollView>
);

其他

  1. StackNavigator隐藏header的两种写法的对比
//写法一
const ProfileNavigator = StackNavigator({
        Home: {screen: MyHomeScreen},
        Profile: {screen: MyProfileScreen},
}, {
        //内外层header都不渲染
        navigationOptions: {header: null}
});
//写法二
const ProfileNavigator = StackNavigator({
        Home: { screen: MyHomeScreen},
        Profile: {screen: MyProfileScreen},
}, {
        //内层header不渲染
        headerMode: 'none' 
});
//写法三
ProfileNavigator.navigationOptions = {
        //外层不渲染
        header: null
}

**写法一: ** 设置MyHomeScreenMyProfileScreen路由页面没有头部。设置的是内层和外层路由页面都没有头部。 优先级低于在MyHomeScreenMyProfileScreen通过 static navigationOptions方法设置。
**写法二: ** 设置MyHomeScreenMyProfileScreen路由页面没有头部。设置的只是内层路由页面没有头部。
写法三: 表示ProfileNavigator作为一个外层路由页面时没有头部。
引申: note this changed starting beta9

Before:
        { header: { visible: false, }}
Now:
        { header: null,}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,818评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 原文链接:https://github.com/halfrost/Halfrost-Field/blob/mast...
    hament阅读 5,647评论 1 31
  • 对我而言的很重要的“考试”结束了,之后的时间里面,第一时间投入了实习工作中。在实习的这段时间里,对于在校的教...
    计研社阅读 2,017评论 2 25
  • 【目录】 这是《落叶》文集里第68片落叶,希望你能喜欢,不为别的,只为这份坚持。 今天我这个老兵再来说说敏捷这个看...
    秋之川阅读 387评论 0 0