1、创建 ViewManager 的子类


2、实现方法 createViewInstance


    @Override

    protected DrawCanvasView createViewInstance(ThemedReactContext reactContext) {

        this.mContext = reactContext;

        return new DrawCanvasView(reactContext.getApplicationContext(), reactContext);

    }


3、通过 @ReactProp 注解导出属性的设置方法


@ReactProp 注解必须包含一个字符串类型的参数 name。这个参数指定了对应属性在JavaScript 端的名字。

    @ReactProp(name = PROP_DRAW)

    public void setCanvasType(final DrawCanvasView drawCanvasView, boolean type) {

        System.out.println(TAG + " " + type);

        if (type) {

            //代表老师,可以绘制画板

            drawCanvasView.setCanvasType(CanvasType.CAN_DRAW);

        } else {

            //代表学生,可以看画板,不能绘制

            drawCanvasView.setCanvasType(CanvasType.CAN_SHOW);

        }

    }



4、注册 ViewManager


    //DrawCanvasPackage.java

    @Override

public List createViewManagers(ReactApplicationContext reactContext) {

    return Arrays.asList(

            new DrawCanvasManager()

    );

}


5、实现对应的 JavaScript 模块


    //DrawCanvasView.js

    import React, {ComponentPropTypesfrom 'react';

import {requireNativeComponentViewfrom 'react-native';

export default class DrawCanvasView extends Component {

    constructor(props) {

        super(props);

    };

 

    _onDraw=(event)=> {

        if (this.props.onDraw) {

            this.props.onDraw(event.nativeEvent);

        }

    }

 

    _onDrawUp=(event)=>{

        if(this.props.onDrawUp){

            this.props.onDrawUp(event.nativeEvent);

        }

    }

 

    _onMenuClick=(event)=>{

        if(this.props.onMenuClick){

            this.props.onMenuClick(event.nativeEvent);

        }

    }

 

    setNativeProps(nativeProps) {

        this._root.setNativeProps(nativeProps);

    }

 

    loadMaterial = (material) => {

        this.setNativeProps({material: material});

    }

    sendCommand = (holder) => {

        this.setNativeProps({send_command: holder});

    }

    _assignRoot = (component) => {

        this._root = component;

    }

 

    render() {

        const nativeProps = Object.assign({}, this.props);

        Object.assign(nativeProps, {

            stylenativeProps.style,

            loadMaterialthis.loadMaterial,

            sendCommandthis.sendCommand,

            onDrawthis._onDraw,

            onDrawUp:this._onDrawUp,

            onMenuClick:this._onMenuClick,

        })

        return (

            <RCTDrawCanvasView

                ref={this._assignRoot}

                {...nativeProps}

            />

        )

    }

}

DrawCanvasView.propTypes={

    ...View.propTypes,

    material:PropTypes.func,

    send_command:PropTypes.func,

    onDraw:PropTypes.func,

    onDrawUp:PropTypes.func,

    can_draw:PropTypes.bool,

    onMenuClick:PropTypes.func,

}

const RCTDrawCanvasView requireNativeComponent("DrawCanvasView",DrawCanvasView, null)




6、自定义事件注册


对于用户的操作,例如绘制画板,缩放,拖拽,JS端需要响应用户的操作,所以需要原生视图向JS端发送事件,传递数据。


·        列举注册事件


   //PaintView.java

   public enum Events {

    EVENT_ON_DRAW("onDraw"),

    EVENT_ON_DRAW_UP("onDrawUp"),

    EVENT_MENU_CLICK("onMenuClick");

 

    private final String mName;

 

    Events(final String name) {

        mName = name;

    }

 

    @Override

    public String toString() {

        return mName;

    }

}


·        导出自定义事件


//DrawCanvasManager.java

@Override

@Nullable

public Map getExportedCustomDirectEventTypeConstants() {

    MapBuilder.Builder builder = MapBuilder.builder();

    for (Events event : Events.values()) {

        builder.put(event.toString()MapBuilder.of("registrationName"event.toString()));

    }

    return builder.build();

}


·        发送事件


//DrawCanvasView.java

@Override

    public void onClick(View v) {

        WritableMap event= Arguments.createMap();

        switch (v.getId()) {

            case R.id.ll_undo:

                mPaintView.undo();

                event.putInt("command"Command.UNDO);

                break;

            case R.id.ll_redo:

                mPaintView.redo();

                event.putInt("command"Command.REDO);

                break;

            case R.id.ll_reset:

                mPaintView.clear();

                event.putInt("command"Command.CLEAR);

                break;

            case R.id.ll_save:

                mPaintView.clear();

                event.putInt("command"Command.SAVE);

                break;

            default:

                Log.i("ID---view"Integer.toString(v.getId()));

                break;

        }

//        System.out.println(TAG+"1     "+getId());

        reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(

                getId()PaintView.Events.EVENT_MENU_CLICK.toString(),event);

    }


7、原生端接收 JS 端传递的数据解析


使用 ReadableMap 接收数据,如果接收的是数组则使用 ReadableArray

/**

  * 画板接收到命令

  *

  * @param drawCanvasView

  @param holder

  */

 @ReactProp(name PROP_COMMAND)

 public void sendCommand(final DrawCanvasView drawCanvasViewReadableMap holder) {

     System.out.println(TAG+" "+holder);

     int command = holder.getInt("command");

     switch (command) {

         case Command.DRAW: {

             ReadableArray path=holder.getArray("path");

             float size=(float) holder.getDouble("paintSize");

             int color=holder.getInt("paintColor");

             drawCanvasView.sendDrawCommand(command,path,size,color);

         }

         break;

/*         case Command.DRAG:{

             float x=(float)holder.getDouble("currentDistanceX");

             float y=(float)holder.getDouble("currentDistanceY");

             drawCanvasView.sendCommand(x,y);

         }

         break;*/

         case Command.GESTURE:{

             ReadableArray data=holder.getArray("data");

             drawCanvasView.sendCommand(data);

         }

         default:

             //数据只有command

             drawCanvasView.sendCommand(command);

             break;

         }

 }





8、原生组件使用


onDraw , onDrawUp , onMenuClick 皆是用于响应用户操作,原生向 JS端 发送事件

<DrawCanvasView

    ref={(ref)=>{this._drawCanvas=ref}}

    onDraw={(e)=>this.onDraw(e)}

    onDrawUp={(e)=>this.onDrawUp(e)}

    can_draw={true}

    onMenuClick={(e)=>this._onMenuClick(e)}

style={{width:width,height:536*size}}/>

 

主要代码展示


     //DrawCanvasManager.java

     public class DrawCanvasManager extends SimpleViewManager {

 

    private static String TAG = DrawCanvasManager.class.getSimpleName();

    public static final String PROP_MATERIAL = "material";

    public static final String PROP_DRAW = "can_draw";

    public static final String PROP_COMMAND = "send_command";

    private Context mContext;

 

    @Override

    public String getName() {

        return "DrawCanvasView";

    }

 

    @Override

    protected DrawCanvasView createViewInstance(ThemedReactContext reactContext) {

        this.mContext = reactContext;

        return new DrawCanvasView(reactContext.getApplicationContext()reactContext);

    }

 

    @Override

    @Nullable

    public Map getExportedCustomDirectEventTypeConstants() {

        MapBuilder.Builder builder = MapBuilder.builder();

        for (Events event : Events.values()) {

            builder.put(event.toString()MapBuilder.of("registrationName"event.toString()));

        }

        return builder.build();

    }

 

    /**

     * name 名称不能包含大写

     *

     * @param drawCanvasView

     * @param material

     * @throws Exception

     */

    @ReactProp(name = PROP_MATERIAL)

    public void loadMaterial(final DrawCanvasView drawCanvasViewReadableMap material) throws Exception {

        //content:// 路径

       /* Uri url=Uri.parse(material.getString("uri"));

        String _uri=getRealFilePath(mContext,url);

        FileInputStream fis=new FileInputStream(_uri);*/

 

        //真实路径

        // Bitmap bitmap=BitmapFactory.decodeFile(uri);

 

        //图片资源

        int name = 0;

        String _name = material.getString("uri");

        if (_name.equals("image1")) {

            // name=R.drawable.image1;

            name = R.drawable.ic_iamge1;

        else if (_name.equals("image2")) {

            name = R.drawable.image2;

            //name=R.drawable.ic_image2;

        else if (_name.equals("image3")) {

            name = R.drawable.image3;

        }

    /*    InputStream fis=mContext.getResources().openRawResource(name);

        Bitmap bitmap = BitmapFactory.decodeStream(fis);

        if(bitmap==null){

            return;

        }

        System.out.println("DrawCanvasManager==" + material.getString("uri")+" "+bitmap);

 

        //最好是矢量图,位图缩放后会变得模糊

     //   Bitmap newBitMpa=big(bitmap);

        drawCanvasView.drawImage(bitmap,(float) material.getDouble("left"),(float) material.getDouble("top"));*/

        Drawable drawable = ContextCompat.getDrawable(mContextname);

        drawCanvasView.addSticker(new DrawableSticker(drawable));

    }

 

    /**

     * 设置Canvas画布的类型

     *

     * @param drawCanvasView

     * @param type

     */

    @ReactProp(name = PROP_DRAW)

    public void setCanvasType(final DrawCanvasView drawCanvasView, boolean type) {

        System.out.println(TAG + " " + type);

        if (type) {

            //代表老师,可以绘制画板

            drawCanvasView.setCanvasType(CanvasType.CAN_DRAW);

        else {

            //代表学生,可以看画板,不能绘制

            drawCanvasView.setCanvasType(CanvasType.CAN_SHOW);

        }

    }

 

    /**

     * 画板接收到命令

     *

     * @param drawCanvasView

     * @param holder

     */

    @ReactProp(name = PROP_COMMAND)

    public void sendCommand(final DrawCanvasView drawCanvasViewReadableMap holder) {

        System.out.println(TAG+" "+holder);

        int command = holder.getInt("command");

        switch (command) {

            case Command.DRAW: {

                ReadableArray path=holder.getArray("path");

                float size=(float) holder.getDouble("paintSize");

                int color=holder.getInt("paintColor");

                drawCanvasView.sendDrawCommand(command,path,size,color);

            }

            break;

   /*         case Command.DRAG:{

                float x=(float)holder.getDouble("currentDistanceX");

                float y=(float)holder.getDouble("currentDistanceY");

                drawCanvasView.sendCommand(x,y);

            }

            break;*/

            case Command.GESTURE:{

                ReadableArray data=holder.getArray("data");

                drawCanvasView.sendCommand(data);

            }

            default:

                //数据只有command

                drawCanvasView.sendCommand(command);

                break;

            }

    }

 

    /**

     * Try to return the absolute file path from the given Uri

     *

     * @param context

     * @param uri

     * @return the file path or null

     */

    public static String getRealFilePath(final Context context, final Uri uri) {

        if (null == uri) return null;

        final String scheme = uri.getScheme();

        String data = null;

        if (scheme == null) {

            data = uri.getPath();

        else if (ContentResolver.SCHEME_FILE.equals(scheme)) {

            data = uri.getPath();

        else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {

            Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);

            if (null != cursor) {

                if (cursor.moveToFirst()) {

                    int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);

                    if (index > -1) {

                        data = cursor.getString(index);

                    }

                }

                cursor.close();

            }

        }

        return data;

    }

 

    private static Bitmap big(Bitmap bitmap) {

        Matrix matrix = new Matrix();

        matrix.postScale(2.5f2.5f)//长和宽放大缩小的比例

        Bitmap resizeBmp = Bitmap.createBitmap(bitmap00bitmap.getWidth()bitmap.getHeight()matrix, true);

        return resizeBmp;

    }

}

//DrawCanvasView.java

public class DrawCanvasView extends RelativeLayout implements View.OnClickListener,OnSeekBarChangeListener,PaintView.OnDrawListener,PaintView.OnStickerOperationListener{

 

    private PaintView mPaintView;

    private VerticalSeekBar mVerticalSeekBar;

    private static final String TAG = DrawCanvasView.class.getSimpleName();

    private Context mContext;

    private ReactContext reactContext;

    View btnUndo,btnRedo;

    public DrawCanvasView(Context contextReactContext reactContext) {

        super(context);

        this.mContext = context;

        this.reactContext=reactContext;

        LayoutInflater.from(context).inflate(R.layout.activity_draw, this);

        initView(context);

    }

 

    public void initView(Context context) {

        //初始化颜色板

        //  initColorPickerDialog();

        //初始化自定义的ToolBar

        initToolbar();

        mPaintView = (PaintView) findViewById(R.id.draw_view);

        mVerticalSeekBar = (VerticalSeekBar) findViewById(R.id.seekBar);

        mVerticalSeekBar.setOnSeekBarChangeListener(this);

        mPaintView.setStrokeWidth(mVerticalSeekBar.getProgress());

        mPaintView.setBgColor(Color.WHITE);

        mPaintView.setOnDrawListener(this);

        mPaintView.setOnStickerOperationListener(this);

        mPaintView.setConstrained(true);

        mPaintView.myContext=reactContext;

    }

 

    /**

     * 初始化自定义toolbar

     */

    private void initToolbar() {

        btnUndo=findViewById(R.id.ll_undo);

        btnUndo.setOnClickListener(this);

        btnUndo.setEnabled(false);

        btnRedo=findViewById(R.id.ll_redo);

        btnRedo.setOnClickListener(this);

        btnRedo.setEnabled(false);

        findViewById(R.id.ll_reset).setOnClickListener(this);

        findViewById(R.id.ll_save).setOnClickListener(this);

    }

 

    @Override

    public void onClick(View v) {

        WritableMap event= Arguments.createMap();

        switch (v.getId()) {

            case R.id.ll_undo:

                mPaintView.undo();

                event.putInt("command"Command.UNDO);

                break;

            case R.id.ll_redo:

                mPaintView.redo();

                event.putInt("command"Command.REDO);

                break;

            case R.id.ll_reset:

                mPaintView.clear();

                event.putInt("command"Command.CLEAR);

                break;

            case R.id.ll_save:

                mPaintView.clear();

                event.putInt("command"Command.SAVE);

                break;

            default:

                Log.i("ID---view"Integer.toString(v.getId()));

                break;

        }

//        System.out.println(TAG+"1     "+getId());

        reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(

                getId()PaintView.Events.EVENT_MENU_CLICK.toString(),event);

    }

 

    @Override

    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

        mPaintView.setStrokeWidth(progress);

    }

 

    @Override

    public void onStartTrackingTouch(SeekBar seekBar) {

 

    }

 

    @Override

    public void onStopTrackingTouch(SeekBar seekBar) {

 

    }

 

    public void drawImage(Bitmap bitmap, float left, float top) {

        mPaintView.drawImage(bitmaplefttop);

    }

 

    @Override

    public void afterPaintInit(int viewWidth, int viewHeight) {

 

    }

 

    @Override

    public void afterEachPaint(ArrayList drawShapes) {

        setUndoEnable(drawShapes);

    }

 

    @Override

    public void afterRedoEachPaint(ArrayList drawShapes) {

        setRedoEnable(drawShapes);

    }

 

    private void setUndoEnable(ArrayList drawShapes) {

        if (drawShapes.size() == 0) {

            btnUndo.setEnabled(false);

        }else {

            btnUndo.setEnabled(true);

        }

    }

    private void setRedoEnable(ArrayList drawShapes) {

        if (drawShapes.size() == 0) {

            btnRedo.setEnabled(false);

        }else {

            btnRedo.setEnabled(true);

        }

    }

 

    public void addSticker(@NonNull Sticker sticker){

        addSticker(sticker,Sticker.Position.CENTER);

    }

 

    public void addSticker(@NonNull final Sticker sticker,final @Sticker.Position int position){

        if(ViewCompat.isLaidOut(this)){

            mPaintView.addStickerImmediately(stickerposition);

        }else{

            post(new Runnable() {

                @Override public void run() {

                    mPaintView.addStickerImmediately(stickerposition);

                }

            });

        }

 

    }

 

    public void setCanvasType(@NonNull CanvasType type){

        mPaintView.viewId=getId();

        System.out.println(TAG+"2     "+getId());

        mPaintView.setCanvasType(type);

    }

 

    public void sendDrawCommand(int commandReadableArray path, float paintSize, int paintColor){

        mPaintView.sendDrawCommand(command,path,paintSize,paintColor);

    }

 

    public void sendCommand(int command){

        switch(command){

            case Command.UNDO:{

                mPaintView.undo();

            }

            break;

            case Command.CLEAR:{

                mPaintView.clear();

            }

            break;

            case Command.REDO:{

                mPaintView.redo();

            }

            break;

            case Command.SAVE:{

 

            }

        }

    }

 

 /*   public void sendCommand(float x,float y){

        mPaintView.sendCommand(x,y);

    }*/

 

    public void sendCommand(ReadableArray data){

        mPaintView.sendCommand(data);

    }

 

    @Override

    public void onStickerAdded(@NonNull Sticker sticker) {

        Log.i(TAG"onStickerAdded");

    }

 

    @Override

    public void onStickerClicked(@NonNull Sticker sticker) {

        Log.i(TAG"onStickerClicked");

    }

 

    @Override

    public void onStickerDeleted(@NonNull Sticker sticker) {

 

    }

 

    @Override

    public void onStickerDragFinished(@NonNull Sticker sticker) {

 

    }

 

    @Override

    public void onStickerZoomFinished(@NonNull Sticker sticker) {

 

    }

 

    @Override

    public void onStickerFlipped(