React Native(以下简称 RN)开发 app 过程中大部分都可以在 JS 端完成, 但是也有一些功能是需要原生端来完成的. 这时 RN 与原生端就不可避免的需要进行交互, 比如页面跳转和数据传递. 本篇文章主要以 RN-- 原生, 原生 --RN-- 原生为例来讲解如何进行基本的页面跳转操作.
关于页面跳转, 我写了三个 demo. 这里发下地址:
- https://github.com/mrarronz/react-native-blog-examples/tree/master/Chapter10-RNInteractionWithNative/RNAddNative
- https://github.com/mrarronz/react-native-blog-examples/tree/master/Chapter10-RNInteractionWithNative/RNPushToNative
- https://github.com/mrarronz/react-native-blog-examples/tree/master/Chapter10-RNInteractionWithNative/NativeJumpToRN
RNAddNative 和 RNPushToNative 差不多, 都是 RN 页面跳转到原生的实例, 区别是一个 RNAddNative 使用了
react-native-navigation
,RNPushToNative 使用了 react-navigation. 在试过两种导航库之后, 我更倾向于使用
react-native-navigation
, 谁用谁知道.
NativeJumpToRN 是原生页面跳转到 RN, 再从 RN 跳转到原生的示例.
在实际开发中, 我认为 RN 页面 (JS 写的) 和原生页面 (iOS 和 Android 写的) 之间最好还是做到分离开来, 通用页面还是用 RN 实现, 各自需要定制化的, 功能复杂的页面还是原生写. 尽量不要搞 RN 和原生组件内嵌, 毕竟写 UI 组件的话是相对比较简单的事, 没必要在原生页面内嵌一个 RN 页面作为组件, 或者 RN 页面内嵌一个原生组件. 将两者分离开, 有需要就直接页面间进行跳转, 这样也方便处理.
RN--Native
如果你还没有创建原生项目, 那直接 react-native init 创建一个 RN 项目, android 和 iOS 的工程会自动给你创建好. android 和 iOS 的工程都是可以拿来独立开发的, 不会受 RN 的影响. 如果已经有原生项目了, 请自行搜索如何集成 react native 到现有原生项目, 为了精简篇幅这里不多做赘述.
以 https://github.com/mrarronz/react-native-blog-examples/tree/master/Chapter10-RNInteractionWithNative/RNPushToNative 为例先来说明一下如何从 RN 跳转到原生页面. 从 RN 跳转到原生, 其实是在原生端创建 Module 类通过桥接的方式导出到 JS 端供 JS 代码调用原生端代码来实现的.
Android
Android 分三步:
1. 定义 Module 类, 继承
ReactContextBaseJavaModule
在 Module 类中, 重写 getName 方法声明 Module 类名称, 创建我们自己的方法用来做页面跳转.
2. 定义 Package 类, 实现接口 ReactPackage
Package 类需要实现
createNativeModules
和 createViewManagers 两个方法. 在
createNativeModules
中初始化 Module 类并添加到集合
3. 定义 Application 类, 继承 android 的 Application, 并实现 ReactApplication 接口.
在 getPackages 方法中初始化 Package 类, 并添加到集合.
RN 项目创建的时候 Application 类就有了, 不需要再创建. 如果是集成到现有原生项目, 就需要手动修改 Application 类.
Module 类核心代码:
- public class OpenNativeModule extends ReactContextBaseJavaModule {
- private ReactContext mReactContext;
- public OpenNativeModule(ReactApplicationContext context) {
- super(context);
- this.mReactContext = context;
- }
- @Override
- public String getName() {
- return "OpenNativeModule";
- }
- @ReactMethod
- public void openNativeVC() {
- Intent intent = new Intent();
- intent.setClass(mReactContext, SettingsActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mReactContext.startActivity(intent);
- }
- }
Package 类核心代码:
- public class TestReactPackage implements ReactPackage {
- @Override
- public List<NativeModule> createNativeModules(
- ReactApplicationContext reactContext) {
- List<NativeModule> modules = new ArrayList<>();
- modules.add(new OpenNativeModule(reactContext));
- return modules;
- }
- @Override
- public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
- return Collections.emptyList();
- }
- }
Application 类核心代码:
- @Override
- protected List<ReactPackage> getPackages() {
- return Arrays.<ReactPackage>asList(
- new MainReactPackage(),
- new VectorIconsPackage(),
- new TestReactPackage()
- );
- }
- iOS
iOS 端比 Android 就简单多了, 只需要创建一个 Module 类, 实现 RCTBridgeModule 协议就行了.
OpenNativeModule.h 代码:
- #import <Foundation/Foundation.h>
- #import <React/RCTBridgeModule.h>
- @interface OpenNativeModule : NSObject<RCTBridgeModule>
- @end
OpenNativeModule.m 代码:
- #import "OpenNativeModule.h"
- #import "AppDelegate.h"
- #import "NativeViewController.h"
- @implementation OpenNativeModule
- RCT_EXPORT_MODULE();
- RCT_EXPORT_METHOD(openNativeVC) {
- dispatch_async(dispatch_get_main_queue(), ^{
- AppDelegate *delegate = (AppDelegate *)([UIApplication sharedApplication].delegate);
- UINavigationController *rootNav = delegate.navController;
- NativeViewController *nativeVC = [[NativeViewController alloc] init];
- [rootNav pushViewController:nativeVC animated:YES];
- });
- }
- @end
最后在 RN 端调用:
- import React from 'react';
- import {View, Text, Button, NativeModules} from 'react-native';
- var nativeModule = NativeModules.OpenNativeModule;
- export default class HomeScreen extends React.Component {
- render() {
- return (
- <View>
- <Text > 首页</Text>
- <Button title={'跳转到原生页面'} onPress={() => {
- this.jumpToNativeView();
- }}/>
- </View>
- )
- }
- jumpToNativeView() {
- nativeModule.openNativeVC();
- }
- }
- Native--RN--Native
原生跳转到 RN 再由 RN 跳转到原生页面, 这个示例可以参考 https://github.com/mrarronz/react-native-blog-examples/tree/master/Chapter10-RNInteractionWithNative/NativeJumpToRN .RN 到原生的跳转与上述原理相同, 不同的是原生到 RN 的跳转. 这个 demo 中我只是将原生项目启动后的初始页面由 RN 页面改为了原生页面, 而原本的 RN 页面我将它内嵌在一个原生页面当中了.
对于 Android 来说, 原本的启动页面是 MainActivity, MainActivity 就对应着 RN 页面. 我创建了一个 LaunchActivity 作为启动页面, 在
AndroidManifest.xml
中将其设置为启动页. 在 LaunchActivity 中添加一个 button, 设置点击跳转到 MainActivity 就完成了原生页面到 RN 的跳转, 是不是很简单?
- public class LaunchActivity extends AppCompatActivity {
- private Button button;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_launch);
- button = (Button) findViewById(R.id.button);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(LaunchActivity.this, MainActivity.class);
- startActivity(intent);
- }
- });
- }
- }
iOS 端也一样, RN 页面的初始化是在 AppDelegate 中完成的, 这里我们将 window 的 rootViewController 改为原生的 UIViewController. AppDelegate 核心代码:
- @implementation AppDelegate
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {
- self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
- _navController = [[UINavigationController alloc] initWithRootViewController:[[HomeViewController alloc] init]];
- self.window.rootViewController = _navController;
- [self.window makeKeyAndVisible];
- return YES;
- }
- @end
在 HomeViewController 中创建一个 button, 点击 button 跳转到 RNViewController.
- @implementation HomeViewController
- - (void)viewDidLoad {
- [super viewDidLoad];
- self.title = @"这是 iOS 原生页面";
- self.view.backgroundColor = [UIColor brownColor];
- UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
- [button setTitle:@"点击跳转到 RN 页面" forState:UIControlStateNormal];
- [button addTarget:self action:@selector(onClickButton) forControlEvents:UIControlEventTouchUpInside];
- [self.view addSubview:button];
- }
- - (void)onClickButton {
- RNViewController *vc = [[RNViewController alloc] init];
- [self.navigationController pushViewController:vc animated:YES];
- }
- @end
AppDelegate 中 RN 页面的初始化操作放到 RNViewController 中来:
- @implementation RNViewController
- - (void)viewDidLoad {
- [super viewDidLoad];
- self.title = @"这是 RN 页面";
- NSURL *jsCodeLocation;
- jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
- RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
- moduleName:@"NativeJumpToRN"
- initialProperties:nil
- launchOptions:nil];
- rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
- self.view = rootView;
- }
这样就完成了原生到 RN 的跳转.
来源: https://juejin.im/post/5b20810ff265da6e432e697c