【问题标题】:Creating Custom UI component for android on React Native. How to send data to JS?在 React Native 上为 android 创建自定义 UI 组件。如何向 JS 发送数据?
【发布时间】:2016-01-12 09:20:34
【问题描述】:

我创建用户界面。它运作良好。但我不知道如何将数据从 Java 发送到 JS?在反应原生模块中,我可以使用回调并激活这个 onClick 事件。但在 UI 中我不知道。

更多关于我需要什么。 我有安卓组件。这样发给JScreateViewInstance(ThemedReactContext reactContext)

并且用户在组件内部发生了一些变化。而我在java类中看到的这些变化。当 JS 请求这些更改时,我需要将这些更改发送给 JS。

你知道如何从 UI 组件向 JS 发送数据吗?请给我一些例子。 谢谢。

【问题讨论】:

    标签: android react-native


    【解决方案1】:

    gre's answer that put me on the right track, but still left me with a lot of work to do 的基础上,我将尝试解释一些缺失的细节。

    有两种基本方法可以做到这一点:

    1. 使用现有的事件类型
    2. 创建和使用自定义事件类型

    现有事件

    正如 gre 提到的,React Native 文档在 Native UI Components section of Events 中对此进行了解释。

    他们展示了如何使用以下代码发送事件:

      WritableMap event = Arguments.createMap();
      event.putString("message", "MyMessage");
      ReactContext reactContext = (ReactContext)getContext();
      reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
          getId(), "topChange", event);
    

    有了这个解释:

    事件名称topChange映射到onChange回调属性 JavaScript(映射在UIManagerModuleConstants.java)。

    UIManagerModuleConstants.java 的实际定义如下所示:

    "topChange",
        MapBuilder.of(
            "phasedRegistrationNames",
                MapBuilder.of(
                    "bubbled", "onChange", 
                    "captured", "onChangeCapture")))
    

    即通过在 Android 代码中使用事件 topChange,您可以在 JS 中使用 onChangeonChangeCapture 来拦截它。

    您可以在其中找到许多其他已声明的事件以供使用。

    其中还声明了“直接”事件可能更有用:

    "topLayout", 
        MapBuilder.of("registrationName", "onLayout")
    

    即Android事件topLayout映射到JS事件回调onLayout

    (I do not understand the difference between the "bubbled" vs "captured" vs "direct" event types)

    要在 JS 中接收事件,请注意文档中引用了 _onChange() 的 3 个位置:

    1. 创建回调方法:_onChange(event: Event) {}

    2. 在构造函数中绑定:this._onChange = this._onChange.bind(this);

    3. 在创建自定义视图时传递它:return <RCTMyCustomView {...this.props} onChange={this._onChange} />;


    自定义事件

    自定义事件需要在使用前向系统声明,将Android事件映射到JS事件,类似于上面React Native的处理方式。

    这是通过覆盖您的ViewManager 中的以下方法之一来完成的:

    • getExportedCustomBubblingEventTypeConstants()
    • getExportedCustomDirectEventTypeConstants()

    javadoc 在显示映射应该如何工作方面非常有帮助,但我发现参考上面提到的UIManagerModuleConstants 中的 React Native 代码很有用。

    一旦在此处声明,您就可以像使用任何其他“现有”事件一样使用它。


    自定义事件实现示例

    我想将点击事件从 Android 发送到 JS,并将其命名为 onClick。我选择为此使用“直接”事件。我还选择在 Android 和 JS 中使用相同的名称 - 这不是必需的。

    需要修改3个文件:

    ViewManager 类

    此代码将 Android 事件名称“onClick”映射到 JS 函数“onClick”。

    /**
     * This method maps the sending of the "onClick" event to the JS "onClick" function.
     */
    @Nullable @Override
    public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.<String, Object>builder()
                .put("onClick",
                        MapBuilder.of("registrationName", "onClick"))
                .build();
    }
    

    查看类

    当点击视图时,这段代码会向 JS 发送一个事件。此处使用的名称是 Android 事件名称,它将映射到您在上面的 ViewManager 类中设置的任何内容。

        // trigger the onPress JS callback
        super.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                final Context context = getContext();
                if (context instanceof ReactContext) {
                    ((ReactContext) context).getJSModule(RCTEventEmitter.class)
                            .receiveEvent(getId(),
                                    "onClick", null);
                }
            }
        });
    

    instanceof 检查之所以存在,是因为此视图有时是在 React 上下文之外的本机代码中引用的。

    React Native JS 组件

    在构造函数中绑定:

      constructor(props) {
        super(props);
        this.onClick = this.onClick.bind(this);
      }
    

    声明实际的回调函数:

      onClick(event: Event) {
        // do something
      }
    

    请务必set the callback when rendering your view:

      render() {
        return <NativeView onClick={this.onClick} />;
      }
    

    这样的布局非常简单,但更详细的文档分散在网络上。

    【讨论】:

    • 注意:可以将MapBuilder.&lt;String, Object&gt;builder() .put("onClick", MapBuilder.of("registrationName", "onClick")) .build();改写为MapBuilder.of( "onClick", MapBuilder.of("registrationName", "onClick"));
    • 您好,我无法理解这一点:(如果我将您的示例与 onClick 事件一起使用,它可以工作,但是当我将 onClick (everywhere) 重命名为 onMyAction 时,它什么也不做。另外,如果我使用 onClick,我无法从 Android 发送的事件属性中读取任何数据:\ 它只是未定义。你有任何其他资源可以学习吗?是 onClick 保留字,这就是它起作用的原因吗?谢谢跨度>
    • 这个例子不太理想,因为原生事件和 JS 事件具有完全相同的名称(onClick),所以不清楚这些神奇的字符串中哪个指的是 JS 事件,哪个指的是原生事件。
    • 感谢@AndrewKoster 的建议 - 这个解释需要大量的研究和实验才能正确,它适用于我的用例。我相信社区迫切需要有关这些事件的更多信息;如果您能提出一个填补空白并显示本机与 JS 事件名称之间的差异的答案,那就太好了。我仍然收到有关此答案的电子邮件,但无法提供进一步帮助。
    • 是的,在 RN 中做任何事情都需要一些研究……文档很少,示例代码也不多。我会看看我是否可以清理我的一些代码并发布它。
    【解决方案2】:

    http://facebook.github.io/react-native/docs/native-components-android.html#events

    ^这显示了如何在 UI 组件上触发从 Java 端到 JS 的事件。

    但对于“自定义事件”(未预定义的事件,如 onLoad、onScroll 等),您还需要覆盖 getExportedCustomDirectEventTypeConstants

    这里是一个例子,为 gl-react-native 触发 onGLProgress:

    (1)定义自定义事件映射:https://github.com/ProjectSeptemberInc/gl-react-native/blob/7d6e83de5a8280d06d47234fe756aa3050e9b9a1/android/src/main/java/com/projectseptember/RNGL/GLCanvasManager.java#L115-L116

    (2) 将事件从 Java 分发到 JS:https://github.com/ProjectSeptemberInc/gl-react-native/blob/0f64a63fec2281e9d6d3641b9061b771a44fcac8/android/src/main/java/com/projectseptember/RNGL/GLCanvas.java#L839-L849

    (3) 在 JS 端,你可以给一个onGLProgress prop 回调。

    【讨论】:

    • 好的,我的视图类中有这个' public void onReceiveNativeEvent() { WritableMap event = Arguments.createMap(); event.putString("message", "myValue"); mReactContext.getJSModule(RCTEventEmitter.class).receiveEvent(1,"MOJE",event); }' 我怎么称呼它?
    • 我可以像这样获得模块回调 NativeModules.MyModule.getMyModuleVoid((varialble) =>{ console.log("my log : " + varialble) } );我需要这样的东西
    • 感谢您提供详细描述的答案!最后,我发现了为什么我的自定义事件什么都不做 =)
    • @gre 这篇文章非常有用,可以为我指明正确的方向!我仍然努力寻找一些缺失的细节,我试图在另一个答案中全面填写。
    • @gre 我试图在 RN 0.62 中重新创建您的答案,但它不起作用。我在这里发布了一个关于堆栈溢出的问题stackoverflow.com/questions/63753720/… 任何人都可以帮助我。我什至尝试使用文档中给出的 topChange 示例,但这也不起作用。
    猜你喜欢
    • 2015-12-26
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 2020-03-20
    • 2016-06-03
    • 2017-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多