(五)react-native开发系列之Android原生交互
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(); // 使用原生模块方法