美文网首页
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