React Native與原生互動之跳轉及傳參
React Native(簡稱RN)開發的app大部分都可以在JS端完成,但是也有一些複雜的功能是需要原生端來完成的,或者是在原生專案中整合RN,此時RN與原生端就不可避免的需要進行互動,比如頁面跳轉和資料傳遞。
關於RN與原生更深層次的呼叫原理,及如何自己封裝RN的原生元件,可以參考《React Native移動開發實戰》(ps:白皮版)。原生跳轉RN是非常簡單的,本文不做講解,本文主要分析其他的運用場景。
RN跳轉原生頁面
使用“react-native init”命令建立一個RN專案,新建立的專案包含了android和iOS的原生專案,如果有原生的程式碼可以在原生專案中編寫,RN的程式碼則單獨放到相應的JS檔案中。從RN跳轉到原生,其實是在原生端建立Module類通過橋接的方式匯出到JS端供JS程式碼呼叫原生端程式碼來實現的。
跳轉到Android原生
對於Android平臺來說,要完成RN跳轉到Android原生的功能,主要會涉及以下幾個步驟:
1. 定義Module類,該類繼承ReactContextBaseJavaModule
在Module類中,重寫getName方法宣告Module類名稱,建立相關方法用來做頁面跳轉。
public class OpenNativeModule extends ReactContextBaseJavaModule {
private ReactContext mReactContext;
public OpenNativeModule(ReactApplicationContext context) {
super (context);
this.mReactContext = context;
}
@Override
public String getName() {
return "OpenNativeModule";
}
//開啟登入設定介面,用於給RN呼叫
@ReactMethod
public void openNativeVC() {
Intent intent = new Intent();
intent.setClass(mReactContext, SettingsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mReactContext.startActivity(intent);
}
}
2. 定義Package類,實現介面ReactPackage。
Package類需要實現createNativeModules和createViewManagers兩個方法,在createNativeModules中初始化Module類並新增到集合。
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();
}
}
3. 定義Application類,繼承android的Application,並實現ReactApplication介面。
每一個自定義的Package都需要新增到Application註冊。在getPackages方法中初始化Package類,並新增到集合。
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new OpenNativePackage()
);
}
跳轉到iOS原生
相比Android,iOS的實現就相對簡單得多,只需要建立一個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中跳轉的時候通過NativeModules.OpenNativeModule物件,然後就可以跳轉了。例如:
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>
<Button title={'跳轉到原生頁面'} onPress={() => {
this.jumpToNativeView();
}}/>
</View>
)
}
jumpToNativeView() {
nativeModule.openNativeVC();
}
}
Native跳轉到RN
Android
對於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
iOS端也一樣,RN頁面的初始化是在AppDelegate中完成的,這裡我們將window的rootViewController改為原生的UIViewController。
@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];
}
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的跳轉就算完成了。