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-