【问题标题】:Calling a Android Native UI component method from React native Js code从 React 原生 Js 代码调用 Android Native UI 组件方法
【发布时间】:2016-07-15 21:53:25
【问题描述】:

我创建了一个 CustomView SignatureView.java,它扩展了 LinearLayout 以在 Android Native 中捕获签名。

并创建 SignatureCapturePackage.java 和 SignatureCaptureViewManager.java

public class SignatureCaptureMainView extends LinearLayout {

     .... 

    public void saveImage(){
               //Save image to file 
     }
}

这是包类

public class SignatureCapturePackage implements ReactPackage {
      private Activity mCurrentActivity;

      public RSSignatureCapturePackage(Activity activity) {
        mCurrentActivity = activity;
      }

      @Override
      public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Arrays.<NativeModule>asList();
      }

      @Override
      public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) {
        return Arrays.<ViewManager>asList(new SignatureCaptureViewManager(mCurrentActivity));
      }

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

这是 ViewManager 类

 public class SignatureCaptureViewManager extends      ViewGroupManager<SignatureCaptureMainView> {
    private Activity mCurrentActivity;

    public static final String PROPS_SAVE_IMAGE_FILE="saveImageFileInExtStorage";
    public static final String PROPS_VIEW_MODE = "viewMode";

    public RSSignatureCaptureViewManager(Activity activity) {
        mCurrentActivity = activity;
    }

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

    @ReactProp(name = PROPS_SAVE_IMAGE_FILE)
    public void setSaveImageFileInExtStorage(SignatureCaptureMainView view, @Nullable Boolean saveFile) {
        Log.d("React View manager setSaveFileInExtStorage:", "" + saveFile);
        if(view!=null){
            view.setSaveFileInExtStorage(saveFile);
        }
    }

    @ReactProp(name = PROPS_VIEW_MODE)
    public void setViewMode(SignatureCaptureMainView view, @Nullable String viewMode) {
        Log.d("React View manager setViewMode:", "" + viewMode);
        if(view!=null){
            view.setViewMode(viewMode);
        }
    }

    @Override
    public SignatureCaptureMainView createViewInstance(ThemedReactContext context) {
        Log.d("React"," View manager createViewInstance:");
        return new SignatureCaptureMainView(context, mCurrentActivity);
    }


  }

这是 Signature.js 包

var React = require('react-native');
  var {
    PropTypes,
    requireNativeComponent,
    View,
  } = React;

  class SignatureCapture extends React.Component {

    constructor() {
      super();
      this.onChange = this.onChange.bind(this);
    }

    onChange(event) {
      console.log("Signature  ON Change Event");
      if (!this.props.onSaveEvent) {
        return;
      }

      this.props.onSaveEvent({
        pathName: event.nativeEvent.pathName,
        encoded: event.nativeEvent.encoded,
      });
    }

    render() {
      return (
        <SignatureView {...this.props} style={{flex: 1}} onChange={this.onChange} />
      );
    }

    save(){

    }
  }

  SignatureCapture.propTypes = {
    ...View.propTypes,
    saveImageFileInExtStorage: PropTypes.bool,
    viewMode:PropTypes.string
  };

  var SignatureView = requireNativeComponent('SignatureView', SignatureCapture, {
    nativeOnly: {onChange: true}
  });

  module.exports = SignatureCapture;

我正在像这样在 ReactNative 中使用模块

<SignatureCapture
                onSaveEvent={this._onSaveEvent}
                saveImageFileInExtStorage={false}
                viewMode={"portrait"}/>

一切正常。但是只有当反应端发生一些点击事件时,我才必须保存图像。即,我必须从 reactnative js 代码中调用 SignatureCaptureMainView 的 saveImage() 方法。

我怎样才能实现它?请帮忙

【问题讨论】:

    标签: android reactjs react-native react-android


    【解决方案1】:

    根据@agent_hunt 给出的指针。

    查看blog 以获得解释

    我在 SignatureCaptureViewManager 中使用了 ui 管理器命令。发布我的解决方案

    public class SignatureCaptureViewManager extends ViewGroupManager<SignatureCaptureMainView> {
    private Activity mCurrentActivity;
    
    public static final String PROPS_SAVE_IMAGE_FILE="saveImageFileInExtStorage";
    public static final String PROPS_VIEW_MODE = "viewMode";
    
    public static final int COMMAND_SAVE_IMAGE = 1;
    
    
    public SignatureCaptureViewManager(Activity activity) {
        mCurrentActivity = activity;
    }
    
    @Override
    public String getName() {
        return "SignatureView";
    }
    
    @ReactProp(name = PROPS_SAVE_IMAGE_FILE)
    public void setSaveImageFileInExtStorage(SignatureCaptureMainView view, @Nullable Boolean saveFile) {
        Log.d("React View manager setSaveFileInExtStorage:", "" + saveFile);
        if(view!=null){
            view.setSaveFileInExtStorage(saveFile);
        }
    }
    
    @ReactProp(name = PROPS_VIEW_MODE)
    public void setViewMode(SignatureCaptureMainView view, @Nullable String viewMode) {
        Log.d("React View manager setViewMode:", "" + viewMode);
        if(view!=null){
            view.setViewMode(viewMode);
        }
    }
    
    @Override
    public SignatureCaptureMainView createViewInstance(ThemedReactContext context) {
        Log.d("React"," View manager createViewInstance:");
        return new SignatureCaptureMainView(context, mCurrentActivity);
    }
    
    @Override
    public Map<String,Integer> getCommandsMap() {
        Log.d("React"," View manager getCommandsMap:");
        return MapBuilder.of(
                "saveImage",
                COMMAND_SAVE_IMAGE);
    }
    
    @Override
    public void receiveCommand(
            SignatureCaptureMainView view,
            int commandType,
            @Nullable ReadableArray args) {
        Assertions.assertNotNull(view);
        Assertions.assertNotNull(args);
        switch (commandType) {
            case COMMAND_SAVE_IMAGE: {
                view.saveImage();
                return;
            }
    
            default:
                throw new IllegalArgumentException(String.format(
                        "Unsupported command %d received by %s.",
                        commandType,
                        getClass().getSimpleName()));
        }
    }
    
    
    }
    

    为了向 ViewManager 发送命令,我在 Signature Capture 组件中添加了此方法

    class SignatureCapture extends React.Component {
    
    constructor() {
    super();
    this.onChange = this.onChange.bind(this);
    }
    
    onChange(event) {
    console.log("Signature  ON Change Event");
    if (!this.props.onSaveEvent) {
      return;
    }
    
    this.props.onSaveEvent({
      pathName: event.nativeEvent.pathName,
      encoded: event.nativeEvent.encoded,
    });
     }
    
     render() {
      return (
       <SignatureView {...this.props} style={{flex: 1}} onChange=      {this.onChange} />
    );
      }
    
    saveImage(){
     UIManager.dispatchViewManagerCommand(
            React.findNodeHandle(this),
            UIManager.SignatureView.Commands.saveImage,
            [],
        );
       }
     }
    
    SignatureCapture.propTypes = {
    ...View.propTypes,
    rotateClockwise: PropTypes.bool,
    square:PropTypes.bool,
    saveImageFileInExtStorage: PropTypes.bool,
    viewMode:PropTypes.string
    };
    
      var SignatureView = requireNativeComponent('SignatureView',   SignatureCapture, {
     nativeOnly: {onChange: true}
     });
    
     module.exports = SignatureCapture;
    

    这就是我在父签名组件中使用 SignatureCapture 组件的方式

    class Signature extends Component {
    
    render() {
    
        return (
            <View style={{ flex: 1, flexDirection: "column" }}>
    
                <SignatureCapture
                    style={{ flex: 8 }}
                    ref="sign",
                    onSaveEvent={this._onSaveEvent}
                    saveImageFileInExtStorage={false}
                    viewMode={"portrait"}/>
    
                <TouchableHighlight style={{ flex: 2 }}
                    onPress={() => { this.saveSign() } } >
                    <Text>Save</Text>
                </TouchableHighlight>
    
            </View>
        );
    }
    // Calls Save method of native view and triggers onSaveEvent callback
    saveSign() {
        this.refs["sign"].saveImage();        
    }
    
    _onSaveEvent(result) {
        //result.encoded - for the base64 encoded png
        //result.pathName - for the file path name
        console.log(result);
      }
    
      }
    
     export default Signature;
    

    【讨论】:

      【解决方案2】:

      我需要一个解决方案,让我从我的组件实例方法中返回值(在我的例子中是 Promises)。使用 receiveCommand 不允许我这样做。

      我能够使用UIManagerModule.addUIBlock 解决这个问题,类似于https://stackoverflow.com/a/31936516/194065

      public class MyViewModule extends ReactContextBaseJavaModule {
      
          public static final String TAG = MyViewModule.class.getSimpleName();
      
          public MyViewModule(ReactApplicationContext reactContext) {
              super(reactContext);
          }
      
          @Override
          public String getName() {
              return "MyView";
          }
      
          @ReactMethod
          public void someMethod(final int viewId, final Promise promise) {
              withMyView(viewId, promise, new MyViewHandler() {
                  @Override
                  public void handle(MyView view) {
                      String value = view.someMethod();
                      promise.resolve(value)
                  }
              });
          }
      
          private void withMyView(final int viewId, final Promise promise, final MyViewHandler handler) {
              UIManagerModule uiManager = getReactApplicationContext().getNativeModule(UIManagerModule.class);
              uiManager.addUIBlock(new UIBlock() {
                  @Override
                  public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
                      View view = nativeViewHierarchyManager.resolveView(viewId);
                      if (view instanceof MyView) {
                          MyView myView = (MyView) view;
                          handler.handle(myView);
                      }
                      else {
                          Log.e(TAG, "Expected view to be instance of MyView, but found: " + view);
                          promise.reject("my_view", "Unexpected view type");
                      }
                  }
              });
          }
      
      
      }
      

      用法:

      import React, { Component } from 'react';
      import { NativeModules, requireNativeComponent, findNodeHandle } from "react-native";
      const MyViewFunctions = NativeModules.MyView;
      
      
      class MyView extends Component {
      
          someMethod() {
              MyViewFunctions.someMethod(findNodeHandle(this.nativeCmp));
          }
      
          render() {
              return (
                  <RCTMyView
                      ref={cmp => this.nativeCmp = cmp}
                      {...this.props}
                  />
              );
      }
      
      const RCMyView = requireNativeComponent('RCMyView', MyView);
      
      export default MyView;
      

      【讨论】:

      • 如何将MyViewModule 添加到createViewManagers 中?它显示不兼容的类型 MyViewModuleViewManager
      • 以及如何使用MyViewFunctions
      • 对不起,我有一个错字。在组件中应该是MyViewFunctions.someMethod... 这个想法是MyView 仍然通过正常方式注册为自定义视图,而MyViewFunctions 基本上只允许静态访问这些视图上的调用方法。所以你仍然会有一个普通的视图管理器,但是你会使用MyViewFunctions 来调用这些视图上的方法,方法是传入你想要调用方法的视图的id。如果这没有意义,请告诉我。
      • 很好的答案,在其他任何地方都没有找到这个。
      • 来自未来的你好。这仍然是首选方法吗? React 原生文档在 Android 端编写得非常糟糕,原生库使用这种(以及其他两种不同的方式)来实现它。我想知道目前接受/推荐的方式是什么。
      【解决方案3】:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-01-31
        • 2016-06-03
        • 2017-11-24
        • 2019-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多