繁体   English   中英

react-native本机ui组件android中具有自定义名称的事件

[英]Events with Custom names in react-native native ui component android

我正在尝试实现对比度更改图像视图,其中图像的对比度由滑块更改,然后可以通过单击按钮来保存图像。 保存后,我希望 android 调用名称 onSave 的回调,以便可以返回文件路径。 为此,我在 JS 中创建了一个对比度更改图像视图 -

import React, {Component} from 'react';
let ReactNative = require('react-native');
let {View,
     Image,
     requireNativeComponent,
     DeviceEventEmitter,
     UIManager,
     ViewPropTypes} = ReactNative;
import PropTypes from 'prop-types';
import Slider from '@react-native-community/slider';

//https://productcrafters.io/blog/creating-custom-react-native-ui-components-android/

//https://stackoverflow.com/questions/34739670/creating-custom-ui-component-for-android-on-react-native-how-to-send-data-to-js/44207488#44207488


class ContrastEditor extends Component{
    constructor(props){
        super(props);
        this.state = {
            constrast : 1,
        };
        this.onValueChange = this.onValueChange.bind(this);
        this.onSave = this.onSave.bind(this);
        this.onReset = this.onReset.bind(this);
    }
    
    onSave(event) {
        console.log(event);
        if(event.nativeEvent.fileName){
            if (!this.props.onSave) {
                return;
            }
            console.log('test');
            this.props.onSave({
                fileName: event.nativeEvent.fileName,
                saveStatus: event.nativeEvent.saveStatus,
            });
        }
    }
    
    onReset(event){
        console.log(event);
        if(event.nativeEvent.resetStatus){
            if(!this.props.onReset){
                return;
            }
            console.log('test2');
            this.props.onReset({resetStatus});
        }
    }
    
    onValueChange(value){
        //this.setState({contrast : value});
    }

    onSlidingComplete(value){
        this.setState({contrast : value});
    }

    saveImage() {
        UIManager.dispatchViewManagerCommand(
            ReactNative.findNodeHandle(this.view),
            UIManager.getViewManagerConfig('RNContrastChangingImageView').Commands.save,
            [],
        );  
    }

    resetImage() {
        UIManager.dispatchViewManagerCommand(
            ReactNative.findNodeHandle(this.view),
            UIManager.getViewManagerConfig('RNContrastChangingImageView').Commands.reset,
            [],
        );
    }

    render(){
        return(
            <View
                style = {{
                    flex : 1,
                }}>
                <RNContrastChangingImageView 
                    ref = {(ref)=>{this.view = ref;}}
                    style = {{
                        flex : 1
                    }}
                    source = {this.props.source}
                    contrast = {this.state.contrast}
                    resizeMode = {'contain'}
                    onSave={this.onSave}
                    onReset={this.onReset}/>
                <Slider
                    minimumValue = {-1}
                    maximumValue = {3}
                    onValueChange = {
                        (value)=>{
                            this.onValueChange(value)
                        }
                    }
                    onSlidingComplete = {
                        (value)=>{
                            this.onSlidingComplete(value)
                        }
                    }/> 
            </View>
        );  
    }   
}

ContrastEditor.propTypes = {
    onSave: PropTypes.func,
    onReset : PropTypes.func,
    source: PropTypes.string.isRequired,
    resizeMode: PropTypes.oneOf(['contain', 'cover', 'stretch']),
}

ContrastEditor.defaultProps = {
    resizeMode: 'contain',
    onSave : ()=>{},
    onReset : ()=>{},
}

let RNContrastChangingImageView = requireNativeComponent(
    'RNContrastChangingImageView', 
    ContrastEditor,
    {nativeOnly: { onSave: true, onReset : true}}
);


export default ContrastEditor;

以下是 ViewManager 类-

package com.reactcontrastimagelibrary;

import java.util.Map;
import javax.annotation.Nullable;

import android.util.Log;
import android.content.Context;

import com.facebook.infer.annotation.Assertions;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;

//Tutorial on RN bridge https://itnext.io/how-to-build-react-native-bridge-and-get-pdf-viewer-44614f11e08e

public class RNContrastChangingImageManager extends SimpleViewManager<RNContrastChangingImageView> {

    private static final String TAG = "ContrastEditor";
    public static final int COMMAND_SAVE_IMAGE = 1;
    public static final int COMMAND_RESET_IMAGE = 2;
    private Context mContext;
    private RNContrastChangingImageView view = null;
    

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

    public RNContrastChangingImageManager(ReactApplicationContext reactContext) {
        mContext = reactContext;
    }

    @Override
    protected RNContrastChangingImageView createViewInstance(ThemedReactContext reactContext) {
        
        if(view == null){
            view = new RNContrastChangingImageView(reactContext);     
        }
        return view;
    }

    @ReactProp(name = "source")
    public void setImageUri(RNContrastChangingImageView view, String imgUrl) {
        view.setImageUri(imgUrl);
    }

    @ReactProp(name = "contrast", defaultFloat = 1f)
    public void setContrastValue(RNContrastChangingImageView view, float contrast) {
        view.setContrast(contrast);
    }

    @ReactProp(name = "resizeMode")
    public void setResizeMode(RNContrastChangingImageView view, String mode) {
        view.setResizeMode(mode);
    }
    
    @Override
    public Map<String,Integer> getCommandsMap() {
        Log.d("React"," View manager getCommandsMap:");
        return MapBuilder.of(
                "save",
                COMMAND_SAVE_IMAGE,
                "reset",
                COMMAND_RESET_IMAGE);
    }

    @Override
    public void receiveCommand(
            RNContrastChangingImageView view,
            int commandType,
            @Nullable ReadableArray args) {
        Assertions.assertNotNull(view);
        Assertions.assertNotNull(args);
        switch (commandType) {
            case COMMAND_SAVE_IMAGE: {
                Log.d(TAG, "Command called");
                view.save();
                return;
            }
            case COMMAND_RESET_IMAGE: {
                view.reset();
                return;
            }
            default:
                throw new IllegalArgumentException(String.format(
                        "Unsupported command %d received by %s.",
                        commandType,
                        getClass().getSimpleName()));
        }
    }

    @Override
    public @Nullable Map getExportedCustomDirectEventTypeConstants() {
        return MapBuilder.of(
                "save",
                MapBuilder.of("registrationName", "onSave"),
                "reset",
                MapBuilder.of("registrationName", "onReset"))
        );
    }
}

这是我的视图类-

package com.reactcontrastimagelibrary;

import android.content.Context;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.ParcelFileDescriptor;
import android.net.Uri;
import android.util.Log;
import android.app.Activity;

import androidx.appcompat.widget.AppCompatImageView;

import org.opencv.android.Utils;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;

import java.io.IOException;
import java.io.InputStream;
import java.io.FileDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.concurrent.ExecutionException;
import java.util.UUID;

import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;

public class RNContrastChangingImageView extends AppCompatImageView {
    public static final String TAG = "ContrastEditor";
    private String cacheFolderName = "RNContrastChangingImage";
    private Bitmap initialData = null;
    private Bitmap imageData = null;
    private String imageUri = null;
    private double contrast = 1;
    protected static  Context mContext;
    public static RNContrastChangingImageView instance = null;
    public String fileName = null;

    public RNContrastChangingImageView(Context context) {
        super(context);
        mContext = context;
        //createInstance(context, activity);
    }

    public static RNContrastChangingImageView getInstance() {
        return instance;
    }

    public static void createInstance(Context context) {
        mContext = context;
        instance = new RNContrastChangingImageView(context);
    }

    public void setImageUri(String imgUri) {
        Log.d(TAG, "set image");
        Log.d(TAG, "image source : " + imgUri);
        if (imgUri != this.imageUri) {
            this.imageUri = imgUri;
            try{
                File imgFile = new File(imgUri);
                Bitmap bitmap = BitmapFactory.decodeStream(
                    new FileInputStream(imgFile)
                );
                Log.d(TAG, "set image source");
                this.imageData = bitmap;
                this.initialData = bitmap;
                this.setImageBitmap(bitmap);
            } catch(FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    public void setContrast(double contrastVal) {
        this.contrast = contrastVal;

        if (this.imageData != null) {
            this.updateImageContrast();
        }
    }

    public void setResizeMode(String mode) {
        switch (mode) {
            case "cover":
                this.setScaleType(ScaleType.CENTER_CROP);
                break;
            case "stretch":
                this.setScaleType(ScaleType.FIT_XY);
                break;
            case "contain":
            default:
                this.setScaleType(ScaleType.FIT_CENTER);
                break;
        }
    }

    private String generateStoredFileName() throws Exception {
        String folderDir = this.mContext.getCacheDir().toString();
        File folder = new File( folderDir + "/" + this.cacheFolderName);
        if (!folder.exists()) {
            boolean result = folder.mkdirs();
            if (result) {
                Log.d(TAG, "wrote: created folder " + folder.getPath());
            } else {
                Log.d(TAG, "Not possible to create folder");
                throw new Exception("Failed to create the cache directory");
            }   
        }   
        return folderDir + "/" + this.cacheFolderName + "/" + "contrast_editted" + UUID.randomUUID() + ".png"; 
    } 

    private void updateImageContrast() {
        try {
            Mat matImage = new Mat();
            Utils.bitmapToMat(this.initialData, matImage);

            Scalar imgScalVec = Core.sumElems(matImage);
            double[] imgAvgVec = imgScalVec.val;
            for (int i = 0; i < imgAvgVec.length; i++) {
                imgAvgVec[i] = imgAvgVec[i] / (matImage.cols() * matImage.rows());
            }
            double imgAvg = (imgAvgVec[0] + imgAvgVec[1] + imgAvgVec[2]) / 3;
            int brightness = -(int) ((this.contrast - 1) * imgAvg);
            matImage.convertTo(matImage, matImage.type(), this.contrast, brightness);

            Bitmap resultImage = Bitmap.createBitmap(
                this.imageData.getWidth(),
                this.imageData.getHeight(),
                this.imageData.getConfig()
            );
            Utils.matToBitmap(matImage, resultImage);
            this.imageData = resultImage;
            this.setImageBitmap(resultImage);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void reset(){
        this.contrast = 1;
        this.setImageBitmap(this.initialData);
        WritableMap event = Arguments.createMap();
        event.putString("resetStatus", "success");
        event.putString("action", "reset");
        final ReactContext reactContext = (ReactContext) this.getContext();
        reactContext.getJSModule(
            RCTEventEmitter.class
        ).receiveEvent(
            getId(), 
            "reset", 
            event
        );
        return;
    }
    
    public void save(){
        String fileName = null;
        try{
            fileName = generateStoredFileName();
        }
        catch (Exception e){
            Log.d(TAG, "failed to create folder");
        }
        Mat matImage = new Mat();
        Utils.bitmapToMat(this.imageData, matImage);
        boolean success = Imgcodecs.imwrite(fileName, matImage);
        matImage.release();
        WritableMap event = Arguments.createMap();  
        if(success){ 
            Log.d(TAG, "image saved, fileName: "+fileName);
            event.putString("fileName", fileName);
            event.putString("saveStatus", "success");
            event.putString("action", "save");
        } else {
            event.putString("fileName", "");
            event.putString("saveStatus", "failure");
            event.putString("action", "save");
        }
        ReactContext reactContext = (ReactContext) this.getContext();
        reactContext.getJSModule(
            RCTEventEmitter.class
        ).receiveEvent(
            getId(), 
            "save", 
            event
        );
        return;
    }
}

重要的方法是

  1. ViewManager 类中的 getExportedCustomDirectEventTypeConstants()
  2. View 类中的 save() 和 reset()

我遵循了这里给出的答案 - 在 React Native 上为 android 创建自定义 UI 组件。 如何向JS发送数据? 但是我无法使 onSave 和 onReset 回调工作,并且实际上没有关于如何在 React-Native 中使用自定义名称创建事件的文档。 我还尝试使用 topChange 事件制作文档中给出的示例,但由于某种原因,这似乎也不起作用。 谁能指出我的错误是什么?

例如,您是否尝试在重置函数中记录 getId() ? 如果它是 -1,则表示您没有在实际视图中调用它。 要测试您的 java/js 接线是否正常工作,请尝试在其中一个管理器函数中测试一个事件,该函数将实际视图作为参数

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM