React Native 中调用原生 android 模块 Toast 例子及说明
KatW65
8年前
<h2><strong>react-native-android-toast</strong></h2> <p>react-native 调用原生 android 模块 Toast 学习笔记</p> <h2><strong>参考官方文档初始化一个react-native项目</strong></h2> <pre> <code class="language-java">react-native init androidToast</code></pre> <p>生成如下目录:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/541be29691322e49f77515ee6902ab07.jpg"></p> <h2><strong>运行命令查看项目</strong></h2> <pre> <code class="language-java">react-native run-android</code></pre> <h3><strong>如图:</strong></h3> <p style="text-align: center;"><img src="https://simg.open-open.com/show/b86508b5e310f87dafa06ad81079d723.jpg"></p> <h2><strong>接入Android原生模块</strong></h2> <p>按照官方的说法,第一步需要创建一个java类 本例中为:ToastModule ,并继承 ReactContextBaseJavaModule ,然后复写 getName() 方法,其返回值,就是在 react-native 中引用的 组件名称</p> <p>复写 getConstants() 方法可以返回一些 常量 用于react-native中调用,官方文档中 return "ToastAndroid" 这个名称在原生的组件中已经存在,返回相同的名称将会冲突,so:改个名字吧!!</p> <p>@ReactMethod 注解:用于java返回一个 react-native 中可调用的 方法 ,其不能有返回值所以使用 void</p> <p>注册模块:创建java类 本例中为:ExampleReactPackage ,实现 ReactPackage 接口</p> <p>复写createJSModules() , createViewManagers() 方法,返回 Collections.emptyList() 空集合</p> <p>createNativeModules() 方法中添加我们需注册的模块对象, new ToastModule() ,并返回模块集合</p> <p>添加已注册模块对象到返回集合中,向react-native抛出模块,如:第三步</p> <p>在react-native中调用,如:第四步</p> <h3><strong>android目录结构</strong></h3> <p style="text-align:center"><img src="https://simg.open-open.com/show/ab6d8392cb37a56f1acd156a8ac968fe.jpg"></p> <h3><strong>注意:引入包的名称不要弄错了</strong></h3> <h3><strong>Java & ReactNative 基本类型对照</strong></h3> <table> <thead> <tr> <th>Java</th> <th>RN</th> </tr> </thead> <tbody> <tr> <td>Boolean</td> <td>Bool</td> </tr> <tr> <td>Integer</td> <td>Number</td> </tr> <tr> <td>Double</td> <td>Number</td> </tr> <tr> <td>Float</td> <td>Number</td> </tr> <tr> <td>String</td> <td>String</td> </tr> <tr> <td>Callback</td> <td>function</td> </tr> <tr> <td>ReadableMap</td> <td>Object</td> </tr> <tr> <td>ReadableArray</td> <td>Array</td> </tr> </tbody> </table> <h3><strong>第一步:创建模块类</strong></h3> <p>在androidtoast目录下,创建一个ToastModule.java的类</p> <pre> <code class="language-java">package com.androidtoast; //包名 import android.widget.Toast; //引入调用的类 import com.非死book.react.bridge.ReactApplicationContext; import com.非死book.react.bridge.ReactContextBaseJavaModule; import com.非死book.react.bridge.ReactMethod; import com.非死book.react.uimanager.IllegalViewOperationException; import java.util.Map; import java.util.HashMap; public class ToastModule extends ReactContextBaseJavaModule { private static final String DURATION_SHORT_KEY = "SHORT"; private static final String DURATION_LONG_KEY = "LONG"; public ToastModule(ReactApplicationContext reactContext) { super(reactContext); } // 复写方法,返回react-native中调用的 组件名 @Override public String getName() { return "ToastNative"; } // 复写方法,返回常量 @Override public Map<String, Object> getConstants() { final Map<String, Object> constants = new HashMap<>(); constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT); constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG); return constants; } // 使用 @ReactMethod注解返回react-native中可调用的 方法 @ReactMethod public void show(String message, int duration) { Toast.makeText(getReactApplicationContext(), message, duration).show(); } }</code></pre> <h3><strong>第二步:注册模块</strong></h3> <p>在androidtoast目录下,创建一个ExampleReactPackage.java的类</p> <pre> <code class="language-java">package com.androidtoast; import android.widget.Toast; import com.非死book.react.bridge.NativeModule; import com.非死book.react.bridge.ReactApplicationContext; import com.非死book.react.bridge.ReactMethod; import com.非死book.react.ReactPackage; import com.非死book.react.bridge.JavaScriptModule; import com.非死book.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ExampleReactPackage implements ReactPackage { @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new ToastModule(reactContext)); return modules; } }</code></pre> <h3><strong>第三步:添加注册类</strong></h3> <p>添加到 MainApplication.java 中的 getPackages() 方法中</p> <pre> <code class="language-java">@Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), // 这个是自动创建 new ExampleReactPackage() // 这个类是我们创建的 ); }</code></pre> <p>项目结构如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/844f4a2e618e9bedae73100d32bd5a8c.jpg"></p> <p>Java部分的代码就结束了,再次提醒下:包名啊!!不要弄错了!!!</p> <h3><strong>第四步:修改react-native代码引入原生模块</strong></h3> <p>修改index.android.js</p> <ul> <li>引入react-native所需模块 NativeModules</li> <li>获取导出组件 NativeModules.ToastNative</li> <li>调用方法 show()</li> </ul> <p>修改了下index.android.js文件,代码如下:</p> <pre> <code class="language-java">/** * Sample React Native App * https://github.com/非死book/react-native * @flow */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Text, View, TouchableOpacity, NativeModules } from 'react-native'; let toast = NativeModules.ToastNative; export default class androidToast extends Component { render() { return ( <View style={styles.container}> <Text style={styles.title}>react-native 调用android原生模块</Text> <TouchableOpacity onPress={()=>{ toast.show('Toast message',toast.SHORT); }}> <Text style={styles.btn}>Click Me</Text> </TouchableOpacity> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF', }, title:{ fontSize:16, }, btn:{ fontSize:16, paddingVertical:7, paddingHorizontal:10, borderColor:'#f00', borderWidth:1, borderRadius:5, marginTop:10, color:'#f00' } }); AppRegistry.registerComponent('androidToast', () => androidToast);</code></pre> <h2><strong>运行程序</strong></h2> <pre> <code class="language-java">react-native run-android</code></pre> <h3><strong>效果如下:</strong></h3> <p><img src="https://simg.open-open.com/show/93bd7d9f6f6035404ab1fb26a3300b89.jpg"></p> <h2><strong>react-native回调函数</strong></h2> <p>*java中提供了一个 Callback 的数据类型对应了react-native中的 function *</p> <p>具体操作就是在@ReactMethod注解的返回函数中 添加 类型 为 Callback 的参数,并通过 invoke(...params) 调用</p> <p>RN中通过调用show方法时提供对应的回调函数就可以了, :smile:</p> <ul> <li> <p>修改 ToastModule.java 代码中 show() 方法,添加回调</p> </li> </ul> <p>注意引包!! import com.非死book.react.bridge.Callback;</p> <pre> <code class="language-java">// 说明下:count,flag是我自定义的变量 @ReactMethod public void show(String message, int duration ,Callback successCallback, Callback errorCallback) { Toast.makeText(getReactApplicationContext(), message, duration).show(); // 通过invoke调用,随便你传参 if(flag) successCallback.invoke("success", ++count); else errorCallback.invoke("error", ++count); flag = !flag; }</code></pre> <ul> <li> <p>修改 index.android.js 中调用函数</p> </li> </ul> <pre> <code class="language-java"><TouchableOpacity onPress={()=>{ toast.show('Toast message',toast.SHORT,(message,count)=>{console.log("==",message,count)},(message,count)=>{console.log("++",message,count)}); }}></code></pre> <p>:ok_hand: ,试试看吧~~</p> <h2><strong>触发事件</strong></h2> <p>首先我们定义一个发送事件的方法</p> <pre> <code class="language-java">private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params){ reactContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit(eventName, params); }</code></pre> <p>引包</p> <pre> <code class="language-java">import javax.annotation.Nullable; import com.非死book.react.bridge.Arguments; import com.非死book.react.bridge.WritableMap; import com.非死book.react.bridge.ReactContext; import com.非死book.react.modules.core.DeviceEventManagerModule;</code></pre> <p>继续改造 show 方法,添加参数,并调用预先定义的方法</p> <pre> <code class="language-java">// 静态方法 WritableMap map = Arguments.createMap(); map.putBoolean("boolean",true); map.putDouble("double",0.003); map.putString("string","string"); sendEvent(this.reactContext, "eventName",map);</code></pre> <p>改造 index.android.js 啦 ,添加事件监听,这里的 eventName 就是我们 sendEvent 中定义的事件名称</p> <pre> <code class="language-java">componentWillMount(){ DeviceEventEmitter.addListener('eventName',(e)=>{ console.log(e) }); }</code></pre> <h3><strong>效果如下:</strong></h3> <p><img src="https://simg.open-open.com/show/f6c8aff93494946bdb4c4d8ddab7b8a3.gif"></p> <h2><strong>曾走过的路</strong></h2> <p>曾想在返回的方法中定义一个 Object 类型的变量,但pa! 报错了!!不支持滴,请查看类型对应表格</p> <pre> <code class="language-java">cloudn't find argument class : Object</code></pre> <p>参照官方文档时,各种类找不到,瞬间醉了!</p> <pre> <code class="language-java"># 百度吧,一般不管用 # stackoverflow,Google 有时可以搜到,尼玛!英文。。。 # github # react-native 源码 !!!这里面有个`ReactAndroid`的目录就是各种`Java`类啦 react-native/ReactAndroid/src/main/java/com/非死book/ https://github.com/非死book/react-native</code></pre> <p><img src="https://simg.open-open.com/show/b7f093ae418390c0e78bbef5c279e567.jpg"></p> <h2><strong>参考文档</strong></h2> <p><a href="/misc/goto?guid=4959724795430352569" rel="nofollow,noindex">江清清 ModulesDemo</a></p> <p><a href="/misc/goto?guid=4959724795519042519" rel="nofollow,noindex">react-native 中文</a></p> <p><a href="/misc/goto?guid=4959724795616073256" rel="nofollow,noindex">react-native 官方英文</a></p> <p><a href="/misc/goto?guid=4959724795700249509" rel="nofollow,noindex">RN-Resource-ipk github</a></p> <p> </p> <p>来自:https://github.com/Xing-He/react-native-android-toast/blob/master/README.md</p> <p> </p>