RN原生的安卓UI組件


https://facebook.github.io/react-native/docs/native-components-android.html

  這里有一大堆的原生組件可以用,一些是平台自帶的,另一些是第三方庫的,而更多的正在其他項目中被使用。RN包裝了大部分核心平台組件,但不是所有。幸運的是可以很簡單地完美集成現有的原生組件到RN中

ImageView案例

  這個案例的目標是實現在JS中使用imageView。RN中對安卓原生組件的創建和管理都是通過繼承ViewManager或SimpleViewManager。SimpleViewManager在這里用起來更加方便,因為它可以把應用公共的屬性如背景色、透明度和彈性布局。

  以上RN中創建出來的這些子類本質上都是單例的,每個子類的單個實例都是通過RN bridge創建的。這些子類把原生組件交給NativeViewHierarchyManager,NativeViewHierarchyManager再通知這些原生組件去設置和更新自己的屬性。而ViewManager則讓這些原生組件通過RN bridge把事件傳遞回給JS。

1.創建ViewManager子類。

public class ReactImageManager extends SimpleViewManager<ReactImageView> {

    public static final String REACT_CLASS = "RCTImageView";

    // JS通過這里返回的name值來獲取當前子類
    @Override
    public String getName() {
        return REACT_CLASS;
    }

    // 視圖都是在這個方法中創建的
    @Override
    protected ReactImageView createViewInstance(ThemedReactContext reactContext) {
        return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), mCallerContext);
    }
}

3.通過ReactProps或ReactPropsGroup注解來暴露視圖屬性的setter

  setter方法的第一個參數應該是要更新的視圖,第二個參數是屬性值。要求是public void。

  ReactProps注解擁有一個必填的字符串name屬性。這個name值用於在JS端引用視圖的屬性。除了name之外,還有其他可選的defaultBoolean、defaultInt和defaultFloat。這些屬性的值會被傳遞到setter函數中(當setter引用的屬性被移除時會選用這些值)

  在RN中更新視圖的屬性會導致setter執行。一種更新組件的方式就是移除之前設置的屬性,這樣setter也會執行,而且采用defaultXX設置的默認值

  @ReactProp(name = "src")
  public void setSrc(ReactImageView view, @Nullable ReadableArray sources) {
    view.setSource(sources);
  }

  @ReactProp(name = "borderRadius", defaultFloat = 0f)
  public void setBorderRadius(ReactImageView view, float borderRadius) {
    view.setBorderRadius(borderRadius);
  }

  @ReactProp(name = ViewProps.RESIZE_MODE)
  public void setResizeMode(ReactImageView view, @Nullable String resizeMode) {
    view.setScaleType(ImageResizeMode.toScaleType(resizeMode));
  }

4.注冊viewManager

  在java中的最后一步就是把ViewManager注冊到application中。

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

  步驟:首先創建一個package(一個子類實現了ReactPackage,實現以上方法),接着在Application中重新getPackages方法,將剛剛的package實例添加到數組中返回即可。過程可以參照這里https://facebook.github.io/react-native/docs/native-modules-android.html

5.實現JS模塊

  最后創建一個模塊,模塊中為用戶定義了組件的接口

// ImageView.js

import PropTypes from 'prop-types';
import {requireNativeComponent, ViewPropTypes} from 'react-native';

var iface = {
  name: 'ImageView',
  propTypes: {
    src: PropTypes.string,
    borderRadius: PropTypes.number,
    resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']),
    ...ViewPropTypes, // include the default view properties
  },
};

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

  以上接口的name值用於debug,而propTypes用於讓RN去反射,來檢測原生組件是否有效。

事件

  就以上例子不做其他任何處理,原生組件可以處理對應的觸摸事件,如setOnClickListener等。現在需要把這個事件通知給JS,當事件發生的時候(下面那個是某事件的回調方法),開始往JS傳遞一個topChange事件

class MyCustomView extends View {
   ...
   public void onReceiveNativeEvent() {
      WritableMap event = Arguments.createMap();
      event.putString("message", "MyMessage");
      ReactContext reactContext = (ReactContext)getContext();
      reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
          getId(),
          "topChange",
          event);
    }
}

  為了建立toChange消息與JS回調函數onChange的映射,需要在ViewManager中重寫以下方法。這樣一來,當以上消息發出之后,RN原生組件上的onChange屬性回調函數就會被執行

public class ReactImageManager extends SimpleViewManager<MyCustomView> {
    ...
    public Map getExportedCustomBubblingEventTypeConstants() {
        return MapBuilder.builder()
            .put(
                "topChange",
                MapBuilder.of(
                    "phasedRegistrationNames",
                    MapBuilder.of("bubbled", "onChange")))
                    .build();
    }
}

  在RN中定義一個組件進行包裝,設置原生組件的onChange事件

// MyCustomView.js

class MyCustomView extends React.Component {
  constructor(props) {
    super(props);
    this._onChange = this._onChange.bind(this);
  }
  _onChange(event: Event) {
    if (!this.props.onChangeMessage) {
      return;
    }
    this.props.onChangeMessage(event.nativeEvent.message);
  }
  render() {
    return <RCTMyCustomView {...this.props} onChange={this._onChange} />;
  }
}
MyCustomView.propTypes = {
  /**
   * Callback that is called continuously when the user is dragging the map.
   */
  onChangeMessage: PropTypes.func,
  ...
};

var RCTMyCustomView = requireNativeComponent(`RCTMyCustomView`, MyCustomView, {
  nativeOnly: {onChange: true}
});

  文檔中對於nativeOnly的解釋我不是很懂....

實踐

  自己測試了一個引用TextView的例子,項目地址:https://github.com/947133297/native-ui-component-android-example

  坑坑坑。。。在RN使用這個原生組件時,如果不設置尺寸,則這個組件不會被渲染出來,通過android studio monitor分析如下,少了一個節點:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM