2017-04-11 19:02:00 weixin_34277853 阅读数 8

http://www.csdn.net/article/2015-11-27/2826345-compare-React-Native-with-ExMobi

原生组件通信原理

React Native通过JavaScript编写APP的方式,乍看以为是以webview提供的现成的JS与原生语言之间的互调,但是如果当我们调试一个React Native程序的时候,在debug视图中是不会看到任何webview被调用的痕迹。所以,实际上React Native并没有使用现成的与webview的通信方法,而是使用了更直接的JS运行环境,比如在iOS中为系统自带的JavaScriptCore。这与Phonegap这类以webview为主的界面展现与本地能力调用的模式有本质上的区别,也是确保React Native高性能和高效率的基础。

有了这个核心基础,我们再来看看React Native是如何通过JS来挂钩到原生UI和本地能力的。

 
   

从上图很容易可以看到,开发者通过JS去调用一个React Native提供的方法,实际需要先经过两个桥接封装类,一个JS的桥接,另一个是原生的桥接。两个桥接类之间就是通过前面提到的JS运行环境来通信。JS桥接类的作用是将开发者的调用行为加入到React Native的模块调用队列,同时生成一个回调的ID。Native桥接类的作用是将队列里的调用行为取出来根据模块找到对应的原生UI或者本地能力的函数来执行,并将执行的结果通过回调的ID逐步传递到开发者的JS回调函数中。也就是经过这两个桥接类的相互作用,建立起了JS函数与原生能力的调用序列。

2018-04-02 16:10:00 u011272795 阅读数 1035

React Native已经封装了大部分最常见的组件,譬如ScrollView和TextInput,但不可能封装全部组件。而且,说不定你曾经为自己以前的App还封装过一些组件,React Native肯定没法包含它们。幸运的是,在React Naitve应用程序中封装和植入已有的组件非常简单。

原生视图需要被一个ViewManager的派生类(或者更常见的,SimpleViewManager的派生类)创建和管理。一个SimpleViewManager可以用于这个场景,是因为它能够包含更多公共的属性,譬如背景颜色、透明度、Flexbox布局等等。

这些子类本质上都是单例——React Native只会为每个管理器创建一个实例。它们创建原生的视图并提供给NativeViewHierarchyManager,NativeViewHierarchyManager则会反过来委托它们在需要的时候去设置和更新视图的属性。ViewManager还会代理视图的所有委托,并给JavaScript发回对应的事件。

提供原生视图很简单:

创建一个ViewManager的子类。
实现createViewInstance方法。
导出视图的属性设置器:使用@ReactProp(或@ReactPropGroup)注解。
把这个视图管理类注册到应用程序包的createViewManagers里。
实现JavaScript模块。

这里,我们以WebView举例 来实现封装原生UI组件的功能.

1. 创建ViewManager的子类

在这个例子里我们创建一个视图管理类ReactWebViewManager,它继承自SimpleViewManager。ReactImageView是这个视图管理类所管理的对象类型,这应当是一个自定义的原生视图。getName方法返回的名字会用于在JavaScript端引用这个原生视图类型。

...
public class ReactWebViewManager extends SimpleViewManager<ReactImageView> {

    @Override
    public String getName() {
        return "AndroidRCTWebView";
    }

2. 实现方法createViewInstance

视图在createViewInstance中创建,且应当把自己初始化为默认的状态。所有属性的设置都通过后续的updateView来进行。

  @Override
    protected WebView createViewInstance(ThemedReactContext reactContext) {
        WebView webView = new WebView(reactContext);
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
        return webView;
    }

3. 通过@ReactProp(或@ReactPropGroup)注解来导出属性的设置方法。

要导出给JavaScript使用的属性,需要申明带有@ReactProp(或@ReactPropGroup)注解的设置方法。方法的第一个参数是要修改属性的视图实例,第二个参数是要设置的属性值。方法的返回值类型必须为void,而且访问控制必须被声明为public。JavaScript所得知的属性类型会由该方法第二个参数的类型来自动决定。支持的类型有:boolean, int, float, double, String, Boolean, Integer, ReadableArray, ReadableMap。

@ReactProp注解必须包含一个字符串类型的参数name。这个参数指定了对应属性在JavaScript端的名字。

除了name,@ReactProp注解还接受这些可选的参数:defaultBoolean, defaultInt, defaultFloat。这些参数必须是对应的基础类型的值(也就是boolean, int, float),这些值会被传递给setter方法,以免JavaScript端某些情况下在组件中移除了对应的属性。注意这个”默认”值只对基本类型生效,对于其他的类型而言,当对应的属性删除时,null会作为默认值提供给方法。

使用@ReactPropGroup来注解的设置方法和@ReactProp不同。请参见@ReactPropGroup注解类源代码中的文档来获取更多详情。

重要! 在ReactJS里,修改一个属性会引发一次对设置方法的调用。有一种修改情况是,移除掉之前设置的属性。在这种情况下设置方法也一样会被调用,并且“默认”值会被作为参数提供(对于基础类型来说可以通过defaultBoolean、defaultFloat等@ReactProp的属性提供,而对于复杂类型来说参数则会设置为null)

 @ReactProp(name = "url")
    public void setUrl(WebView view, @Nullable String url) {
        Log.e("TAG", "setUrl");
        view.loadUrl(url);
    }

    @ReactProp(name = "html")
    public void setHtml(WebView view, @Nullable String html) {
        Log.e("TAG", "setHtml");
        view.loadData(html, "text/html; charset=utf-8", "UTF-8");
    }  

至此 , 我们的ReactWebViewManager类的内容就算写完了,下面是源码:

package com.githubdemo.reactManager;

import android.support.annotation.Nullable;
import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;


import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;

/**
 * Created by dongfanggouwu-zy on 2018/4/2.
 */
public class ReactWebViewManager extends SimpleViewManager<WebView> {
    @Override
    public String getName() {
        return "AndroidRCTWebView";
    }

    @Override
    protected WebView createViewInstance(ThemedReactContext reactContext) {
        WebView webView = new WebView(reactContext);
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
        return webView;
    }


    @ReactProp(name = "url")
    public void setUrl(WebView view, @Nullable String url) {
        Log.e("TAG", "setUrl");
        view.loadUrl(url);
    }

    @ReactProp(name = "html")
    public void setHtml(WebView view, @Nullable String html) {
        Log.e("TAG", "setHtml");
        view.loadData(html, "text/html; charset=utf-8", "UTF-8");
    }
}

4. 注册ViewManager

在Java中的最后一步就是把视图控制器注册到应用中。这和原生模块的注册方法类似,唯一的区别是我们把它放到createViewManagers方法的返回值里。

package com.githubdemo.reactManager;

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.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Created by dongfanggouwu-zy on 2018/4/2.
 */

public class AppReactPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.<ViewManager>asList(
                new ReactWebViewManager()
        );
    }


}

当然不要忘了 这个package需要在MainApplication.java文件的getPackages方法中提供:



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 AppReactPackage()   //这个是我们自己要加的
            );
        }

        @Override
        protected String getJSMainModuleName() {
            return "index";
        }


    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
    }
}


这里有一个需要注意的地方ReactWebViewManager 是我们自己写的,所以导入包的时候 不要导错了 ,如果你自己起了别的名字 可能会跟android内部的一些类名方法相同,需要特别注意.

5. 实现对应的JavaScript模块

整个过程的最后一步就是创建JavaScript模块并且定义Java和JavaScript之间的接口层。大部分过程都由React底层的Java和JavaScript代码来完成,你所需要做的就是通过propTypes来描述属性的类型。

// WebView.js
/**
 * Created by 卓原 on 2018/4/2.
 * zhuoyuan93@gmail.com
 */
import {
    requireNativeComponent,
    View,

} from 'react-native';
import PropTypes from 'prop-types';

let iface = {
    name: 'WebView',
    propTypes: {
        url: PropTypes.string,
        html: PropTypes.string,
        ...View.propTypes // 包含默认的View的属性
    }
};

module.exports = requireNativeComponent('AndroidRCTWebView', iface);

requireNativeComponent通常接受两个参数,第一个参数是原生视图的名字,而第二个参数是一个描述组件接口的对象。组件接口应当声明一个友好的name,用来在调试信息中显示;组件接口还必须声明propTypes字段,用来对应到原生视图上。这个propTypes还可以用来检查用户使用View的方式是否正确。

注意,如果你还需要一个JavaScript组件来做一些除了指定name和propTypes以外的事情,譬如事件处理,你可以把原生组件用一个普通React组件封装。在这种情况下,requireNativeComponent的第二个参数变为用于封装的组件。这个在后文的例子里面用到。

译注:和原生模块不同,原生视图的前缀RCT不会被自动去掉。

正式使用:

/**
 * Created by 卓原 on 2018/3/1.
 * zhuoyuan93@gmail.com
 */

import React from 'react';
import {
    View
} from 'react-native';

import WebView from '../common/WebView';

export default class FavoritePage extends React.Component {

    render() {
        return (
            <View>
                <WebView
                    url="https://www.baidu.com"
                    style={{width: 200, height: 400}}/>

            </View>
        )
    }
}

现在 你封装的原生组件就已经可以在RN上面使用了.
但是,这还只是最基础的将原始UI组件显示出来,如果你想再RN端进行一些事件处理的话,还要一些操作,而更为常见的却是事件,比如我们需要在javascript层处理这个WebView的滚动事件,这时候又要怎么做呢。

事件

现在我们已经知道了怎么导出一个原生视图组件,并且我们可以在JS里很方便的控制它了。不过我们怎么才能处理来自用户的事件,譬如缩放操作或者拖动?当一个原生事件发生的时候,它应该也能触发JavaScript端视图上的事件,这两个视图会依据getId()而关联在一起。

这时候我们就需要继承WebView,重写对应的事件,然后将事件传递给javascript层了

package com.githubdemo.rctViews;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.webkit.WebView;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;

/**
 * Created by dongfanggouwu-zy on 2018/4/2.
 */

public class RCTWebView extends WebView {
    public RCTWebView(Context context) {
        super(context);
    }

    public RCTWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RCTWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        Log.e("TAG", "onScrollChanged");
        WritableMap event = Arguments.createMap();
        event.putInt("ScrollX", l);
        event.putInt("ScrollY", t);
        ReactContext reactContext = (ReactContext) getContext();
        reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                getId(), "topChange", event);
    }
}

我们重写了滚动时回调的onScrollChanged方法,构造了一个WritableMap 对象,将ScrollX和ScrollY传入,然后调用reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(getId(), “topChange”, event);将事件发生到javascript层,注意topChange对应着javascript层的onChange方法,这个映射关系在UIManagerModuleConstants类中。

然后我们需要修改ReactWebViewManager 中的createViewInstance方法,在里面返回我们实现的子类,就像这样子

...
public class ReactWebViewManager extends SimpleViewManager<WebView> {
  ...
    @Override
    protected WebView createViewInstance(ThemedReactContext reactContext) {
        WebView webView = new RCTWebView(reactContext);  //这里就改成我们重写的WebView
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
        return webView;
    }
...
}

而javascript层也需要进行一定程度的改造,以前的WebView.js已经无法满足我们的需求了.最终的代码如下,

/**
 * Created by 卓原 on 2018/4/2.
 * zhuoyuan93@gmail.com
 */
'use strict';
import React from 'react';
import {
    requireNativeComponent,
    View
} from 'react-native';
import PropTypes from 'prop-types';

var RCTWebView = requireNativeComponent('AndroidRCTWebView', WebView, {
    nativeOnly: {onChange: true}
});

export default class WebView extends React.Component {

    static propTypes = {
        url: PropTypes.string,
        html: PropTypes.string,
        onScrollChange: PropTypes.func,
        ...View.propTypes // 包含默认的View的属性
    };

    constructor(props) {
        super(props);
    }

    _onChange(event: Event) {
        if (!this.props.onScrollChange) {
            return;
        }
        this.props.onScrollChange({ScrollX: event.nativeEvent.ScrollX, ScrollY: event.nativeEvent.ScrollY});
    }

    render() {
        return (
            <RCTWebView {...this.props} onChange={(event) => this._onChange(event)}/>
        )
    }
}

/*
let iface = {
    name: 'WebView',
    propTypes: {
        url: PropTypes.string,
        html: PropTypes.string,
        ...View.propTypes // 包含默认的View的属性
    }
};

module.exports = requireNativeComponent('AndroidRCTWebView', iface);*/

在onChange函数中,我们进行判断,如果属性onScrollChange没有设置或者为false,就直接return,否则就调用设置的onScrollChange属性值(该值是一个函数类型),将Java层传入的两个参数传到该函数中去,{ScrollX:event.nativeEvent.ScrollX,ScrollY:event.nativeEvent.ScrollY}

然后我们来进行调用:

export default class FavoritePage extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            a: 22
        }
    }

    render() {
        return (
            <View>
                <WebView
                    onScrollChange={this.onWebViewScroll}
                    url={"https://www.baidu.com"}
                    style={{width: 200, height: 200}}/>

                <Text onPress={() => this.setState(preState => {
                    a: preState.a++
                }, console.log(this.state.a))}>aaa</Text>
            </View>
        )
    }

    onWebViewScroll(event) {
        console.log(event);
    }
}

之后我们在WebView上滑动 就会在后台打印数来数据了~

2019-12-10 09:04:03 weixin_43526443 阅读数 6

ReactNative要善用原生组件
比如
在这里插入图片描述这个button按钮效果
我们可以使用原生组件ActivityIndicator

import { ActivityIndicator} from 'react-native';

<Button block style={styles.loginButton} onPress={this.login.bind(this)} activeOpacity={1}>
                {
                    this.state.loginProgress
                    ? <ActivityIndicator color="white"/>
                    : <Text style={Theme.lightBold}>{i18n.t('login')}</Text>
                }
            </Button>

一般登陆还会有个问题
就是键盘会把界面往上挤,这是我们可以调整原生组件KeyboardAvoidingView的behavior属性

import {  KeyboardAvoidingView } from 'react-native';
render() {
    return (
        <KeyboardAvoidingView behavior="padding" style={styles.container}>
            {this.renderLogo()}
            {this.renderForm()}
        </KeyboardAvoidingView>
    );
}
2018-05-29 17:35:39 qq_20369621 阅读数 548

android工程:

ReactWebViewManager.class(新建)

public class ReactWebViewManager extends SimpleViewManager<WebView> {

    public static final String REACT_CLASS = "RCTWebView";

    @Override
    public String getName() {
        return REACT_CLASS;
    }

    @Override
    protected WebView createViewInstance(ThemedReactContext reactContext) {
        WebView webView = new WebView(reactContext);
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
        return webView;
    }

    @ReactProp(name = "url")
    public void setUrl(WebView view,@Nullable String url) {
        Log.e("TAG", "setUrl");
        view.loadUrl(url);
    }
    @ReactProp(name = "html")
    public void setHtml(WebView view,@Nullable String html) {
        Log.e("TAG", "setHtml");
        view.loadData(html, "text/html; charset=utf-8", "UTF-8");
    }
}

AnExampleReactPackage.class(新建)

public class AnExampleReactPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();

        modules.add(new ToastModule(reactContext));

        return modules;
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.<ViewManager>asList(
                new ReactImageManager(),
                new ReactWebViewManager()
        );
    }
}

MainApplication.class添加新建的模块

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 AnExampleReactPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
  }
}

js部分

WebView.js(新建)...View.propTypes这个一定要写,不然新版本会报错,吧默认属性也给进去

import React,{PropTypes} from 'react';
import {requireNativeComponent,View} from 'react-native';

//新版本必须加...View.propTypes
var iface = {
    name: 'WebView',
    propTypes: {
        url: PropTypes.string,
        html: PropTypes.string,
        ...View.propTypes
    },
}

module.exports = requireNativeComponent('RCTWebView', iface,{
    nativeOnly:{
        "testID": true,
        'renderToHardwareTextureAndroid': true,
        'accessibilityComponentType': true,
        'accessibilityLabel': true,
        'importantForAccessibility': true,
        'accessibilityLiveRegion': true,
        'onLayout':true,
    }
});

调用:

var WebView = require('../AndroidNativeModules/WebView');

<WebView  url="https://www.baidu.com" style={{width:200,height:400}}></WebView>

2017-06-05 15:04:10 mwp123555 阅读数 1674

官网的例子以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



没有更多推荐了,返回首页