Android Drawing Fill Tool

2019-04-28 16:03发布

问题:

I am making a drawing app for android and I need some help adding a fill tool.

I want the tool to flood fill, and to behave like it would in Microsoft Paint, but on a phone.

I have a custom view that draws a path on a canvas. I draw different paths for different pens and brushes, and I allow users to pick line thickness and color.

When I do:

paint.setStyle(Paint.Style.FILL);

and I paint, I don't get a fill how I want.

I have gotten some suggestions to use the "Flood Fill Algorithm", but I can't figure out how to implement it in my code.

Where could I go to see an example of what I am trying to do? Does anyone have sample code to show me how I could make the tool work with my android view?

EDIT:

CartoonView.java:

    public class CartoonView extends View {
        ArrayList<Paint> paints = new ArrayList<Paint>();
        ArrayList<Path> paths = new ArrayList<Path>();
        int color;
        int thickness;
        boolean pencilSelected;

    public boolean isPencilSelected() {
        return pencilSelected;
    }

    public void setPencilSelected(boolean pencilSelected) {
        this.pencilSelected = pencilSelected;
    }

    public ArrayList<Paint> getPaints() {
        return paints;
    }

    public void setPaints(ArrayList<Paint> paints) {
        this.paints = paints;
    }

    public ArrayList<Path> getPaths() {
        return paths;
    }

    public void setPaths(ArrayList<Path> paths) {
        this.paths = paths;
    }

    public int getThickness() {
        return thickness;
    }

    public int getColor() {
        return color;
    }

    public CartoonView(Context context, AttributeSet attrs) {
        super(context, attrs);
        color = Color.BLACK;
        thickness = 3;
        pencilSelected = true;
        createPaint();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (Path path : paths) {
            canvas.drawPath(path, paints.get(paths.indexOf(path)));
        }
    }

    public void setPaintColor(int newColor) {
        color = newColor;
        createPaint();
    }

    public void setPaintThickness(int newThickness) {
        thickness = newThickness;
        createPaint();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (this.isEnabled()) {
            Path path;
            if (paths.size() == 0) {
                path = new Path();

                Paint paint = new Paint();
                paint.setAntiAlias(true);
                paint.setStrokeWidth(5f);
                paint.setStyle(Paint.Style.STROKE);
                paint.setStrokeJoin(Paint.Join.ROUND);
                paint.setColor(color);
                paint.setStrokeWidth(thickness);

                thickness = (int) paint.getStrokeWidth();

                paths.add(path);
                paints.add(paint);
            } else {
                path = paths.get(paths.size() - 1);
            }
            float eventX = event.getX();
            float eventY = event.getY();
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                path.moveTo(eventX, eventY);
                return true;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(eventX, eventY);
                break;
            default:
                return true;
            }
            invalidate();
        }
        return true;
    }

    public void createPaint() {
        Path path = new Path();

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(5f);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setColor(color);
        paint.setStrokeWidth(thickness);

        paths.add(path);
        paints.add(paint);
    }

    public void clearView(){
        paths.clear();
        paints.clear();
        invalidate();
    }
}

回答1:

I completely agree with the some of the commentators who said about Flood Fill Algorithm.

Below is the function which works as what you want. Just try it:

private void FloodFill(Bitmap bmp, Point pt, int targetColor, int replacementColor){
Queue<Point> q = new LinkedList<Point>();
q.add(pt);
while (q.size() > 0) {
    Point n = q.poll();
    if (bmp.getPixel(n.x, n.y) != targetColor)
        continue;

    Point w = n, e = new Point(n.x + 1, n.y);
    while ((w.x > 0) && (bmp.getPixel(w.x, w.y) == targetColor)) {
        bmp.setPixel(w.x, w.y, replacementColor);
        if ((w.y > 0) && (bmp.getPixel(w.x, w.y - 1) == targetColor))
            q.add(new Point(w.x, w.y - 1));
        if ((w.y < bmp.getHeight() - 1)
                && (bmp.getPixel(w.x, w.y + 1) == targetColor))
            q.add(new Point(w.x, w.y + 1));
        w.x--;
    }
    while ((e.x < bmp.getWidth() - 1)
            && (bmp.getPixel(e.x, e.y) == targetColor)) {
        bmp.setPixel(e.x, e.y, replacementColor);

        if ((e.y > 0) && (bmp.getPixel(e.x, e.y - 1) == targetColor))
            q.add(new Point(e.x, e.y - 1));
        if ((e.y < bmp.getHeight() - 1)
                && (bmp.getPixel(e.x, e.y + 1) == targetColor))
            q.add(new Point(e.x, e.y + 1));
        e.x++;
    }
}}

You can also read some information regarding Flood fill on link: Link1, Link2 and Link3

Hope you got answer to your question. Please let me know i can help you in another way or you have doubt in above answer.

Enjoy Coding... :)

Updated with more information:

Above functiona will work on bases of the FloodFill algorithm. What it will exactly do is, It will detect the pixcel point and color on that pixel whereever you touch. Then it will fill the color by pixel to pixel by the selected color till any pixel dont have different color then it have currently.

I hope it will help you.



回答2:

private void setupDrawing(){

        drawPath = new Path();
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(50);
        drawPaint.setStyle(Paint.Style.STROKE);

     // here i use round round u may use anther option also :) 

        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        canvasPaint = new Paint(Paint.DITHER_FLAG);
 }