保存时从自定义绘图视图裁剪透明 space

crop transparent space from custom drawing View when saving

我正在开发一个用户可以在屏幕上绘图的应用程序。

我有一个名为 DrawingView 的自定义 View,它使用 Canvas 对象进行绘制。

我能够成功绘制,并将绘图保存为 PNG

问题

我 运行 遇到的问题是,当我保存绘图时,它会保存整个屏幕,绘图周围的所有透明 space。

例如:

期望的结果

我希望实现的是一种从绘图中裁剪透明 space 的方法。

例如:

绘图视图

public class DrawingView extends View {
    private final static String TAG = DrawingView.class.getSimpleName();

    private static final float TOLERANCE = 5f;
    private static final float STROKE_WIDTH = 15f;

    private final ArrayList<Pair<Path, Paint>> mPaths = new ArrayList<>();

    private Bitmap mBitmap;
    private Canvas mCanvas;
    private Path mPath;
    private Paint mPaint;
    private float mX;
    private float mY;



    public DrawingView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        mPath = new Path();
        mPaint = new Paint(Paint.DITHER_FLAG);

        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeWidth(STROKE_WIDTH);

        mPaths.add(new Pair<>(mPath, new Paint(mPaint)));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
        mCanvas = new Canvas(mBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBitmap, 0, 0, mPaint);

        if (!mPaths.isEmpty()) {
            canvas.drawPath(mPaths.get(mPaths.size() - 1).first, mPaths.get(mPaths.size() - 1).second);
        }
    }

    public void save(String path) {
        try {
            FileOutputStream writer = new FileOutputStream(path);
            mBitmap.compress(Bitmap.CompressFormat.PNG, 100, writer);
            writer.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void startTouch(float x, float y) {
        mPath.reset();
        mPath.moveTo(x, y);

        mX = x;
        mY = y;

        mPaths.add(new Pair<>(mPath, new Paint(mPaint)));
        invalidate();
    }

    private void moveTouch(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(x - mY);

        if (dx >= TOLERANCE || dy >= TOLERANCE) {
            mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
        invalidate();
    }

    private void upTouch() {
        mPath.lineTo(mX, mY);
        mCanvas.drawPath(mPath, mPaint);

        mPaths.add(new Pair<>(mPath, new Paint(mPaint)));
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                startTouch(x, y);
                break;

            case MotionEvent.ACTION_MOVE:
                moveTouch(x, y);
                break;

            case MotionEvent.ACTION_UP:
                upTouch();
                break;
        }
        return true;
    }

    public void clear() {
        ArrayList<Pair<Path, Paint>> paths = new ArrayList<>();

        for (Pair pair : mPaths) {
            paths.add(pair);
        }
        mPaths.removeAll(paths);

        mBitmap = Bitmap.createBitmap(mCanvas.getWidth(), mCanvas.getHeight(), Bitmap.Config.ARGB_4444);
        mCanvas = new Canvas(mBitmap);
        invalidate();
    }
}

只是一个想法,但也许您可以存储正在绘制的每个点的 x 和 y。然后,您可以在绘制点列表中取最小的 x、最大的 x、最小的 y 和最大的 y。最后,使用这些数据,您可能能够找到所需图像的宽度、高度和位置(这就像在您绘制的图像周围创建矩形)。从那里,希望你能找到一种方法来调整屏幕大小或使用来自点的数据保存图像。

我知道这不是代码解决方案的代码,但这只是我认为您应该尝试的一个想法!

解决方案

所以我在绘制图像时使用 class RectF 来跟踪我的最小和最大 X,Y 坐标。

我在构造函数中创建了一个名为 RectF 的对象:mDrawnAreaRect 然后我添加了一个方法来查找最小和最大 X、Y 值,名为 findMinMax:

 private void findMinMax(int x, int y) {
    if (x < mDrawnAreaRect.left) { //min x
        mDrawnAreaRect.left = x;
    }

    if (y >  mDrawnAreaRect.top) { //max y
        mDrawnAreaRect.top = y;
    }

    if (x >  mDrawnAreaRect.right) { //max x
        mDrawnAreaRect.right = x;
    }

    if (y <  mDrawnAreaRect.bottom) { //min y
        mDrawnAreaRect.bottom = y;
    }
}

然后我在 Override onTouchEvent 方法的底部实现了这个方法,如下所示:

@Override
public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX();
    float y = event.getY();

    switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:
            startTouch(x, y);
            break;

        case MotionEvent.ACTION_MOVE:
            moveTouch(x, y);
            break;

        case MotionEvent.ACTION_UP:
            upTouch();
            break;
    }

    findMinMax((int) x, (int) y); //IMPLEMENTED HERE
    return true;
}

最后,当我保存位图时,我使用 mDrawnAreaRect 中的左、上、右和下值在一个名为 cropBitmap 的方法中裁剪出透明的 space:

private Bitmap cropBitmap() {
    int minX = mDrawnAreaRect.left <= 0 ? 0 : (int) mDrawnAreaRect.left;
    int maxY = mDrawnAreaRect.top > mBitmap.getHeight() ? mBitmap.getHeight() : (int) mDrawnAreaRect.top;
    int maxX = mDrawnAreaRect.right > mBitmap.getWidth() ? mBitmap.getWidth() : (int) mDrawnAreaRect.right;
    int minY = mDrawnAreaRect.bottom <= 0 ? 0 : (int) mDrawnAreaRect.bottom;

    int width = maxX - minX;
    int height = maxY - minY;

    width = width > mBitmap.getWidth() ? mBitmap.getWidth() : width;
    height = height > mBitmap.getHeight() ? mBitmap.getHeight() : height;

    return Bitmap.createBitmap(mBitmap, minX, minY, width, height);
}