ReactNative(嵌入到android)调用android原生组件与原生模块(比如某个方法)
官网的例子以ReactNative项目来说明,没有关于ReactNative嵌入到android是如何调用android原生组件的例子,没办法只能参考ReactNative源代码来自己调用。本文以调用android组件ExpandableListView为例。
1、创建调用原生组件模块ReactExpandableListViewManager。
package com.example.test.widget.reactnative.expandableListView;import android.app.Activity;import android.content.Context;import android.content.SharedPreferences;import android.graphics.Color;import android.widget.ExpandableListView;import com.example.test.adapter.ExpandableListAdapter;import com.example.test.bean.AppUpdate;import com.facebook.react.module.annotations.ReactModule;import com.facebook.react.uimanager.SimpleViewManager;import com.facebook.react.uimanager.ThemedReactContext;import com.facebook.react.uimanager.annotations.ReactProp;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.List;import java.util.Set;import rx.Observable;import rx.Observer;import rx.Subscriber;import rx.android.schedulers.AndroidSchedulers;import rx.schedulers.Schedulers;/** * Created by Springever on 2017/5/18. */@ReactModule(name = ReactExpandableListViewManager.REACT_CLASS)public class ReactExpandableListViewManager extends SimpleViewManager implements ExpandableListAdapter.Callback { public static final String REACT_CLASS = "RCTExpandableListView";//和ReactNative的js组件名字一致 private ExpandableListView expandableListView; private ExpandableListAdapter mUpdateAdapter; private Activity activity; private static final String NAME_ENTITIES = "entities"; private static String PREF_IGNORE = "ignore"; private static final String JSON_UPAPPITEMS = "upappitems"; private static final String JSON_IGNOREAPPITEMS = "ignoreappitems"; public List mUpdates = new ArrayList(); public List mIgnores = new ArrayList(); public ReactExpandableListViewManager(){ } @Override public String getName() { return REACT_CLASS; } @Override protected ExpandableListView createViewInstance(ThemedReactContext reactContext) { expandableListView =new ExpandableListView(reactContext); mUpdateAdapter = new ExpandableListAdapter(reactContext); mUpdateAdapter.registerCallback(this);//注册回调函数 expandableListView.setAdapter(mUpdateAdapter); expandableListView.setCacheColorHint(Color.TRANSPARENT);//点击时候不会变黑 expandableListView.setGroupIndicator(null);//去掉左边图标 expandableListView.expandGroup(ExpandableListAdapter.GROUP_UPDATE);//触发展开 expandableListView.expandGroup(ExpandableListAdapter.GROUP_IGNORE);//触发展开 activity = reactContext.getCurrentActivity(); showData(); return expandableListView; } @ReactProp(name = "layoutWidth") public void setLayoutWidth(ExpandableListView view, int layoutWidth) { } @ReactProp(name = "layoutHeight") public void setLayoutHeight(ExpandableListView view, int layoutHeight) { } public void showData() { final Thread t = Thread.currentThread(); Observable.create(new Observable.OnSubscribe() { @Override public void call(Subscriber<? super JSONObject> subscriber) { byte[] bytes = readFromAsset(activity, "preload/update.json"); JSONObject jsonObj = null; if (bytes != null) { try { jsonObj = new JSONObject(new String(bytes)); } catch (JSONException e) { e.printStackTrace(); } } subscriber.onNext(jsonObj); subscriber.onCompleted(); } }) .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程 .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程 .subscribe(new Observer() { @Override public void onNext(JSONObject jsonObj) { JSONObject entities = jsonObj.optJSONObject(NAME_ENTITIES); if (entities != null) { try { readFromJSON(entities); //上次忽略更新的应用 SharedPreferences pref = activity.getSharedPreferences(PREF_IGNORE, 0); Set ignoreSet = pref.getAll().keySet(); List update = new ArrayList(); List ignore = new ArrayList(); if (mUpdates != null) { for (AppUpdate au : mUpdates) { //比较本地应用 //int status = getXXX(au.mPackageName, au.mVersionCode, au.mVersion); //if (status != STATUS_INSTALLED_OLD_VERSION) // continue; if (ignoreSet.contains(au.mPackageName)) { ignore.add(au); } else { update.add(au); } } } mUpdateAdapter.setData(update, ignore); } catch (JSONException e) { e.printStackTrace(); } } } @Override public void onCompleted() { } @Override public void onError(Throwable e) { } }); /* JSONObject jsonObj = null; try { byte[] bytes = readFromAsset(this, "preload/update.json"); if (bytes != null) { jsonObj = new JSONObject(new String(bytes)); } else { } JSONObject entities = jsonObj.optJSONObject(NAME_ENTITIES); if (entities != null) { readFromJSON(entities); //上次忽略更新的应用 SharedPreferences pref = getSharedPreferences(PREF_IGNORE, 0); Set ignoreSet = pref.getAll().keySet(); List update = new ArrayList(); List ignore = new ArrayList(); if (mUpdates != null) { for (AppUpdate au : mUpdates) { //比较本地应用 //int status = getXXX(au.mPackageName, au.mVersionCode, au.mVersion); //if (status != STATUS_INSTALLED_OLD_VERSION) // continue; if (ignoreSet.contains(au.mPackageName)) { ignore.add(au); } else { update.add(au); } } } mUpdateAdapter.setData(update, ignore); } } catch (Exception e) { } */ } public static byte[] readFromAsset(Context context, String fileName) { byte[] ret = null; InputStream instream = null; try { instream = context.getAssets().open(fileName); byte[] buffer = new byte[8192]; ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len = -1; while ((len = instream.read(buffer)) >= 0) baos.write(buffer, 0, len); baos.flush(); ret = baos.toByteArray(); baos.close(); } catch (IOException e) { } finally { try { if (instream != null) instream.close(); } catch (IOException e) { } } return ret; } public void readFromJSON(JSONObject jsonObj) throws JSONException { mUpdates.clear(); Object upAppItemObj = jsonObj.opt(JSON_UPAPPITEMS); if (upAppItemObj != null) { // 兼容两种更新接口数据 if (upAppItemObj instanceof JSONArray) { parseUpdateArrayData(mUpdates, (JSONArray) upAppItemObj); } else if (upAppItemObj instanceof JSONObject) { int objCount = ((JSONObject) upAppItemObj).length(); parseUpdateObjData(mUpdates, (JSONObject) upAppItemObj, objCount); } else { // Can't resolve upappitems, do nothing. } } mIgnores.clear(); Object ignoreAppItemObj = jsonObj.opt(JSON_IGNOREAPPITEMS); if (ignoreAppItemObj != null) { if (ignoreAppItemObj instanceof JSONArray) { parseUpdateArrayData(mIgnores, (JSONArray) ignoreAppItemObj); } else if (ignoreAppItemObj instanceof JSONObject) { int objCount = ((JSONObject) ignoreAppItemObj).length(); parseUpdateObjData(mIgnores, (JSONObject) ignoreAppItemObj, objCount); } else { // Can't resolve ignoreappitems, do nothing. } } } private void parseUpdateObjData(List outList, JSONObject jsonObj, int objCount) { int length = objCount; if (jsonObj == null || objCount <= 0) return; for (int pos = 0; pos < length; ++pos) { JSONObject updateObj = jsonObj.optJSONObject(String.valueOf(pos)); if (updateObj == null) continue; try { AppUpdate update = new AppUpdate(); update.readFromJSON(updateObj); outList.add(update); } catch (JSONException e) { continue; } } } private void parseUpdateArrayData(List outList, JSONArray jsonObj) { JSONArray updateArray = jsonObj; int length = 0; if (updateArray != null && (length = updateArray.length()) > 0) { for (int pos = 0; pos < length; ++pos) { JSONObject updateObj = updateArray.optJSONObject(pos); if (updateObj == null) continue; try { AppUpdate update = new AppUpdate(); update.readFromJSON(updateObj); outList.add(update); } catch (JSONException e) { continue; } } } } public JSONObject generateJSONObject() throws JSONException { JSONObject ret = new JSONObject(); JSONArray array = new JSONArray(); for (AppUpdate update : mUpdates) { if (update == null) continue; JSONObject updateObj = update.generateJSONObject(); array.put(updateObj); } ret.put(JSON_UPAPPITEMS, array); array = new JSONArray(); for (AppUpdate update : mIgnores) { if (update == null) continue; JSONObject updateObj = update.generateJSONObject(); array.put(updateObj); } ret.put(JSON_IGNOREAPPITEMS, array); return ret; } @Override public void onUpdate(ExpandableListAdapter.UpdateInfoHolder updateInfo) { } @Override public void onIgnore(AppUpdate item) { } @Override public void onRemoveIgnore(AppUpdate item) { }}
核心东西是继承SimpleViewManager
REACT_CLASS可以说是组件对外发布的名称(ReactNative的js通过这个名字可以找到这个组件)。
@ReactProp(name = "layoutWidth") public void setLayoutWidth(ExpandableListView view, int layoutWidth) { }
这个方法是ReactNative的js会传递layoutWidth属性过来(而且是数字类型),这是固定写法。 2、将ViewManager类注册到ReactPackage
package com.example.test.widget.reactnative.expandableListView;import com.facebook.react.ReactPackage;import com.facebook.react.bridge.JavaScriptModule;import com.facebook.react.bridge.NativeModule;import com.facebook.react.bridge.ReactApplicationContext;import com.facebook.react.uimanager.ViewManager;import java.util.Arrays;import java.util.Collections;import java.util.List;/** * Created by Springever on 2017/5/18. */public class ExpandableReactPackage implements ReactPackage { @Override public List createNativeModules(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List> createJSModules() { return Collections.emptyList(); } @Override public List createViewManagers(ReactApplicationContext reactContext) { return Arrays.asList( new ReactExpandableListViewManager() ); }}
3、将ReactPackage添加到application中(在ReactNative的启动Activity中添加)
addPackage(new ExpandableReactPackage())
package com.example.test.activity;import android.app.Activity;import android.content.Intent;import android.net.Uri;import android.os.Build;import android.os.Bundle;import android.provider.Settings;import android.view.KeyEvent;import com.example.test.BuildConfig;import com.example.test.widget.reactnative.expandableListView.ExpandableReactPackage;import com.facebook.react.ReactInstanceManager;import com.facebook.react.ReactRootView;import com.facebook.react.common.LifecycleState;import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;import com.facebook.react.shell.MainReactPackage;/** * Created by Springever on 2017/5/2. */public class ReactNativeActivity extends Activity implements DefaultHardwareBackBtnHandler { private ReactRootView mReactRootView; private ReactInstanceManager mReactInstanceManager; private final static int OVERLAY_PERMISSION_REQ_CODE=1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); } } mReactRootView = new ReactRootView(this); mReactInstanceManager = ReactInstanceManager.builder() .setApplication(getApplication()) .setBundleAssetName("index.android.bundle") .setJSMainModuleName("index.android") .addPackage(new MainReactPackage()) .addPackage(new ExpandableReactPackage()) .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED) .build(); // 注意这里的HelloWorld必须对应“index.android.js”中的 // “AppRegistry.registerComponent()”的第一个参数 mReactRootView.startReactApplication(mReactInstanceManager, "ReactNativeActivity", null); setContentView(mReactRootView); } @Override public void invokeDefaultOnBackPressed() { super.onBackPressed(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == OVERLAY_PERMISSION_REQ_CODE) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { // SYSTEM_ALERT_WINDOW permission not granted... } } } } @Override public void onBackPressed() { if (mReactInstanceManager != null) { mReactInstanceManager.onBackPressed(); } else { super.onBackPressed(); } } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) { mReactInstanceManager.showDevOptionsDialog(); return true; } return super.onKeyUp(keyCode, event); } @Override protected void onPause() { super.onPause(); if (mReactInstanceManager != null) { mReactInstanceManager.onHostPause(this); } } @Override protected void onResume() { super.onResume(); if (mReactInstanceManager != null) { mReactInstanceManager.onHostResume(this, this); } } @Override protected void onDestroy() { super.onDestroy(); if (mReactInstanceManager != null) { mReactInstanceManager.onHostDestroy(); } }}
4、编写ReactNative的ExpandableListView.js
RCTExpandableListView与ViewManager类中的REACT_CLASS一致'use strict';import { PropTypes } from 'react';import { requireNativeComponent, View } from 'react-native';var iFace = { name: 'ExpandableTextView', /* propTypes: { src: PropTypes.string, borderRadius: PropTypes.number, resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']), ...View.propTypes // 包含默认的View的属性 }, */ propTypes: { layoutWidth: PropTypes.number, layoutHeight: PropTypes.number, ...View.propTypes },};module.exports = requireNativeComponent('RCTExpandableListView', iFace);
5、ReactNative的index.anroid.js调用组件
Dimensions获得屏幕大小
var ExpandableListView = require('./android/lib/ExpandableListView');
const {width, height} = Dimensions.get('window');class SubScreen extends React.Component { static navigationOptions = { tabBarLabel: 'SubScreen', tabBarIcon: ({ tintColor }) => ( ), }; render() { height=height-300; return ( Alert测试 ExpandableListView测试 ); }}
最后由于ReactNative的自定义的顶层容器改写了requestLayout(),导致重写等不能上传到顶层容器RootViewImpl,这样的后果是导致类似ListView的setData、notifyDataSetChanged方法失效,解决办法是在自定组件中重写requestLayout方法,先调用父类requestLayout,而后手动触发measure计算方法、layout布局方法。代码如下:
public class ReactExpandableListView extends ExpandableListView { public ReactExpandableListView(Context context) { super(context); } public ReactExpandableListView(Context context, AttributeSet attrs) { super(context, attrs); } public ReactExpandableListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public ReactExpandableListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } private final Runnable measureAndLayout = new Runnable() { @Override public void run() { measure( MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); layout(getLeft(), getTop(), getRight(), getBottom()); } }; @Override public void requestLayout() { super.requestLayout(); // The spinner relies on a measure + layout pass happening after it calls requestLayout(). // Without this, the widget never actually changes the selection and doesn't call the // appropriate listeners. Since we override onLayout in our ViewGroups, a layout pass never // happens after a call to requestLayout, so we simulate one here. post(measureAndLayout); }}
这样,我们需要用ReactExpandableListView替代ExpandableListView。 最后ReactNative的js如何调用原生模块(比如某个方法),可以参考官网http://reactnative.cn/docs/0.44/native-modules-android.html#content ,这次官网写的是对的。
具体代码可以参考git:https://github.com/Springever/Test
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用
- python list.sort()根据多个关键字排序的方法实现
- Android(安卓)AsyncTask
- Android(安卓)content provider基础与使用
- Android下Activity全屏显示实现方法
- Android获取mac地址方法,6.0以上也可用
- 利用HTML5开发Android
- android MultiDex multiDex原理(一)