React Navigation提供了一种在屏幕之间切换并管理导航历史记录的方式。 若是您的应用程序只使用一个 stack navigator ,则它在概念上相似于Web浏览器处理导航状态的方式 - 当用户与它进行交互时,应用程序会从导航堆栈中新增和删除页面,这会致使用户看到不一样的页面。 Web浏览器和 React Navigation 工做原理的一个主要区别是:React Navigation 的 stack navigator 提供了在 Android 和 iOS 设备上,在堆栈中的路由之间导航时你指望的手势和动画。php
yarn add react-navigation
# or with npm
# npm install --save react-navigation
复制代码
npm install --save react-native-gesture-handler
react-native link react-native-gesture-handler
复制代码
npm install
复制代码
而后把依赖引入到Android项目中:html
第一步:setting.gradle
文件中添加java
include ':react-native-gesture-handler'
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
复制代码
第二步: 在Android/app/文件夹下的build.gradle
文件中添加node
implementation project(':react-native-gesture-handler')
复制代码
第三步: 修改Applicationreact
第四步: 修改Activityandroid
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
/** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */
@Override
protected String getMainComponentName() {
return "demo";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
};
}
}
复制代码
reactSrc
用来存放react-native代码。下面新建pages
文件夹专门存放页面文件。pages/home/home.js ↓ios
import React from 'react'
import { Text, Button, View, TouchableNativeFeedback } from 'react-native'
export default class Home extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<TouchableNativeFeedback >
<Button
title="Go to Details"
onPress={() => this.props.navigation.navigate('Details')}
/></TouchableNativeFeedback>
</View>
)
}
}
复制代码
pages/detail/detail.js ↓web
import React from 'react'
import { StyleSheet, Text } from 'react-native'
export default class Detail extends React.Component {
render() {
return (
<Text>Detail</Text>
)
}
}
const style = StyleSheet.create({
webview: {
flex: 1
}
})
复制代码
而后修改App.js文件 ↓npm
import React, { Component } from 'react';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import HomeScreen from './reactSrc/pages/home/home'
import DetailsScreen from './reactSrc/pages/detail/detail'
const AppNavigator = createStackNavigator({
Home: {
screen: HomeScreen,
},
Details: {
screen: DetailsScreen,
},
}, {
initialRouteName: 'Home',
});
const AppContainer = createAppContainer(AppNavigator);
export default class App extends React.Component {
render() {
return <AppContainer />; } } 复制代码
若是我再Detail页面再调用this.props.navigation.navigate('Details')
会发生什么? 答案是什么都不会发生。这是由于咱们已经在 Details 路由上了。 导航功能粗略地意味着“去这个页面”,若是你已经在那个页面上,那么意味着它不会作任何事情。 若是你想再次进入Detail页面。请用Push
方法。编程
每次调用 push
时, 咱们会向导航堆栈中添加新路由。 当你调用 navigate
时, 它首先尝试查找具备该名称的现有路由, 而且只有在堆栈上没有一个新路由时才会推送该路由。
若是当前页面能够执行返回操做,则 stack navigator 会自动提供一个包含返回按钮的标题栏(若是导航堆栈中只有一个页面,则没有任何可返回的内容,所以也不存在返回键)。 有时候你但愿可以以编程的方式触发此行为,可使用this.props.navigation.goBack()
将上面两个页面的代码稍加修改:
pages/home/home.js ↓
import React from 'react'
import { Text, Button, View, TouchableNativeFeedback } from 'react-native'
export default class Home extends React.Component {
render() {
const itemId = this.props.navigation.getParam('itemId', 'NO-ID');
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
<TouchableNativeFeedback >
<Button
title={itemId}
onPress={() => this.props.navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
})}
/></TouchableNativeFeedback>
</View>
)
}
}
复制代码
pages/detail/detail.js ↓
import React from 'react'
import { View, Text, Button } from 'react-native'
export default class Detail extends React.Component {
render() {
/* 2. Get the param, provide a fallback value if not available */
const { navigation } = this.props;
const itemId = navigation.getParam('itemId', 'NO-ID');
const otherParam = navigation.getParam('otherParam', 'some default value');
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Text>itemId: {JSON.stringify(itemId)}</Text>
<Text>otherParam: {JSON.stringify(otherParam)}</Text>
<Button
title="Go to Details... again"
onPress={() =>
this.props.navigation.push('Details', {
itemId: Math.floor(Math.random() * 100),
})}
/>
<Button
title="Go to Home"
onPress={() => this.props.navigation.navigate('Home', {
itemId: "Back From Detail:" + navigation.getParam('itemId', 'NO-ID')
})}
/>
<Button
title="Go back"
onPress={() => this.props.navigation.goBack()}
/>
</View>
);
}
}
复制代码
一开始进入Home页面,路由里面没有‘itemId’对应的值,因此按钮上显示NO-ID
。 点击这个按钮进入到Detail页面,Detail页面上显示从Home页面传过来的数据。
点击‘Go to Details... again’ 会新刷新数据。 点击‘Go to Home’会返回Home页,并传回数据。
调用此方法可跳转到应用程序中的另外一个页面.若是已存在,将后退到此路由
关闭当前页面并返回上一个页面
推一个新的路由到堆栈
返回堆栈中的上一个页面
跳转到堆栈中最顶层的页面
一次渲染一个页面,并支持页面切换, 当咱们打开一个新的页面时,该页面会被置于堆栈的顶层。
默认状况下,stack navigator 被配置为具备熟悉的iOS和Android外观 & 感受:新屏幕从iOS右侧滑入,从Android底部淡入。 在iOS上,stack navigator 也能够配置为屏幕从底部滑入的模式样式。
在一个页面和另外一个页面之间进行切换,在屏幕上没有 UI,在页面变为非活动状态时卸载页面。
DrawerNavigator提供从左侧滑入的抽屉。
TabNavigator标签栏,可以让您在不一样路由之间进行切换。 路由被懒加载 - 它们的屏幕组件只有在第一次获取焦点时才会被加载。
一个modal就像一个弹出窗口 — 它不是主要导航流程的一部分 — 它一般有一个不一样的转换,一个不一样的关闭方式,而且打算专一于一个特定的内容或交互。
class HomeScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
const params = navigation.state.params || {};
return {
headerLeft: (
<Button
onPress={() => navigation.navigate('MyModal')}
title="Info"
color="#fff"
/>
),
/* the rest of this config is unchanged */
};
};
/* render function, etc */
}
class ModalScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30 }}>This is a modal!</Text>
<Button
onPress={() => this.props.navigation.goBack()}
title="Dismiss"
/>
</View>
);
}
}
const MainStack = createStackNavigator(
{
Home: {
screen: HomeScreen,
},
Details: {
screen: DetailsScreen,
},
},
{
/* Same configuration as before */
}
);
const RootStack = createStackNavigator(
{
Main: {
screen: MainStack,
},
MyModal: {
screen: ModalScreen,
},
},
{
mode: 'modal',
headerMode: 'none',
}
);
复制代码
说明:咱们将一个 stack navigator 嵌套到另外一个stack navigator 中。mode配置能够是card(默认)或modal。 在 iOS 上,modal表现为从页面底部划入,并容许用户从页面顶部向下缩小以关闭它。 modal配置对Android没有影响。当咱们调用navigate方法时,咱们不须要指定除咱们想要导航的路由以外的任何东西。 没有必要限定它属于哪一个堆栈, React Navigation 尝试在最近的导航器上查找路线,而后在那里执行操做。将页面从HomeScreen切换到MainStack 。咱们知道MainStack没法处理路由MyModal,所以它会将其传递到能够处理该路由的RootStack,从而实现页面的跳转。
withNavigation是一个高阶组件,它能够将 navigation 这个 prop 传递到一个包装的组件。 当你没法直接将 navigation 这个 prop 传递给组件,或者不想在深度嵌套的子组件中传递它时,它将很是有用。注:通常状况下navigation只会在navigation启动的组件中做为prop传递。
import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />; } } // withNavigation returns a component that wraps MyBackButton and passes in the // navigation prop export default withNavigation(MyBackButton); 复制代码
处理外部 URI,经过一个URI打开一个React-Native页面。
新建一个页面 pages/person/person.js ↓
import React from 'react'
import { View, Text, Button } from 'react-native'
export default class Person extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Person Screen {this.props.navigation.getParam('user', 'NO-USER')}</Text> </View>
);
}
}
复制代码
App.js
添加person
页面的路由, person
页面使用独立的栈 ↓
import React, { Component } from 'react';
import { createStackNavigator, createAppContainer, createSwitchNavigator } from 'react-navigation';
import HomeScreen from './reactSrc/pages/home/home'
import DetailsScreen from './reactSrc/pages/detail/detail'
import PersonScreen from './reactSrc/pages/person/person'
const HomeNavigator = createStackNavigator({
Home: {
screen: HomeScreen,
},
Details: {
screen: DetailsScreen,
},
});
const MainNavigator = createSwitchNavigator({
HomeNavigator: {
screen: HomeNavigator,
},
PersonNavigator: {
screen: PersonScreen,
path: 'person/:user',
},
});
const AppContainer = createAppContainer(MainNavigator);
export default class App extends React.Component {
render() {
return <AppContainer uriPrefix='reactdemo://demoproject/' />; } } 复制代码
Android清单文件AndroidManifest.xml中给ReactNative的宿祖Activity加上intent-filter
,
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.demo">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MainApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme">
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="demoproject"
android:scheme="reactdemo"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>
复制代码
我新建了一个安卓原生界面Main2Activity
,上面有两个按钮分别演示两种跳转方法(这里使用了安卓的anko库): Main2Activity.kt ↓
package com.demo
import android.content.Intent
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main2.*
import org.jetbrains.anko.startActivity
class Main2Activity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
button2.setOnClickListener {
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.data = Uri.parse("reactdemo://demoproject/person/:John")
startActivity(intent)
}
button1.setOnClickListener {
startActivity<MainActivity>()
}
}
}
复制代码
activity_main2.xml ↓
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Main2Activity">
<Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:text="跳ReactNativeActivity" app:layout_constraintBottom_toTopOf="@+id/button2" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />
<Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="URI跳" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
复制代码
在android/app/build.gradle文件写了注释:
/** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * and bundleReleaseJsAndAssets). * These basically call `react-native bundle` with the correct arguments during the Android build * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the * bundle directly from the development server. Below you can see all the possible configurations * and their defaults. If you decide to add a configuration block, make sure to add it before the * `apply from: "../../node_modules/react-native/react.gradle"` line. * * project.ext.react = [ * // the name of the generated asset file containing your JS bundle * bundleAssetName: "index.android.bundle", * * // the entry file for bundle generation * entryFile: "index.android.js", * * // whether to bundle JS and assets in debug mode * bundleInDebug: false, * * // whether to bundle JS and assets in release mode * bundleInRelease: true, * * // whether to bundle JS and assets in another build variant (if configured). * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants * // The configuration property can be in the following formats * // 'bundleIn${productFlavor}${buildType}' * // 'bundleIn${buildType}' * // bundleInFreeDebug: true, * // bundleInPaidRelease: true, * // bundleInBeta: true, * * // whether to disable dev mode in custom build variants (by default only disabled in release) * // for example: to disable dev mode in the staging build type (if configured) * devDisabledInStaging: true, * // The configuration property can be in the following formats * // 'devDisabledIn${productFlavor}${buildType}' * // 'devDisabledIn${buildType}' * * // the root of your project, i.e. where "package.json" lives * root: "../../", * * // where to put the JS bundle asset in debug mode * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", * * // where to put the JS bundle asset in release mode * jsBundleDirRelease: "$buildDir/intermediates/assets/release", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in debug mode * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in release mode * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", * * // by default the gradle tasks are skipped if none of the JS files or assets change; this means * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to * // date; if you have any other folders that you want to ignore for performance reasons (gradle * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ * // for example, you might want to remove it from here. * inputExcludes: ["android/**", "ios/**"], * * // override which node gets called and with what additional arguments * nodeExecutableAndArgs: ["node"], * * // supply additional arguments to the packager * extraPackagerArgs: [] * ] */
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
……
复制代码
react.gradle
文件被引入,为每一个编译版本都注册了任务,而后下面是提供了一些可自定义的参数。固然它们都有默认值。默认在Debug模式下是不打bundle包的,在Release模式下才会打。而且官方推荐在Debug模式下从 development server 直接加载bundle包。
默认参数下会在"$buildDir/intermediates/assets/debug"下生成名为index.android.bundle
的文件
可是网上都是推荐把这个文件生成到app/src/main/assets/文件夹下面:
首先建立一个assets文件夹 而后在React-Native项目的根目录下执行:
react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/
复制代码
而后文件就会自动生成了,而后直接打包就能够了。