React Native 基于畫板簡析封裝安卓原生UI

1、創(chuàng)建 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端發(fā)送事件,傳遞數據。

·列舉注冊事件

//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();

}

·發(fā)送事件

//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端 發(fā)送事件

<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各種技術咨詢及開發(fā)業(yè)務

-END-

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容