美文网首页
React Native 基于画板简析封装安卓原生UI

React Native 基于画板简析封装安卓原生UI

作者: SEATELL海说软件 | 来源:发表于2017-12-25 14:35 被阅读0次

    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

    importReact, {Component,PropTypes}from'react';

    import{requireNativeComponent,View}from'react-native';

    export default classDrawCanvasViewextendsComponent{

    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() {

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

    Object.assign(nativeProps, {

    style:nativeProps.style,

    loadMaterial:this.loadMaterial,

    sendCommand:this.sendCommand,

    onDraw:this._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,

    }

    constRCTDrawCanvasView=requireNativeComponent("DrawCanvasView",DrawCanvasView,null)

    6、自定义事件注册

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

    ·列举注册事件

    //PaintView.java

    public enumEvents {

    EVENT_ON_DRAW("onDraw"),

    EVENT_ON_DRAW_UP("onDrawUp"),

    EVENT_MENU_CLICK("onMenuClick");

    private finalStringmName;

    Events(finalString name) {

    mName= name;

    }

    @Override

    publicStringtoString() {

    returnmName;

    }

    }

    ·导出自定义事件

    //DrawCanvasManager.java

    @Override

    @Nullable

    publicMapgetExportedCustomDirectEventTypeConstants() {

    MapBuilder.Builder builder = MapBuilder.builder();

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

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

    }

    returnbuilder.build();

    }

    ·发送事件

    //DrawCanvasView.java

    @Override

    public voidonClick(View v) {

    WritableMap event= Arguments.createMap();

    switch(v.getId()) {

    caseR.id.ll_undo:

    mPaintView.undo();

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

    break;

    caseR.id.ll_redo:

    mPaintView.redo();

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

    break;

    caseR.id.ll_reset:

    mPaintView.clear();

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

    break;

    caseR.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

    /**

    *画板接收到命令

    *

    *@paramdrawCanvasView

    *@paramholder

    */

    @ReactProp(name=PROP_COMMAND)

    public voidsendCommand(finalDrawCanvasView drawCanvasView,ReadableMap holder) {

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

    intcommand = holder.getInt("command");

    switch(command) {

    caseCommand.DRAW: {

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

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

    intcolor=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;*/

    caseCommand.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 classDrawCanvasManagerextendsSimpleViewManager {

    private staticString TAG = DrawCanvasManager.class.getSimpleName();

    public static finalString PROP_MATERIAL ="material";

    public static finalString PROP_DRAW ="can_draw";

    public static finalString PROP_COMMAND ="send_command";

    privateContext mContext;

    @Override

    publicString getName() {

    return"DrawCanvasView";

    }

    @Override

    protectedDrawCanvasView createViewInstance(ThemedReactContext reactContext) {

    this.mContext = reactContext;

    return newDrawCanvasView(reactContext.getApplicationContext(),reactContext);

    }

    @Override

    @Nullable

    publicMap getExportedCustomDirectEventTypeConstants() {

    MapBuilder.Builder builder = MapBuilder.builder();

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

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

    }

    returnbuilder.build();

    }

    /**

    * name名称不能包含大写

    *

    *@paramdrawCanvasView

    *@parammaterial

    *@throwsException

    */

    @ReactProp(name = PROP_MATERIAL)

    public voidloadMaterial(finalDrawCanvasView drawCanvasView,ReadableMap material)throwsException {

    //content://路径

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

    String _uri=getRealFilePath(mContext,url);

    FileInputStream fis=new FileInputStream(_uri);*/

    //真实路径

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

    //图片资源

    intname =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(mContext,name);

    drawCanvasView.addSticker(newDrawableSticker(drawable));

    }

    /**

    *设置Canvas画布的类型

    *

    *@paramdrawCanvasView

    *@paramtype

    */

    @ReactProp(name = PROP_DRAW)

    public voidsetCanvasType(finalDrawCanvasView drawCanvasView, booleantype) {

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

    if(type) {

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

    drawCanvasView.setCanvasType(CanvasType.CAN_DRAW);

    }else{

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

    drawCanvasView.setCanvasType(CanvasType.CAN_SHOW);

    }

    }

    /**

    *画板接收到命令

    *

    *@paramdrawCanvasView

    *@paramholder

    */

    @ReactProp(name = PROP_COMMAND)

    public voidsendCommand(finalDrawCanvasView drawCanvasView,ReadableMap holder) {

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

    intcommand = holder.getInt("command");

    switch(command) {

    caseCommand.DRAW: {

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

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

    intcolor=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;*/

    caseCommand.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

    *

    *@paramcontext

    *@paramuri

    *@returnthe file path or null

    */

    public staticString getRealFilePath(finalContext context, finalUri uri) {

    if(null== uri)return null;

    finalString 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, newString[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);

    if(null!= cursor) {

    if(cursor.moveToFirst()) {

    intindex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);

    if(index > -1) {

    data = cursor.getString(index);

    }

    }

    cursor.close();

    }

    }

    returndata;

    }

    private staticBitmap big(Bitmap bitmap) {

    Matrix matrix =newMatrix();

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

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

    returnresizeBmp;

    }

    }

    //DrawCanvasView.java

    public classDrawCanvasViewextendsRelativeLayoutimplementsView.OnClickListener,OnSeekBarChangeListener,PaintView.OnDrawListener,PaintView.OnStickerOperationListener{

    privatePaintViewmPaintView;

    privateVerticalSeekBarmVerticalSeekBar;

    private static finalStringTAG= DrawCanvasView.class.getSimpleName();

    privateContextmContext;

    privateReactContextreactContext;

    ViewbtnUndo,btnRedo;

    publicDrawCanvasView(Context context,ReactContext reactContext) {

    super(context);

    this.mContext= context;

    this.reactContext=reactContext;

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

    initView(context);

    }

    public voidinitView(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 voidinitToolbar() {

    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 voidonClick(View v) {

    WritableMap event= Arguments.createMap();

    switch(v.getId()) {

    caseR.id.ll_undo:

    mPaintView.undo();

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

    break;

    caseR.id.ll_redo:

    mPaintView.redo();

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

    break;

    caseR.id.ll_reset:

    mPaintView.clear();

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

    break;

    caseR.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 voidonProgressChanged(SeekBar seekBar, intprogress, booleanfromUser) {

    mPaintView.setStrokeWidth(progress);

    }

    @Override

    public voidonStartTrackingTouch(SeekBar seekBar) {

    }

    @Override

    public voidonStopTrackingTouch(SeekBar seekBar) {

    }

    public voiddrawImage(Bitmap bitmap, floatleft, floattop) {

    mPaintView.drawImage(bitmap,left,top);

    }

    @Override

    public voidafterPaintInit(intviewWidth, intviewHeight) {

    }

    @Override

    public voidafterEachPaint(ArrayList drawShapes) {

    setUndoEnable(drawShapes);

    }

    @Override

    public voidafterRedoEachPaint(ArrayList drawShapes) {

    setRedoEnable(drawShapes);

    }

    private voidsetUndoEnable(ArrayList drawShapes) {

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

    btnUndo.setEnabled(false);

    }else{

    btnUndo.setEnabled(true);

    }

    }

    private voidsetRedoEnable(ArrayList drawShapes) {

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

    btnRedo.setEnabled(false);

    }else{

    btnRedo.setEnabled(true);

    }

    }

    public voidaddSticker(@NonNullSticker sticker){

    addSticker(sticker,Sticker.Position.CENTER);

    }

    public voidaddSticker(@NonNullfinalSticker sticker,final@Sticker.Positionintposition){

    if(ViewCompat.isLaidOut(this)){

    mPaintView.addStickerImmediately(sticker,position);

    }else{

    post(newRunnable() {

    @Overridepublic voidrun() {

    mPaintView.addStickerImmediately(sticker,position);

    }

    });

    }

    }

    public voidsetCanvasType(@NonNullCanvasType type){

    mPaintView.viewId=getId();

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

    mPaintView.setCanvasType(type);

    }

    public voidsendDrawCommand(intcommand,ReadableArray path, floatpaintSize, intpaintColor){

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

    }

    public voidsendCommand(intcommand){

    switch(command){

    caseCommand.UNDO:{

    mPaintView.undo();

    }

    break;

    caseCommand.CLEAR:{

    mPaintView.clear();

    }

    break;

    caseCommand.REDO:{

    mPaintView.redo();

    }

    break;

    caseCommand.SAVE:{

    }

    }

    }

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

    mPaintView.sendCommand(x,y);

    }*/

    public voidsendCommand(ReadableArray data){

    mPaintView.sendCommand(data);

    }

    @Override

    public voidonStickerAdded(@NonNullSticker sticker) {

    Log.i(TAG,"onStickerAdded");

    }

    @Override

    public voidonStickerClicked(@NonNullSticker sticker) {

    Log.i(TAG,"onStickerClicked");

    }

    @Override

    public voidonStickerDeleted(@NonNullSticker sticker) {

    }

    @Override

    public voidonStickerDragFinished(@NonNullSticker sticker) {

    }

    @Override

    public voidonStickerZoomFinished(@NonNullSticker sticker) {

    }

    @Override

    public voidonStickerFlipped(@NonNullSticker sticker) {

    }

    @Override

    public voidonStickerDoubleTapped(@NonNullSticker sticker) {

    }

    @Overridepublic booleanonInterceptTouchEvent(MotionEvent ev) {

    mPaintView.onInterceptTouchEvent(ev);

    return super.onInterceptTouchEvent(ev);

    }

    }

    海说接受react各种技术咨询及开发业务

    -END-

    相关文章

      网友评论

          本文标题:React Native 基于画板简析封装安卓原生UI

          本文链接:https://www.haomeiwen.com/subject/bsylgxtx.html