Coder Social home page Coder Social logo

dsbgenius / whiteboard Goto Github PK

View Code? Open in Web Editor NEW
602.0 602.0 167.0 21.37 MB

SketchBoard是一个可涂鸦、绘图、添加文字、图像(可旋转缩放)、背景的Fragment,其中主要由SketchView利用matrix完成所有图形绘制操作。 SketchBoard is a Fragment that can add a graffiti, graphics, text, images (rotating and scaling), or background 。

Home Page: http://www.jianshu.com/users/9efe1db2c646/latest_articles

Java 100.00%

whiteboard's People

Contributors

bryant1410 avatar diaoxinqiang avatar tangentlu666 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

whiteboard's Issues

触摸点与绘制点不对照

MainActivity中包含一个Fragment,我在这个Fragment中使用WhiteBoardFragment。然后出现触摸点与绘制点不对照。请问这个怎么解决?目前直接在Activity中使用是正常的。

关于Canvas

您好,我想请问一下关于Canvas旋转之后,触摸的点的坐标对不上绘制的坐标,这个有米有好的解决办法?

tempHoldCanva和tempCanvas的区别

把把十个操作以前的笔画全都画进固化层 这个是做什么用的呢?
为什么要新建画布,在原先的画布上不可以吗?
求大神告知。

SketchView控件拓展性问题

类成员修饰符好多都是private和默认,
比如:float simpleScale = 0.5f;//图片载入的缩放倍数
这个变量没有set和get,那么如果无法修改应该用final来表示,这个变量又对应 BitmapUtils.decodeSampleBitMapFromFile(mContext, path, simpleScale);
导致将图片从sd卡去除来的时候,进行了缩放操作,导致细线变粗变模糊,画质不清。
本来想复写
public void setBackgroundByPath(String path) {
Bitmap sampleBM = getSampleBitMap(path);
if (sampleBM != null) {
curSketchData.backgroundBM = sampleBM;
backgroundSrcRect = new Rect(0, 0, curSketchData.backgroundBM.getWidth(), curSketchData.backgroundBM.getHeight());
backgroundDstRect = new Rect(0, 0, mWidth, mHeight);
invalidate();
} else {
Toast.makeText(mContext, "图片文件路径有误!", Toast.LENGTH_SHORT).show();
}
}
结果发现,curSketchData有set没get。。。修饰符又是默认,只能改源码。。。

关于橡皮擦

大神,能不能让橡皮擦真正的是橡皮擦,现在是和画笔一样连同背景一期覆盖了啊。大神

绘制起始点移位

当画板SketchView父类布局高度变化时,绘制的起始点就从手指着落点,y轴坐标偏移了高度缩放的距离,父类布局高度恢复时,偏移情况也恢复。

能用在eclipse上吗

公司老大让我在一个eclipse项目上用上你的库, 请问你有过这方面经验吗

导入依赖的问题

您好我直接导入依赖会有问题 有些功能我不需要 想去掉 可以提供些帮助吗

加功能

请问下如何加文字,圆,矩形,可以拖动旋转,放大功能。。。

图片文件选择错误问题。因为取到的是映射地址。

修改如下:

`
/*

  • Copyright (C) 2015 Federico Iosue ([email protected])
  • This program is free software: you can redistribute it and/or modify
  • it under the terms of the GNU General Public License as published by
  • the Free Software Foundation, either version 3 of the License, or
  • (at your option) any later version.
  • This program is distributed in the hope that it will be useful,
  • but WITHOUT ANY WARRANTY; without even the implied warranty of
  • MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  • GNU General Public License for more details.
  • You should have received a copy of the GNU General Public License
  • along with this program. If not, see http://www.gnu.org/licenses/.
    */

package com.yinghe.whiteboardlib.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.support.annotation.NonNull;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.Toast;

import com.yinghe.whiteboardlib.R;
import com.yinghe.whiteboardlib.Utils.BitmapUtils;
import com.yinghe.whiteboardlib.Utils.ScreenUtils;
import com.yinghe.whiteboardlib.bean.PhotoRecord;
import com.yinghe.whiteboardlib.bean.SketchData;
import com.yinghe.whiteboardlib.bean.StrokeRecord;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import static com.yinghe.whiteboardlib.bean.StrokeRecord.STROKE_TYPE_CIRCLE;
import static com.yinghe.whiteboardlib.bean.StrokeRecord.STROKE_TYPE_DRAW;
import static com.yinghe.whiteboardlib.bean.StrokeRecord.STROKE_TYPE_ERASER;
import static com.yinghe.whiteboardlib.bean.StrokeRecord.STROKE_TYPE_LINE;
import static com.yinghe.whiteboardlib.bean.StrokeRecord.STROKE_TYPE_RECTANGLE;
import static com.yinghe.whiteboardlib.bean.StrokeRecord.STROKE_TYPE_TEXT;

public class SketchView extends View implements OnTouchListener {

final String TAG = getClass().getSimpleName();

public interface TextWindowCallback {
    void onText(View view, StrokeRecord record);
}


public void setTextWindowCallback(TextWindowCallback textWindowCallback) {
    this.textWindowCallback = textWindowCallback;
}

private TextWindowCallback textWindowCallback;
private static final float TOUCH_TOLERANCE = 4;

public static final int EDIT_STROKE = 1;
public static final int EDIT_PHOTO = 2;

private static final int ACTION_NONE = 0;
private static final int ACTION_DRAG = 1;
private static final int ACTION_SCALE = 2;
private static final int ACTION_ROTATE = 3;

public static final int DEFAULT_STROKE_SIZE = 7;
public static final int DEFAULT_STROKE_ALPHA = 100;
public static final int DEFAULT_ERASER_SIZE = 50;


private float strokeSize = DEFAULT_STROKE_SIZE;
private int strokeRealColor = Color.BLACK;//画笔实际颜色
private int strokeColor = Color.BLACK;//画笔颜色
private int strokeAlpha = 255;//画笔透明度
private float eraserSize = DEFAULT_ERASER_SIZE;

// private int background = Color.WHITE;

Paint boardPaint;

Bitmap mirrorMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.mark_copy);
Bitmap deleteMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.mark_delete);
Bitmap rotateMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.mark_rotate);
Bitmap resetMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.mark_reset);
//    Bitmap rotateMarkBM = BitmapFactory.decodeResource(getResources(), R.drawable.test);
RectF markerCopyRect = new RectF(0, 0, mirrorMarkBM.getWidth(), mirrorMarkBM.getHeight());//镜像标记边界
RectF markerDeleteRect = new RectF(0, 0, deleteMarkBM.getWidth(), deleteMarkBM.getHeight());//删除标记边界
RectF markerRotateRect = new RectF(0, 0, rotateMarkBM.getWidth(), rotateMarkBM.getHeight());//旋转标记边界
RectF markerResetRect = new RectF(0, 0, resetMarkBM.getWidth(), resetMarkBM.getHeight());//旋转标记边界
private List<String> storagePaths = new ArrayList<>();
private Path strokePath;
private Paint strokePaint;
private float downX, downY, preX, preY, curX, curY;
private int mWidth, mHeight;

SketchData curSketchData;
//    private List<PhotoRecord> curSketchData.photoRecordList;

// private List curSketchData.strokeRecordList;
// private List curSketchData.strokeRedoList;
private Context mContext;

//    private Bitmap curSketchData.backgroundBM;
Rect backgroundSrcRect = new Rect();
Rect backgroundDstRect = new Rect();
StrokeRecord curStrokeRecord;
PhotoRecord curPhotoRecord;

int actionMode;

private int editMode = EDIT_STROKE;
private static float SCALE_MAX = 4.0f;
private static float SCALE_MIN = 0.2f;
private static float SCALE_MIN_LEN;

float simpleScale = 0.5f;//图片载入的缩放倍数
/**
 * 缩放手势
 */
private ScaleGestureDetector mScaleGestureDetector = null;
public void setStrokeType(int strokeType) {
    this.strokeType = strokeType;
}

public int getStrokeType() {
    return strokeType;
}

private int strokeType = StrokeRecord.STROKE_TYPE_DRAW;

private OnDrawChangedListener onDrawChangedListener;

public void setSketchData(SketchData sketchData) {
    this.curSketchData = sketchData;
    curPhotoRecord = null;
}

public void updateSketchData(SketchData sketchData) {
    if (curSketchData != null)
        curSketchData.thumbnailBM = getThumbnailResultBitmap();//更新数据前先保存上一份数据的缩略图
    setSketchData(sketchData);
}



public SketchView(Context context, AttributeSet attr) {
    super(context, attr);
    this.mContext = context;

// setSketchData(new SketchData());
initParams(context);
if (isFocusable()) {
this.setOnTouchListener(this);
mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.OnScaleGestureListener() {
@OverRide
public boolean onScale(ScaleGestureDetector detector) {
onScaleAction(detector);
return true;
}

            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector detector) {

            }
        });
    }
    storagePaths = getAllMount();
    invalidate();
}



private void initParams(Context context) {

// setFocusable(true);
// setFocusableInTouchMode(true);
setBackgroundColor(Color.WHITE);

    strokePaint = new Paint();
    strokePaint.setAntiAlias(true);
    strokePaint.setDither(true);
    strokePaint.setColor(strokeRealColor);
    strokePaint.setStyle(Paint.Style.STROKE);
    strokePaint.setStrokeJoin(Paint.Join.ROUND);
    strokePaint.setStrokeCap(Paint.Cap.ROUND);
    strokePaint.setStrokeWidth(strokeSize);

    boardPaint = new Paint();
    boardPaint.setColor(Color.GRAY);
    boardPaint.setStrokeWidth(ScreenUtils.dip2px(mContext, 0.8f));
    boardPaint.setStyle(Paint.Style.STROKE);

    SCALE_MIN_LEN = ScreenUtils.dip2px(context, 20);
}


public void setStrokeAlpha(int mAlpha) {
    this.strokeAlpha = mAlpha;
    calculColor();
    strokePaint.setStrokeWidth(strokeSize);
}


public void setStrokeColor(int color) {
    strokeColor = color;
    calculColor();
    strokePaint.setColor(strokeRealColor);
}


private void calculColor() {
    strokeRealColor = Color.argb(strokeAlpha, Color.red(strokeColor), Color.green(strokeColor), Color.blue(strokeColor));
}



@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    mWidth = MeasureSpec.getSize(widthMeasureSpec);
    mHeight = MeasureSpec.getSize(heightMeasureSpec);
    setMeasuredDimension(mWidth, mHeight);
}

SparseArray<Path> mActivePointers = new SparseArray<>();
@Override
public boolean onTouch(View arg0, MotionEvent event) {

    //当前DOWN或者UP的是手指的index
    int curPointerIndex = event.getActionIndex();
    //通过index获得当前手指的id
    int curPointerId = event.getPointerId(curPointerIndex);

    int actionMasked = event.getActionMasked();


    curX = event.getX();
    curY = event.getY();
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_POINTER_DOWN:
        case MotionEvent.ACTION_DOWN:
            PointF pointF = new PointF();
            pointF.x = event.getX(curPointerIndex);
            pointF.y = event.getY(curPointerIndex);
            Path p = new Path();
            p.moveTo(pointF.x, pointF.y);
            mActivePointers.append(curPointerId, p);
            touch_down(event);
            invalidate();
            break;
        case MotionEvent.ACTION_MOVE:
            touch_move(event);
            invalidate();
            break;
        case MotionEvent.ACTION_UP:
            touch_up();
            invalidate();
            break;
    }
    preX = curX;
    preY = curY;
    return true;
}

private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return (float) Math.sqrt(x * x + y * y);
}

@Override
protected void onDraw(Canvas canvas) {
    drawBackground(canvas);
    drawRecord(canvas);
    if (onDrawChangedListener != null)
        onDrawChangedListener.onDrawChanged();
}

private void drawBackground(Canvas canvas) {
    if (curSketchData.backgroundBM != null) {

// Rect dstRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
// canvas.drawBitmap(curSketchData.backgroundBM, backgroundSrcRect, backgroundDstRect, null);
Matrix matrix = new Matrix();
float wScale = (float) canvas.getWidth() / curSketchData.backgroundBM.getWidth();
float hScale = (float) canvas.getHeight() / curSketchData.backgroundBM.getHeight();
matrix.postScale(wScale, hScale);
canvas.drawBitmap(curSketchData.backgroundBM, matrix, null);
// canvas.drawBitmap(curSketchData.backgroundBM, backgroundSrcRect, dstRect, null);
Log.d(TAG, "drawBackground:src= " + backgroundSrcRect.toString() + ";dst=" + backgroundDstRect.toString());
} else {
canvas.drawColor(Color.WHITE);
}
}

private void drawRecord(Canvas canvas) {
    drawRecord(canvas, true);
}

private void drawRecord(Canvas canvas, boolean isDrawBoard) {
    if (curSketchData != null) {
        for (PhotoRecord record : curSketchData.photoRecordList) {
            if (record != null)
                canvas.drawBitmap(record.bitmap, record.matrix, null);
        }
        if (isDrawBoard && editMode == EDIT_PHOTO && curPhotoRecord != null) {
            SCALE_MAX = curPhotoRecord.scaleMax;
            float[] photoCorners = calculateCorners(curPhotoRecord);//计算图片四个角点和中心点
            drawBoard(canvas, photoCorners);//绘制图形边线
            drawMarks(canvas, photoCorners);//绘制边角图片
        }
        for (StrokeRecord record : curSketchData.strokeRecordList) {
            int type = record.type;
            if (type == StrokeRecord.STROKE_TYPE_ERASER || type == StrokeRecord.STROKE_TYPE_DRAW || type == StrokeRecord.STROKE_TYPE_LINE) {
                canvas.drawPath(record.path, record.paint);
            } else if (type == STROKE_TYPE_CIRCLE) {
                canvas.drawOval(record.rect, record.paint);
            } else if (type == STROKE_TYPE_RECTANGLE) {
                canvas.drawRect(record.rect, record.paint);
            } else if (type == STROKE_TYPE_TEXT) {
                if (record.text != null) {
                    StaticLayout layout = new StaticLayout(record.text, record.textPaint, record.textWidth, Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
                    canvas.translate(record.textOffX, record.textOffY);
                    layout.draw(canvas);
                    canvas.translate(-record.textOffX, -record.textOffY);
                }
            }
        }
    }

}

//绘制图像边线(由于图形旋转或不一定是矩形,所以用Path绘制边线)
private void drawBoard(Canvas canvas, float[] photoCorners) {
    Path photoBorderPath = new Path();
    photoBorderPath.moveTo(photoCorners[0], photoCorners[1]);
    photoBorderPath.lineTo(photoCorners[2], photoCorners[3]);
    photoBorderPath.lineTo(photoCorners[4], photoCorners[5]);
    photoBorderPath.lineTo(photoCorners[6], photoCorners[7]);
    photoBorderPath.lineTo(photoCorners[0], photoCorners[1]);
    canvas.drawPath(photoBorderPath, boardPaint);
}

//绘制边角操作图标
private void drawMarks(Canvas canvas, float[] photoCorners) {
    float x;
    float y;
    x = photoCorners[0] - markerCopyRect.width() / 2;
    y = photoCorners[1] - markerCopyRect.height() / 2;
    markerCopyRect.offsetTo(x, y);
    canvas.drawBitmap(mirrorMarkBM, x, y, null);

    x = photoCorners[2] - markerDeleteRect.width() / 2;
    y = photoCorners[3] - markerDeleteRect.height() / 2;
    markerDeleteRect.offsetTo(x, y);
    canvas.drawBitmap(deleteMarkBM, x, y, null);

    x = photoCorners[4] - markerRotateRect.width() / 2;
    y = photoCorners[5] - markerRotateRect.height() / 2;
    markerRotateRect.offsetTo(x, y);
    canvas.drawBitmap(rotateMarkBM, x, y, null);

    x = photoCorners[6] - markerResetRect.width() / 2;
    y = photoCorners[7] - markerResetRect.height() / 2;
    markerResetRect.offsetTo(x, y);
    canvas.drawBitmap(resetMarkBM, x, y, null);
}

private float[] calculateCorners(PhotoRecord record) {
    float[] photoCornersSrc = new float[10];//0,1代表左上角点XY,2,3代表右上角点XY,4,5代表右下角点XY,6,7代表左下角点XY,8,9代表中心点XY
    float[] photoCorners = new float[10];//0,1代表左上角点XY,2,3代表右上角点XY,4,5代表右下角点XY,6,7代表左下角点XY,8,9代表中心点XY
    RectF rectF = record.photoRectSrc;
    photoCornersSrc[0] = rectF.left;
    photoCornersSrc[1] = rectF.top;
    photoCornersSrc[2] = rectF.right;
    photoCornersSrc[3] = rectF.top;
    photoCornersSrc[4] = rectF.right;
    photoCornersSrc[5] = rectF.bottom;
    photoCornersSrc[6] = rectF.left;
    photoCornersSrc[7] = rectF.bottom;
    photoCornersSrc[8] = rectF.centerX();
    photoCornersSrc[9] = rectF.centerY();
    curPhotoRecord.matrix.mapPoints(photoCorners, photoCornersSrc);
    return photoCorners;
}

private float getMaxScale(RectF photoSrc) {
    return Math.max(getWidth(), getHeight()) / Math.max(photoSrc.width(), photoSrc.height());

// SCALE_MIN = SCALE_MAX / 5;
}

public void addStrokeRecord(StrokeRecord record) {
    curSketchData.strokeRecordList.add(record);
    invalidate();
}

private void touch_down(MotionEvent event) {
    downX = event.getX();
    downY = event.getY();
    if (editMode == EDIT_STROKE) {
        curSketchData.strokeRedoList.clear();
        curStrokeRecord = new StrokeRecord(strokeType);
        if (strokeType == STROKE_TYPE_ERASER) {
            strokePath = new Path();
            strokePath.moveTo(downX, downY);
            strokePaint.setColor(Color.WHITE);
            strokePaint.setStrokeWidth(eraserSize);
            curStrokeRecord.paint = new Paint(strokePaint); // Clones the mPaint object
            curStrokeRecord.path = strokePath;
        } else if (strokeType == STROKE_TYPE_DRAW || strokeType == STROKE_TYPE_LINE) {
            strokePath = new Path();
            strokePath.moveTo(downX, downY);
            curStrokeRecord.path = strokePath;
            strokePaint.setColor(strokeRealColor);
            strokePaint.setStrokeWidth(strokeSize);
            curStrokeRecord.paint = new Paint(strokePaint); // Clones the mPaint object
        } else if (strokeType == STROKE_TYPE_CIRCLE || strokeType == STROKE_TYPE_RECTANGLE) {
            RectF rect = new RectF(downX, downY, downX, downY);
            curStrokeRecord.rect = rect;
            strokePaint.setColor(strokeRealColor);
            strokePaint.setStrokeWidth(strokeSize);
            curStrokeRecord.paint = new Paint(strokePaint); // Clones the mPaint object
        } else if (strokeType == STROKE_TYPE_TEXT) {
            curStrokeRecord.textOffX = (int) downX;
            curStrokeRecord.textOffY = (int) downY;
            TextPaint tp = new TextPaint();
            tp.setColor(strokeRealColor);
            curStrokeRecord.textPaint = tp; // Clones the mPaint object
            textWindowCallback.onText(this, curStrokeRecord);
            return;
        }
        curSketchData.strokeRecordList.add(curStrokeRecord);
    } else if (editMode == EDIT_PHOTO) {
        float[] downPoint = new float[]{downX, downY};
        if (isInMarkRect(downPoint)) {// 先判操作标记区域
            return;
        }
        if (isInPhotoRect(curPhotoRecord, downPoint)) {//再判断是否点击了当前图片
            actionMode = ACTION_DRAG;
            return;
        }
        selectPhoto(downPoint);//最后判断是否点击了其他图片
    }
}

//judge click which photo,then can edit the photo
private void selectPhoto(float[] downPoint) {
    PhotoRecord clickRecord = null;
    for (int i = curSketchData.photoRecordList.size() - 1; i >= 0; i--) {
        PhotoRecord record = curSketchData.photoRecordList.get(i);
        if (isInPhotoRect(record, downPoint)) {
            clickRecord = record;
            break;
        }
    }
    if (clickRecord != null) {
        setCurPhotoRecord(clickRecord);
        actionMode = ACTION_DRAG;
    } else {
        actionMode = ACTION_NONE;
    }
}

private boolean isInMarkRect(float[] downPoint) {
    if (markerRotateRect.contains(downPoint[0], (int) downPoint[1])) {//判断是否在区域内
        actionMode = ACTION_ROTATE;
        return true;
    }
    if (markerDeleteRect.contains(downPoint[0], (int) downPoint[1])) {//判断是否在区域内
        curSketchData.photoRecordList.remove(curPhotoRecord);
        setCurPhotoRecord(null);
        actionMode = ACTION_NONE;
        return true;
    }
    if (markerCopyRect.contains(downPoint[0], (int) downPoint[1])) {//判断是否在区域内
        PhotoRecord newRecord = initPhotoRecord(curPhotoRecord.bitmap);
        newRecord.matrix = new Matrix(curPhotoRecord.matrix);
        newRecord.matrix.postTranslate(ScreenUtils.dip2px(mContext, 20), ScreenUtils.dip2px(mContext, 20));//偏移小段距离以分辨新复制的图片
        setCurPhotoRecord(newRecord);
        actionMode = ACTION_NONE;
        return true;
    }
    if (markerResetRect.contains(downPoint[0], (int) downPoint[1])) {//判断是否在区域内
        curPhotoRecord.matrix.reset();
        curPhotoRecord.matrix.setTranslate(getWidth() / 2 - curPhotoRecord.photoRectSrc.width() / 2,
                getHeight() / 2 - curPhotoRecord.photoRectSrc.height() / 2);
        actionMode = ACTION_NONE;
        return true;
    }
    return false;
}

private boolean isInPhotoRect(PhotoRecord record, float[] downPoint) {
    if (record != null) {
        float[] invertPoint = new float[2];
        Matrix invertMatrix = new Matrix();
        record.matrix.invert(invertMatrix);
        invertMatrix.mapPoints(invertPoint, downPoint);
        return record.photoRectSrc.contains(invertPoint[0], invertPoint[1]);
    }
    return false;
}


private void touch_move(MotionEvent event) {
    if (editMode == EDIT_STROKE) {
        if (strokeType == STROKE_TYPE_ERASER) {
            strokePath.quadTo(preX, preY, (curX + preX) / 2, (curY + preY) / 2);
        } else if (strokeType == STROKE_TYPE_DRAW) {
            strokePath.quadTo(preX, preY, (curX + preX) / 2, (curY + preY) / 2);
        } else if (strokeType == STROKE_TYPE_LINE) {
            strokePath.reset();
            strokePath.moveTo(downX, downY);
            strokePath.lineTo(curX, curY);
        } else if (strokeType == STROKE_TYPE_CIRCLE || strokeType == STROKE_TYPE_RECTANGLE) {
            curStrokeRecord.rect.set(downX < curX ? downX : curX, downY < curY ? downY : curY, downX > curX ? downX : curX, downY > curY ? downY : curY);
        } else if (strokeType == STROKE_TYPE_TEXT) {

        }
    } else if (editMode == EDIT_PHOTO && curPhotoRecord != null) {
        if (actionMode == ACTION_DRAG) {
            onDragAction(curX - preX, curY - preY);
        } else if (actionMode == ACTION_ROTATE) {
            onRotateAction(curPhotoRecord);
        } else if (actionMode == ACTION_SCALE) {
            mScaleGestureDetector.onTouchEvent(event);
        }
    }
    preX = curX;
    preY = curY;
}

private void onScaleAction(ScaleGestureDetector detector) {
    float[] photoCorners = calculateCorners(curPhotoRecord);
    //目前图片对角线长度
    float len = (float) Math.sqrt(Math.pow(photoCorners[0] - photoCorners[4], 2) + Math.pow(photoCorners[1] - photoCorners[5], 2));
    double photoLen = Math.sqrt(Math.pow(curPhotoRecord.photoRectSrc.width(), 2) + Math.pow(curPhotoRecord.photoRectSrc.height(), 2));
    float scaleFactor = detector.getScaleFactor();
    //设置Matrix缩放参数
    if ((scaleFactor < 1 && len >= photoLen * SCALE_MIN && len >= SCALE_MIN_LEN) || (scaleFactor > 1 && len <= photoLen * SCALE_MAX)) {
        Log.e(scaleFactor + "", scaleFactor + "");
        curPhotoRecord.matrix.postScale(scaleFactor, scaleFactor, photoCorners[8], photoCorners[9]);
    }
}

private void onRotateAction(PhotoRecord record) {
    float[] 啥orners = calculateCorners(record);
    //放大
    //目前触摸点与图片显示中心距离
    float a = (float) Math.sqrt(Math.pow(curX - 啥orners[8], 2) + Math.pow(curY - 啥orners[9], 2));
    //目前上次旋转图标与图片显示中心距离
    float b = (float) Math.sqrt(Math.pow(啥orners[4] - 啥orners[0], 2) + Math.pow(啥orners[5] - 啥orners[1], 2)) / 2;

    //设置Matrix缩放参数
    double photoLen = Math.sqrt(Math.pow(record.photoRectSrc.width(), 2) + Math.pow(record.photoRectSrc.height(), 2));
    if (a >= photoLen / 2 * SCALE_MIN && a >= SCALE_MIN_LEN && a <= photoLen / 2 * SCALE_MAX) {
        //这种计算方法可以保持旋转图标坐标与触摸点同步缩放
        float scale = a / b;
        record.matrix.postScale(scale, scale, 啥orners[8], 啥orners[9]);
    }

    //旋转
    //根据移动坐标的变化构建两个向量,以便计算两个向量角度.
    PointF preVector = new PointF();
    PointF curVector = new PointF();
    preVector.set(preX - 啥orners[8], preY - 啥orners[9]);//旋转后向量
    curVector.set(curX - 啥orners[8], curY - 啥orners[9]);//旋转前向量
    //计算向量长度
    double preVectorLen = getVectorLength(preVector);
    double curVectorLen = getVectorLength(curVector);
    //计算两个向量的夹角.
    double cosAlpha = (preVector.x * curVector.x + preVector.y * curVector.y)
            / (preVectorLen * curVectorLen);
    //由于计算误差,可能会带来略大于1的cos,例如
    if (cosAlpha > 1.0f) {
        cosAlpha = 1.0f;
    }
    //本次的角度已经计算出来。
    double dAngle = Math.acos(cosAlpha) * 180.0 / Math.PI;
    // 判断顺时针和逆时针.
    //判断方法其实很简单,这里的v1v2其实相差角度很小的。
    //先转换成单位向量
    preVector.x /= preVectorLen;
    preVector.y /= preVectorLen;
    curVector.x /= curVectorLen;
    curVector.y /= curVectorLen;
    //作curVector的逆时针垂直向量。
    PointF verticalVec = new PointF(curVector.y, -curVector.x);

    //判断这个垂直向量和v1的点积,点积>0表示俩向量夹角锐角。=0表示垂直,<0表示钝角
    float vDot = preVector.x * verticalVec.x + preVector.y * verticalVec.y;
    if (vDot > 0) {
        //v2的逆时针垂直向量和v1是锐角关系,说明v1在v2的逆时针方向。
    } else {
        dAngle = -dAngle;
    }
    record.matrix.postRotate((float) dAngle, 啥orners[8], 啥orners[9]);
}

/**
 * 获取p1到p2的线段的长度
 *
 * @return
 */
double getVectorLength(PointF vector) {
    return Math.sqrt(vector.x * vector.x + vector.y * vector.y);
}
private void onDragAction(float distanceX, float distanceY) {
    curPhotoRecord.matrix.postTranslate((int) distanceX, (int) distanceY);
}


private void touch_up() {
}


@NonNull
public Bitmap getResultBitmap() {
    Bitmap newBM = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
    Canvas canvas = new Canvas(newBM);
    canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));//抗锯齿
    //绘制背景
    drawBackground(canvas);
    drawRecord(canvas, false);
    canvas.save(Canvas.ALL_SAVE_FLAG);
    canvas.restore();
    return newBM;
}

@NonNull
public void createCurThumbnailBM() {
    curSketchData.thumbnailBM = getThumbnailResultBitmap();
}

@NonNull
public Bitmap getThumbnailResultBitmap() {
    return BitmapUtils.createBitmapThumbnail(getResultBitmap(), true, ScreenUtils.dip2px(mContext, 200), ScreenUtils.dip2px(mContext, 200));
}
/*
 * 删除一笔
 */
public void undo() {
    if (curSketchData.strokeRecordList.size() > 0) {
        curSketchData.strokeRedoList.add(curSketchData.strokeRecordList.get(curSketchData.strokeRecordList.size() - 1));
        curSketchData.strokeRecordList.remove(curSketchData.strokeRecordList.size() - 1);
        invalidate();
    }
}


/*
 * 撤销
 */
public void redo() {
    if (curSketchData.strokeRedoList.size() > 0) {
        curSketchData.strokeRecordList.add(curSketchData.strokeRedoList.get(curSketchData.strokeRedoList.size() - 1));
        curSketchData.strokeRedoList.remove(curSketchData.strokeRedoList.size() - 1);
    }
    invalidate();
}


public int getRedoCount() {
    return curSketchData.strokeRedoList != null ? curSketchData.strokeRedoList.size() : 0;
}


public int getRecordCount() {
    return (curSketchData.strokeRecordList != null && curSketchData.photoRecordList != null) ? curSketchData.strokeRecordList.size() + curSketchData.photoRecordList.size() : 0;
}

public int getStrokeRecordCount() {
    return curSketchData.strokeRecordList != null ? curSketchData.strokeRecordList.size() : 0;
}


public int getStrokeSize() {
    return Math.round(this.strokeSize);
}


public void setSize(int size, int eraserOrStroke) {
    switch (eraserOrStroke) {
        case STROKE_TYPE_DRAW:
            strokeSize = size;
            break;
        case STROKE_TYPE_ERASER:
            eraserSize = size;
            break;
    }

}


public void erase() {
    // 先判断是否已经回收
    for (PhotoRecord record : curSketchData.photoRecordList) {
        if (record != null && record.bitmap != null && !record.bitmap.isRecycled()) {
            record.bitmap.recycle();
            record.bitmap = null;
        }
    }
    if (curSketchData.backgroundBM != null && !curSketchData.backgroundBM.isRecycled()) {
        // 回收并且置为null
        curSketchData.backgroundBM.recycle();
        curSketchData.backgroundBM = null;
    }
    curSketchData.strokeRecordList.clear();
    curSketchData.photoRecordList.clear();
    curSketchData.strokeRedoList.clear();
    curPhotoRecord = null;
    System.gc();
    invalidate();
}


public void setOnDrawChangedListener(OnDrawChangedListener listener) {
    this.onDrawChangedListener = listener;
}

public interface OnDrawChangedListener {

    public void onDrawChanged();
}

public void addPhotoByPath(String path) {
    Bitmap sampleBM = getSampleBitMap(path);
    if (sampleBM != null) {
        PhotoRecord newRecord = initPhotoRecord(sampleBM);
        setCurPhotoRecord(newRecord);
    } else {
        Toast.makeText(mContext, "图片文件路径有误!", Toast.LENGTH_SHORT).show();
    }
}

public void setBackgroundByPath(String path) {
    Bitmap sampleBM = getSampleBitMap(path);
    if (sampleBM != null) {
        curSketchData.backgroundBM = sampleBM;
        backgroundSrcRect = new Rect(0, 0, curSketchData.backgroundBM.getWidth(), curSketchData.backgroundBM.getHeight());
        backgroundDstRect = new Rect(0, 0, mWidth, mHeight);
        invalidate();
    } else {
        Toast.makeText(mContext, "图片文件路径有误!", Toast.LENGTH_SHORT).show();
    }
}

public Bitmap getSampleBitMap(String path) {
    Bitmap sampleBM = null;
    Log.i(TAG,"path:"+path);
    Log.i(TAG,"environment"+Environment.getExternalStorageDirectory().toString());
    if (isContainsPath(path)) {
        sampleBM = getSDCardPhoto(path);
    } else {
        sampleBM = getAssetsPhoto(path);
    }
    return sampleBM;
}

/**
 * 跟所有设备地址进行比较
 * @param path
 * @return
 */
private boolean isContainsPath(String path) {
    for (String mPath : storagePaths) {
        if (path.contains(mPath)) {
            return true;
        }
    }
    return false;
}

/**
 * 获取所有存储设备地址
 * @return
 */
private List<String> getAllMount(){
    List<String> strMeadiaPath = new ArrayList<>();
    int countC = 0;
    StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
    try
    {
        String[] paths = (String[]) sm.getClass().getMethod("getVolumePaths").invoke(sm);
        for(int i = 0; i < paths.length; i++)
        {
            String status = (String) sm.getClass().getMethod("getVolumeState", String.class).invoke(sm, paths[i]);
            if(status.equals(android.os.Environment.MEDIA_MOUNTED))
            {
                Log.i(TAG,"path:"+paths[i]);
                strMeadiaPath.add(countC, paths[i]);
                countC++;
            }
        }
    }
    catch (Exception e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return strMeadiaPath;
}

@NonNull
private PhotoRecord initPhotoRecord(Bitmap bitmap) {
    PhotoRecord newRecord = new PhotoRecord();
    newRecord.bitmap = bitmap;
    newRecord.photoRectSrc = new RectF(0, 0, newRecord.bitmap.getWidth(), newRecord.bitmap.getHeight());
    newRecord.scaleMax = getMaxScale(newRecord.photoRectSrc);//放大倍数
    newRecord.matrix = new Matrix();
    newRecord.matrix.postTranslate(getWidth() / 2 - bitmap.getWidth() / 2, getHeight() / 2 - bitmap.getHeight() / 2);
    return newRecord;
}

private void setCurPhotoRecord(PhotoRecord record) {
    curSketchData.photoRecordList.remove(record);
    curSketchData.photoRecordList.add(record);
    curPhotoRecord = record;
    invalidate();
}

public Bitmap getSDCardPhoto(String path) {
    File file = new File(path);
    if (file.exists()) {
        return BitmapUtils.decodeSampleBitMapFromFile(mContext, path, simpleScale);
    } else {
        return null;
    }
}

public Bitmap getAssetsPhoto(String path) {
    return BitmapUtils.getBitmapFromAssets(mContext, path);
}

public void setEditMode(int editMode) {
    this.editMode = editMode;
    invalidate();
}

public int getEditMode() {
    return editMode;
}

}
`

画板数据传输以及清除

如果我想要用在多个客户端使用画板进行沟通,那么发送一个什么样的画板数据比较好呢?还有就是在对方的客户端上如何显示多个人的绘画呢?其中一个人希望清空本人画板,那么如何清空接收端的数据呢?

选择背景和选择图片的问题

点击所有图片时,弹出框边框颜色会越变越深,这是多重绘制导致的还是什么问题?然后单独将碎片和布局引出后,点击所有图片app崩溃,并且logcat没有出现任何异常信息,无法针对性解决。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.