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<ExpandableListView> 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<AppUpdate> mUpdates = new ArrayList<AppUpdate>();

    public List<AppUpdate> mIgnores = new ArrayList<AppUpdate>();

    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<JSONObject>() {
            @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<JSONObject>() {
                    @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<String> ignoreSet = pref.getAll().keySet();
                                List<AppUpdate> update = new ArrayList<AppUpdate>();
                                List<AppUpdate> ignore = new ArrayList<AppUpdate>();
                                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<String> ignoreSet = pref.getAll().keySet();
                List<AppUpdate> update = new ArrayList<AppUpdate>();
                List<AppUpdate> ignore = new ArrayList<AppUpdate>();
                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<AppUpdate> 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<AppUpdate> 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<ExpandableListView>,实现protected ExpandableListView createViewInstance(ThemedReactContext reactContext)与getName(),这两个方法会自动调用。
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<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.<ViewManager>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 }) => (
      <Image
        source={require('./android/img/notif-icon.png')}
        style={[styles.icon, {tintColor: tintColor}]}
      />
    ),
  };


  render() {
    height=height-300;
    return (
        <View>
            <Text>Alert测试</Text>
            <AlertCustom style={{flex:1,}}/>
            <Text>ExpandableListView测试</Text>
            <ExpandableListView style={{width:width,height:height,alignItems:"flex-end"}} layoutWidth={900} layoutHeight={900} />
        </View>
    );
  }
}

最后由于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



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值