【React Native】文件翻譯閱讀紀錄 - 指南(Android) - 原生 UI 組件

by - 上午9:00




Facebook Open Source React Native

原生 UI 組件
有大量的本機UI小部件可以在最新的應用程序中使用 - 其中一些是平台的一部分,另一些可用作第三方庫,還有更多可能在您自己的產品組合中使用。 React Native已經包含了幾個最關鍵的平台組件,比如ScrollView和TextInput,但不是全部,而且肯定不是你自己為之前的應用編寫的。幸運的是,將這些現有組件包裝起來以便與React Native應用程序無縫集成非常容易。

與本機模塊指南一樣,這也是一個更高級的指南,假設您對Android SDK編程有些熟悉。本指南將向您展示如何構建本機UI組件,引導您完成核心React Native庫中可用的現有ImageView組件的子集的實現。

ImageView 示例

對於此示例,我們將介紹實現要求,以允許在JavaScript中使用ImageView。

通過擴展ViewManager或更常見的SimpleViewManager來創建和操作本機視圖。在這種情況下,SimpleViewManager很方便,因為它應用了常見屬性,例如背景顏色,不透明度和Flexbox佈局。


這些子類本質上是單例 - 每個只有一個實例由橋創建。它們向NativeViewHierarchyManager提供本機視圖,該視圖委託給它們以根據需要設置和更新視圖的屬性。 ViewManagers通常也是視圖的代理,通過網橋將事件發送回JavaScript。
Vending 視圖很簡單:
  1. 創建ViewManager子類。
  2. 實現 createViewInstance 方法
  3. 使用@ReactProp(或@ReactPropGroup)註釋公開視圖屬性設置器
  4. 在應用程序包的 createViewManagers 中註冊管理器。
  5. 實現JavaScript模塊

1. 創建 ViewManager 子類

在這個例子中,我們創建了視圖管理器類ReactImageManager,它擴展了ReactImageView類型的SimpleViewManager。 ReactImageView是管理器管理的對像類型,它將是自定義本機視圖。 getName返回的名稱用於從JavaScript引用本機視圖類型。
...

public class ReactImageManager extends SimpleViewManager<ReactImageView> {

  public static final String REACT_CLASS = "RCTImageView";

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

2. 實現方法 createViewInstance

視圖在 createViewInstance 方法中創建,視圖應在其默認狀態下初始化,任何屬性都將通過對 updateView 的後續調用來設置。
  @Override
  public ReactImageView createViewInstance(ThemedReactContext context) {
    return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), mCallerContext);
  }

3. 使用@ReactProp(或@ReactPropGroup)註釋公開視圖屬性設置器

要在JavaScript中反映的屬性需要作為使用@ReactProp(或@ReactPropGroup)註釋的setter方法公開。 Setter方法應該將視圖更新(當前視圖類型)作為第一個參數,將屬性值更新為第二個參數。應該將Setter聲明為void方法,並且應該是公共的。發送給JS的屬性類型是根據setter的value參數的類型自動確定的。目前支持以下類型的值:boolean,int,float,double,String,Boolean,Integer,ReadableArray,ReadableMap。

註釋@ReactProp有一個String類型的強制參數名稱。分配給鏈接到setter方法的@ReactProp註釋的名稱用於引用JS端的屬性。

除了名稱,@ ReactProp註釋可能採用以下可選參數:defaultBoolean,defaultInt,defaultFloat。這些參數應該是相應的基本類型(相應的是boolean,int,float),並且如果setter引用的屬性已從組件中刪除,則提供的值將傳遞給setter方法。請注意,“默認”值僅為基本類型提供,如果setter是某種複雜類型,則在相應屬性被刪除時,null將作為默認值提供。

使用@ReactPropGroup註釋的方法的setter聲明要求與@ReactProp不同,請參閱@ReactPropGroup註釋類docs以獲取有關它的更多信息。
重要! 在 ReactJS 中更新屬性值將導致 setter 方法調用。請注意,我們可以更新組件的方法之一是刪除之前設置的屬性。在這種情況下,也會調用setter方法來通知視圖管理器屬性已更改。在這種情況下,將提供“默認”值(對於基本類型,“default”可以使用 defaultBoolean,defaultFloat 等指定 @ReactProp 註釋的參數,對於復雜類型,將調用 setter 並將值設置為 null)。
  @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註冊到應用程序,這通過應用程序包成員函數createViewManagers以類似於Native Modules的方式進行。
  @Override
  public List<ViewManager> createViewManagers(
                            ReactApplicationContext reactContext) {
    return Arrays.<ViewManager>asList(
      new ReactImageManager()
    );
  }

5. 實現 JavaScript 模塊

最後一步是創建JavaScript模塊,為新視圖的用戶定義Java和JavaScript之間的接口層。大部分工作都是由Java和JavaScript中的內部React代碼處理的,剩下的就是描述propTypes。
// 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);
requireNativeComponent 通常有兩個參數,第一個是本機視圖的名稱,第二個是描述組件接口的對象。組件接口應聲明用於調試消息的友好名稱,並且必須聲明由Native View反映的propTypes。 propTypes用於檢查用戶使用本機視圖的有效性。請注意,如果您需要JavaScript組件執行的不僅僅是指定名稱和propTypes,比如執行自定義事件處理,則可以將本機組件包裝在普通的反應組件中。在這種情況下,您希望將包裝器組件而不是iface傳遞給requireNativeComponent。這在下面的MyCustomView示例中說明。

活動

所以現在我們知道如何公開我們可以從JS輕鬆控制的本機視圖組件,但是我們如何處理來自用戶的事件,比如捏縮放或平移?當本機事件發生時,本機代碼應該向 View 的 JavaScript 表示發出一個事件,並且這兩個視圖與 getId() 方法返回的值鏈接。
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);
    }
}
要將 topChange 事件名稱映射到JavaScript中的 onChange 回調 prop,請通過覆蓋 ViewManager 中的 getExportedCustomBubblingEventTypeConstants 方法來註冊它:
public class ReactImageManager extends SimpleViewManager<MyCustomView> {
    ...
    public Map getExportedCustomBubblingEventTypeConstants() {
        return MapBuilder.builder()
            .put(
                "topChange",
                MapBuilder.of(
                    "phasedRegistrationNames",
                    MapBuilder.of("bubbled", "onChange")))
                    .build();
    }
}
使用raw事件調用此回調,我們通常在包裝器組件中處理該事件以生成更簡單的API:
// 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。 有時您將需要為本機組件公開一些特殊屬性,但實際上並不希望它們作為相關React組件的API的一部分。 例如,Switch為原始本機事件提供了一個自定義 onChange 處理程序,並公開了一個 onValueChange 處理程序屬性,該屬性僅使用布爾值而不是原始事件調用(類似於上例中的onChangeMessage)。 由於您不希望這些僅本機屬性成為API的一部分,因此您不希望將它們放在propTypes 中,但如果不這樣做,則會出現錯誤。 解決方案只是通過 nativeOnly 選項調用它們。



You May Also Like

0 意見