在 Android Canvas 中使用撤消/重做操作自定义画笔 [英] Custom Brush with Undo/Redo operation in Android Canvas

查看:73
本文介绍了在 Android Canvas 中使用撤消/重做操作自定义画笔的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个带有自定义画笔的画布绘图应用程序 &撤消/重做操作.首先,我的代码在不使用自定义画笔(包括撤消/重做操作)的情况下完美运行.根据这个答案

现在的问题是,

  1. 撤消、重做操作不起作用,每当移动触摸点时,自定义画笔都会一遍又一遍地绘制.

    问:如何使撤销/重做操作生效?

  2. 定制的刷子不光滑.现在它们看起来粗糙和做作.

    问.如何使用自定义笔触使绘画流畅自然?

在此处查看我的示例代码,

public class DrawingView extends View {私有上下文ctx;私有 ArrayList<路径>路径 = 新的 ArrayList();私有 ArrayList<路径>undonePaths = new ArrayList();私有地图<路径,浮动>BrushMap = new HashMap();私有地图<路径,列表<Vector2>>customBrushMap = new HashMap>();私有位图 mBitmapBrush;私有 Vector2 mBitmapBrushDimensions;私人列表mPositions = new ArrayList(100);私人布尔 isCustomBrush = false;私有 int selectedColor;私人浮动刷大小,lastBrushSize;私人浮动 mX, my;私有静态最终浮动 TOUCH_TOLERANCE = 4;私有路径 drawPath;私人油漆 drawPaint,canvasPaint;私有 intpaintColor = 0xFF660000,paintAlpha = 255;私人画布 drawCanvas;私有位图画布位图;私有静态最终类 Vector2 {公共向量2(浮动x,浮动y){this.x = x;这.y = y;}公开最终浮动 x;公开最终浮动 y;}公共绘图视图(上下文上下文,属性集属性){超级(上下文,属性);ctx = 上下文;设置绘图();}私有无效 setupDrawing() {BrushSize = getResources().getInteger(R.integer.small_size);lastBrushSize = BrushSize;drawPath = 新路径();drawPaint = new Paint();drawPaint.setColor(paintColor);drawPaint.setAntiAlias(true);drawPaint.setDither(true);drawPaint.setStrokeWidth(brushSize);drawPaint.setStyle(Paint.Style.STROKE);drawPaint.setStrokeJoin(Paint.Join.ROUND);drawPaint.setStrokeCap(Paint.Cap.ROUND);canvasPaint = new Paint(Paint.DITHER_FLAG);}@覆盖protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);drawCanvas = new Canvas(canvasBitmap);}私人无效touch_start(浮动x,浮动y){undonePaths.clear();drawPath.reset();drawPath.moveTo(x, y);mX = x;米 = y;}私人无效touch_move(浮动x,浮动y){float dx = Math.abs(x - mX);float dy = Math.abs(y - my);if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {drawPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);mX = x;米 = y;}customBrushMap.put(drawPath, mPositions);}私人无效touch_up(){drawPath.lineTo(mX, my);drawCanvas.drawPath(drawPath, drawPaint);路径.添加(绘制路径);BrushMap.put(drawPath,brushSize);drawPath = 新路径();drawPath.reset();无效();}公共无效 onClickUndo() {如果 (paths.size() > 0) {undonePaths.add(paths.remove(paths.size() - 1));无效();}}公共无效 onClickRedo() {如果 (undonePaths.size() > 0) {path.add(undonePaths.remove(undonePaths.size() - 1));无效();}}@覆盖公共布尔 onTouchEvent(MotionEvent 事件){//检测用户触摸浮动 x = event.getX();浮动 y = event.getY();开关 (event.getAction() & MotionEvent.ACTION_MASK) {案例 MotionEvent.ACTION_DOWN:触摸开始(x,y);无效();休息;案例 MotionEvent.ACTION_MOVE:触摸移动(x,y);如果(isCustomBrush){mPositions.add(new Vector2(x - mBitmapBrushDimensions.x/2, y - mBitmapBrushDimensions.y/2));}触摸移动(x,y);无效();休息;案例 MotionEvent.ACTION_POINTER_DOWN:无效();休息;案例 MotionEvent.ACTION_UP:润色();无效();休息;}返回真;}@覆盖受保护的无效 onDraw(画布画布){画布.save();对于(路径 p:路径){drawPaint.setColor(colorsMap.get(p));drawPaint.setShader(shaderMap.get(p));drawPaint.setStrokeWidth(brushMap.get(p));drawPaint.setAlpha(opacityMap.get(p));如果(isCustomBrush){如果(customBrushMap.get(p)!= null){for (Vector2 pos : customBrushMap.get(p)) {油漆油漆 = 新油漆();ColorFilter filter = new PorterDuffColorFilter(selectedColor, PorterDuff.Mode.SRC_IN);Paint.setColorFilter(过滤器);canvas.drawBitmap(mBitmapBrush, pos.x, pos.y, 油漆);}}} 别的 {canvas.drawPath(p, drawPaint);drawPaint.setColor(selectedColor);drawPaint.setStrokeWidth(brushSize);canvas.drawPath(drawPath, drawPaint);}}canvas.restore();}公共无效setCustomBrush(活动活动,字符串customBrush){isCustomBrush = true;无效();int patternID = getResources().getIdentifier(customBrush, drawable", com.androidapp.drawingstutorial");mBitmapBrush = BitmapFactory.decodeResource(getResources(), patternID);mBitmapBrushDimensions = new Vector2(mBitmapBrush.getWidth(), mBitmapBrush.getHeight());}}

解决方案

为了让你的画笔看起来更平滑",不是 100% 确定这是什么意思,但你需要收紧你正在使用的图像上的点为您的自定义画笔.使它们更紧凑将使边缘看起来更光滑.

至于撤销重做,在你的 touchUp 方法中你永远不会只添加到 customBrushMap 中的 BrushMap.因此在 ondraw 中没有什么可绘制的,因为该地图将始终为空.

I want to implement a canvas drawing application with custom brush & undo/redo operation. First of all my code works perfectly without using the custom brush (including the undo/redo operation). According to this answer How to make custom brush for canvas in android? I used simple image spikes for bitmap draw.

Now the issue are,

  1. The undo, redo operation doesn't works, custom brush paints over and over again whenever moving the touch points.

    Q: How to make the undo/redo operation work?

  2. The custom brush stokers aren't smooth as they should. Right now they looks rough and artificial.

    Q. How to make the painting smooth and natural with custom brush strokes?

Check my sample code here,

public class DrawingView extends View {

    private Context ctx;

    private ArrayList<Path> paths = new ArrayList<Path>();
    private ArrayList<Path> undonePaths = new ArrayList<Path>();

    private Map<Path, Float> brushMap = new HashMap<Path, Float>();
    private Map<Path, List<Vector2>> customBrushMap = new HashMap<Path, List<Vector2>>();
    
    private Bitmap mBitmapBrush;
    private Vector2 mBitmapBrushDimensions;
    private List<Vector2> mPositions = new ArrayList<Vector2>(100);
    private boolean isCustomBrush = false;

    private int selectedColor;
    private float brushSize, lastBrushSize;

    private float mX, mY;
    private static final float TOUCH_TOLERANCE = 4;

    private Path drawPath;
    private Paint drawPaint, canvasPaint;
    private int paintColor = 0xFF660000, paintAlpha = 255;
    private Canvas drawCanvas;
    private Bitmap canvasBitmap;

    private static final class Vector2 {
        public Vector2(float x, float y) {
            this.x = x;
            this.y = y;
        }

        public final float x;
        public final float y;
    }

    public DrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        ctx = context;
        setupDrawing();
    }

    private void setupDrawing() {
        brushSize = getResources().getInteger(R.integer.small_size);
        lastBrushSize = brushSize;

        drawPath = new Path();
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setDither(true);
        drawPaint.setStrokeWidth(brushSize);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        canvasPaint = new Paint(Paint.DITHER_FLAG);
    }

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

    private void touch_start(float x, float y) {
        undonePaths.clear();
        drawPath.reset();
        drawPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

    private void touch_move(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
            drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }
        customBrushMap.put(drawPath, mPositions);
    }

    private void touch_up() {
        drawPath.lineTo(mX, mY);
        drawCanvas.drawPath(drawPath, drawPaint);
        paths.add(drawPath);
        brushMap.put(drawPath, brushSize);
        drawPath = new Path();
        drawPath.reset();
        invalidate();
    }

    public void onClickUndo() {
        if (paths.size() > 0) {
            undonePaths.add(paths.remove(paths.size() - 1));
            invalidate();
        }
    }

    public void onClickRedo() {
        if (undonePaths.size() > 0) {
            paths.add(undonePaths.remove(undonePaths.size() - 1));
            invalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        //detect user touch
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction() & MotionEvent.ACTION_MASK) {

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

            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                if (isCustomBrush) {
                mPositions.add(new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2));
                }
                touch_move(x, y);
                invalidate();
                break;

            case MotionEvent.ACTION_POINTER_DOWN:
                invalidate();
                break;

            case MotionEvent.ACTION_UP:
                touch_up();
                invalidate();
                break;
        }

        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        for (Path p : paths) {

        drawPaint.setColor(colorsMap.get(p));
        drawPaint.setShader(shaderMap.get(p));
        drawPaint.setStrokeWidth(brushMap.get(p));
        drawPaint.setAlpha(opacityMap.get(p));

        if (isCustomBrush) {
            if (customBrushMap.get(p) != null) {
                for (Vector2 pos : customBrushMap.get(p)) {
                    Paint paint = new Paint();
                    ColorFilter filter = new PorterDuffColorFilter(selectedColor, PorterDuff.Mode.SRC_IN);
                    paint.setColorFilter(filter);
                    canvas.drawBitmap(mBitmapBrush, pos.x, pos.y, paint);
                }
            }
        } else {
            canvas.drawPath(p, drawPaint);
            drawPaint.setColor(selectedColor);
            drawPaint.setStrokeWidth(brushSize);
            canvas.drawPath(drawPath, drawPaint);
        }
    }
        canvas.restore();
    }

    public void setCustomBrush(Activity activity, String customBrush) {
        isCustomBrush = true;
        invalidate();
        int patternID = getResources().getIdentifier(customBrush, "drawable", "com.androidapp.drawingstutorial");
        mBitmapBrush = BitmapFactory.decodeResource(getResources(), patternID);
        mBitmapBrushDimensions = new Vector2(mBitmapBrush.getWidth(), mBitmapBrush.getHeight());
    }
}

解决方案

To make your brush look "smoother" not 100% sure what is meant by that, but you would need to tighten up the dots on the image you are using for your custom brush. Making them more compact will make the edges look smoother.

As for undo redo, in your touchUp method your never adding to customBrushMap only brushMap. So in the ondraw there is nothing to draw since that map will always be empty.

这篇关于在 Android Canvas 中使用撤消/重做操作自定义画笔的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆