react-native可以做web與原生的交互,這是使用react-native開發項目的主要目的之一,也是主要優勢,用rn而不用原生交互則毫無價值,這篇文章用來記錄在項目中rn的原生交互使用過程。
之前說過要做的是一個pda項目,所以今天以input獲取焦點的時候禁止軟鍵盤彈出為例,大體說一下rn的原生交互過程。
android的原生交互分為以下幾步
- 編寫原生代碼
- 向js暴露原生接口
- 注冊原生模塊
- 導出並再rn導入原生,模塊
1、編寫原生模塊
作為web工程師出身的我,對原生android代碼是不太了解的,充其量也只是稍微了解點java語言,但是通過自己的努力,還是過來了(笑哭);根據需求,就是再剛一進入頁面的時候,讓第一個input獲取焦點,並同時隱藏軟鍵盤,前端代碼很好寫,就是在獲取焦點之后調用隱藏軟鍵盤的原生功能;搞清楚了需求之后就開始編寫原生代碼了。
如上圖,在newpda目錄下面新建BoardModule類文件,用來編寫原生功能代碼;這個類繼承自 ReactContextBaseJavaModule,代碼如下
package com.newpda; import android.util.Log; import android.widget.Toast; import android.widget.EditText; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import android.content.Context; import android.app.Activity; import android.app.ActivityManager; import android.view.inputmethod.InputMethodManager; import com.facebook.infer.annotation.Assertions; import javax.annotation.Nullable; /** * Description: Created by song on 2018/7/3. email:gaosongai@foxmail.com */ public class BoardModule extends ReactContextBaseJavaModule { private final ReactApplicationContext reactContext; public BoardModule(ReactApplicationContext reactContext) { super(reactContext); this.reactContext = reactContext; // 獲取上下文 } @Override public String getName() { return "BoardModule"; } /** * 關閉Edittext軟件盤,光標依然正常顯示。 */ @ReactMethod public void hideboard() { Activity currentActivity = getCurrentActivity(); InputMethodManager mInputMethodManager = (InputMethodManager) Assertions.assertNotNull(this.reactContext.getSystemService(Context.INPUT_METHOD_SERVICE)); mInputMethodManager.hideSoftInputFromWindow(currentActivity.getCurrentFocus().getWindowToken(), 0); } }
hideboard為隱藏軟鍵盤的方法,並向js暴露hideboard方法,要導出一個方法給JavaScript使用,Java方法需要使用注解@ReactMethod
2、注冊模塊
然后注冊原生模塊,同級目錄下新建CustomBoardPackage類,代碼如下
package com.newpda; import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Description: * Created by song on 2018/9/6. * email:gaosongai@foxmail.com */ public class CustomBoardPackage implements ReactPackage { @Override public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { List<NativeModule> modules=new ArrayList<>(); modules.add(new BoardModule(reactContext)); return modules; } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } }
我們需要在應用的Package類的createNativeModules方法中添加這個模塊。如果模塊沒有被注冊,它也無法在JavaScript中被訪問到。
然后這個package需要在MainApplication.java文件的getPackages方法中提供,文件在同級目錄下,代碼如下
package com.newpda; import android.app.Application; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; import java.util.Arrays; import java.util.List; public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } @Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new CustomBoardPackage() // 剛剛添加的方法 ); } @Override protected String getJSMainModuleName() { return "index"; } }; @Override public ReactNativeHost getReactNativeHost() { return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); SoLoader.init(this, /* native exopackage */ false); } }
3、在rn中引入模塊
為了讓你的功能從JavaScript端訪問起來更為方便,通常我們都會把原生模塊封裝成一個JavaScript模塊。這不是必須的,但省下了每次都從NativeModules中獲取對應模塊的步驟。這個JS文件也可以用於添加一些其他JavaScript端實現的功能,App.js同級目錄下新建androidtoast.js,內容如下
import {NativeModules} from 'react-native'; module.exports = NativeModules.BoardModule;
然后在組件內部使用
import BoardModule from "../../../androidtoast"; BoardModule.hideboard(); // 使用原生模塊方法