Calling a Android Native UI component method from React native Js code
Solution 1
As per the pointer given by @agent_hunt.
check this blog for explaination
I have used ui manager commands in SignatureCaptureViewManager. Posting my solutions
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()));
}
}
}
For sending commands to ViewManager i have added this method in Signature Capture component
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;
This is how i am using SignatureCapture component in my parent Signature component
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;
Solution 2
I needed a solution that let me return values from my component instance method (Promises in my case). Using receiveCommand
didn't allow me to do this.
I was able to solve this using UIManagerModule.addUIBlock
, similar to 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");
}
}
});
}
}
Usage:
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;
Solution 3
Please see instructions for exactly similar problem at https://github.com/facebook/react-native/pull/4438#issuecomment-163533312
![John](https://i.stack.imgur.com/zFPrp.jpg?s=256&g=1)
John
Android, React and React-Native enthusiast , And code freak. Love to program and solve problems
Updated on June 08, 2022Comments
-
John about 2 years
I have created a CustomView SignatureView.java which extends LinearLayout for capturing signature in Android Native.
And created SignatureCapturePackage.java and SignatureCaptureViewManager.java
public class SignatureCaptureMainView extends LinearLayout { .... public void saveImage(){ //Save image to file } }
this the Package class
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(); } }
this is the ViewManager class
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); } }
This is Signature.js bundle
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;
I am using the Module in ReactNative like this
<SignatureCapture onSaveEvent={this._onSaveEvent} saveImageFileInExtStorage={false} viewMode={"portrait"}/>
Everything worksFine. But i have to save the image only when some click event occurs in the react side. ie, i have to call SignatureCaptureMainView's saveImage() method from reactnative js code.
How can i achieve it ?.Please help
-
Val almost 7 yearsHow can I add
MyViewModule
intocreateViewManagers
? It shows incompatible typeMyViewModule
withViewManager
. -
Val almost 7 yearsAnd how to use
MyViewFunctions
? -
Sean Adkinson almost 7 yearsSorry, I had a typo. Should've been
MyViewFunctions.someMethod
in the component... The idea is thatMyView
is still registered as a custom view through the normal means, and thatMyViewFunctions
essentially just allows for static access to calling methods on those views. So you would still have a normal view manager, but you'd useMyViewFunctions
to call methods on those views, by passing in the id of the view that you want to call the method on. Let me know if that doesn't make sense. -
Adamski about 6 yearsGreat answer, hadn't found this anywhere else.
-
Cristiano Coelho about 4 yearsHello from the future. Is this still the preferred way to do it? React native docs are so poorly written on the Android side, and native libraries use this (and other two different ways) to implement this. I wonder what's the currently accepted/recommended way for this.