Erase paths that are redrawn out of a List - android

I've been working on a drawing app for Android lately and I am now facing a problem I cannot solve.
The idea is to be able to draw on two drawing layers. When changing to the 2nd layer you only see the drawings of this layer, while being in the 1st layer you see both layers with the 2nd being slightly transparent.
To do this I have two lists which save the drawn paths and paints.
case MotionEvent.ACTION_DOWN:
//save a path to the list
Paint strokePaint=new Paint();
Path strokePath=new Path();
setupDrawingPaint(strokePaint); //some initialization
stroke = new Stroke(strokePath, strokePaint); //object to save the paths
stroke.movePath(touchX, touchY);
if(drawingLayer==1){
strokeListL1.add(stroke);
}
else{
strokeListL2.add(stroke);
}
//draw the path
drawingPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
stroke.linePath(touchX, touchY);
drawingPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
canvas.drawPath(drawingPath, drawingPaint);
drawingPath.reset()
break;
invalidate();
Now in the onDraw()-Method the path is drawn normally except when the layer was changed, then the stroke objects from the list are drawn.
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
//draw the layer(s)
if(layerChanged)
{
if(drawingLayer==1){
for(Stroke stroke : strokeListL1)
canvas.drawPath(stroke.getPath(), stroke.getPaint());
for(Stroke stroke : strokeListL2){
Paint paint = stroke.getPaint();
paint.setAlpha(50);
canvas.drawPath(stroke.getPath(), paint);
}
}
else{
for(Stroke stroke : strokeListL2){
Paint paint = stroke.getPaint();
paint.setAlpha(255);
canvas.drawPath(stroke.getPath(), paint);
}
}
}
else
canvas.drawPath(drawingPath, drawingPaint);
layerChanged=false;
And here start the problems. As long as I dont change the layers erasing is working fine but as soon as i do change the layers instead of erasing it just clears the whole canvas.
The erase method looks like this:
public void setErase(boolean erase){
eraseMode=erase;
if(eraseMode)
drawingPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
else
drawingPaint.setXfermode(null);
}
If I do some erasing before a layer change the "erasing strokes" will be drawn in a color too, once i change back to that layer. I am not sure if i maneuvered myself into a dead end or if I am just not getting it. Hope you guys can help me out.
So far

Okay so this error was dumb. The problem was the Paint object being reinitialized everytime a new Path is drawn by the User. That, of course, will unset the set Xfermode.
case MotionEvent.ACTION_DOWN:
Paint strokePaint=new Paint();
The solution is to give the current Paint object to the constructor.
Paint strokePaint=new Paint(drawingPaint);

Related

Canvas bitmap is not being saved

i am new to android canvas and i failed to get solution of my problem,
the problem is:
I am using canvas in two modes AvoidXfermode.Mode.TARGET(to remove occurence of a single color) and PorterDuff.Mode.CLEAR(to erase a part of my canvas).
everything independently is working perfect
but in my code i want to use both of them in such a way that when i choose i want to erase a particular color it should erase it, and my code is doing so,
but after removing that particular color when i am switching my mode to eraser mode...everything which was deleted using AvoidXfermode comes back.
i am doing these things on a bitmap,
my onDraw method is:
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(DrawBitmap, 0, 0, DrawBitmapPaint);
if (flag != 1) {
setDrawingCacheEnabled(true);
for (Path p : paths) {
canvas.drawPath(p, mPaint);
Bitmap bitmap = getDrawingCache();
DrawBitmap = bitmap;
//
}
//canvas.setBitmap(bitmap.isMutable() ? bitmap : bitmap.copy(Bitmap.Config.ARGB_8888,true));
} else if (flag == 1) {
if (touchx > mCanvas.getWidth() || touchy > mCanvas.getHeight() || touchx < 0 || touchy < 0) {
return;
}
for (Path p : paths) {
color = DrawBitmap.getPixel(touchx, touchy);
mPaint.setXfermode(new AvoidXfermode(color, 100, AvoidXfermode.Mode.TARGET));
mPaint.setColor(Color.TRANSPARENT);
canvas.drawPaint(mPaint);
setDrawingCacheEnabled(true);
Bitmap bitmap = getDrawingCache();
DrawBitmap = bitmap;
}
}
}
everything is declared in onDraw for now just for the this question,
and the code where i switch my mode is:
case R.id.erase:
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaint.setColor(Color.TRANSPARENT);
mPaint.setStrokeWidth(40);
flag = 0;
break;
case R.id.DELETE:
flag = 1;
break;
the code where i am creating my canvas is:
DrawBitmap = BitmapFactory.decodeResource(MainActivity.this.getResources(), R.drawable.sample);
DrawBitmap = DrawBitmap.copy(Bitmap.Config.ARGB_8888, true);
mCanvas = new Canvas(DrawBitmap);
mPath = new Path();
paths.add(mPath);
DrawBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
i am wrong somewhere,but i don't know where, any suggestions would be helpful, thanks in advance.
Bitmap bitmap = getDrawingCache();
DrawBitmap = bitmap;
Your logic is a lot complicated. I specifically mean the above lines of code. You have mCanvas that is backed up with the DrawBitmap bitmap. Then you have the canvas of onDraw() which is backed by it's own bitmap. The thing you need to understand is whatever you draw in DrawBitmap stays or as you would call it "saved". Anything that is drawn via canvas is subjected to change during the next call to onDraw().
So if you want to save something in DrawBitmap use the mCanvas. The moment you want to save the state of the user's drawing, draw it in mCanvas. You could just draw everything on mCanvas and at the end of onDraw(), you can call canvas.drawBitmap(DrawBitmap, 0, 0, DrawBitmapPaint); This way everything that is done by the user is saved every step of the way.

How to draw multiple texts on canvas

I've created a drawing app and now I'm trying to allow the user to add text to the drawing. The text will be placed anywhere on the screen where the user touches with his finger. With the following code, the text could be drawn on screen with the drawing. However, this is only done once. The moment I tap on the screen, the previous text disappears and the new text is placed at my current finger position.
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(canvasColor);
for(Pair<Path, Paint> p : paths){
canvas.drawPath(p.first, p.second);
}
canvas.drawPath(drawPath, drawPaint);
if(textCB && !pbCol)
{
tPaint = new Paint();
tPaint.setTextSize(textSize);
tPaint.setStyle(Paint.Style.FILL);
tPaint.setColor(tColor);
canvas.drawText(addText, tx, ty, tPaint);
}
}
What can I do to allow multiple text to be drawn on the canvas?
Ok, I found a way to do this. After drawing the text, I got the bitmap from getDrawingCache, then drew the bitmap onto the canvas.

Can I draw alpha using just one bitmap?

I have the following code to draw on a canvas. That code is taken from SO and Android SDK demos and I have skimmed it down to better explain my problem. The code is essentially working, but it makes older parts of the alpha drawings get darker over time because it draws the bitmap over and over in onDraw() (which is not a problem while using solid lines as demonstrated in the SDK, but which becomes one when using alpha).
public class CanvasView extends View {
public void init() {
bitmap = Bitmap.createBitmap(1280, 720, Bitmap.Config.ARGB_8888); // a bitmap is created
canvas = new Canvas(bitmap); // and a canvas is instantiated
}
// Events
#Override public boolean onTouchEvent(#Nonnull MotionEvent event) {
float x = event.getX(); float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // when the user touches down
path.reset(); // the previous path is reset (if any)
drawingTool.down(); // my own class which encapsulates path.moveTo()
break;
case MotionEvent.ACTION_MOVE: // when the user paints
Rect dirtyRect = drawingTool.move(x, y); // encapsulates path.quadTo() // the path is built
if (dirtyRect != null) { invalidate(dirtyRect); } // and the dirty rectangle is invalidated
break;
case MotionEvent.ACTION_UP: // when the user lifts the finger
canvas.drawPath(path, paint); // the final path is drawn
path.reset();
invalidate(); // and the full area invalidated (just to make sure the final drawing looks as it should)
break;
}
return true;
}
#Override protected void onDraw(#Nonnull Canvas canvas) { // after every move or up event
super.onDraw(canvas);
canvas.drawBitmap(bitmap, 0, 0, null); // the previous bitmap is drawn [here is the problem]
canvas.drawPath(path, paint); // and the new path is added
}
}
The problem happens in onDraw(), because the bitmap is drawn over and over. So every time a new path is finished, the previous drawing becomes darker.
I know I could take a second bitmap and cache the results after each path is drawn, and then apply that "clean" bitmap for each new path. But this would be expensive.
Is there a way to draw alpha lines without using a second bitmap or re-drawing everything after each path? I am looking for an inexpensive solution.
Problem here is that bitmap and canvas are directly coupled, so when I draw on the canvas, the result immediately reflects in the bitmap. So I can't just clear one or the other?
I played with this for some time and I realized that the solution is as simple as switching the two commands in onDraw.
Instead of
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.drawPath(path, paint);
use
canvas.drawPath(path, paint);
canvas.drawBitmap(bitmap, 0, 0, null);
Drawing the bitmap last solves the problem. It is still too dark while being painted, so it needs some more tweaking, but the main problem is solved.
Plus, the good thing about it is that I won't need a second bitmap. But it is also clear that a second bitmap wouldn't have made much sense, as my bitmap is caching the image already and onDraw() just draws it into the view.
Actually you can set alpha level to Paint object. For example:
Paint transparentpaint = new Paint();
transparentpaint.setAlpha(100); // 0 - 255
canvas.drawBitmap(bitmap, 0, 0, transparentpaint);
Try to paste this instead of canvas.drawBitmap(bitmap, 0, 0, null);

Connect the dots of an imageview

It is a difficult task to create an app for kids and i am given a task of creating an connect the dots app.
example
https://play.google.com/store/apps/details?id=zok.android.dots
i have done some tutorials of onTouchEvent.
i know how to paint on screen Draw in Canvas by finger, Android
i am getting the dot coordinates using this Android: How do I get the x y coordinates within an image / ImageView? example
but I really don't know how to achieve this target.
I'd really appreciate a solution to this! Thanks!
input image is http://imgur.com/Z24yQUx,t6nL71r
EDIT
#Override
protected void onDraw(Canvas canvas) {
//backgroundBitmap is the image i want to show in background
if(DISPLAY_ALPHABET==0)
{
canvas.drawBitmap(backgroundBitmap, 0f, 0f, null);
DISPLAY_ALPHABET=1;
}
show(canvas);
}
public void show(Canvas canvas)
{
Paint paint = new Paint();
int cnt=1;
canvas.drawPaint(paint);
//color of numbers
paint.setColor(Color.BLUE);
paint.setTextSize(16);
canvas.drawColor(BACKGROUND);
** canvas.drawBitmap(mBitmap, 0, 0, null);**
canvas.drawPath(mPath, mPaint);
mPaint.setColor(Color.BLACK);
//Drawing points on canvas
for (Point point : mPoints) {
canvas.drawPoint(point.x, point.y, mPaint);
canvas.drawText(""+cnt++, point.x-7, point.y-7, paint);
}
mPaint.setColor(Color.RED);
}
I don't have much idea about this but still felt like worth thinking
You can save the next dot coordinates in a arraylist or a vector.
when you are drawing the line using canvas, check whether the Motion-Event x and y coordinates matches to that of the next point in the vector.
Once a coordinate is touched search for the next one in the vector
You can use a counter to increase the vector position, once the dot touched increment the counter.
EDIT: Check out my Demo App in this Link and check what you might need.

How to delete the image drawn in Canvas?

How will I delete the image drawn on my canvas if my code is this? Where will I put the delete process here? I've tried using the canvas.drawColor(Color.BLACK); but it is not working.
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
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;
case MotionEvent.ACTION_UP:
// nothing to do
break;
default:
return false;
}
// Schedules a repaint.
invalidate();
return true;
}
}
Old thread I know, but I was mucking around with API fingerpaint demo and wanted to clear canvas but not fill with solid colour (I had a background). Building on #coder_For_Life22 answer above I included following method:
protected void clear(){
Xfermode x = mPaint.getXfermode();
mPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
mCanvas.drawPaint(mPaint);
mPaint.setXfermode(x);
//Schedule redraw()
invalidate();
}
Try this with your Paint object..
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
canvas.drawPaint(paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC));
When invalidate() (or postInvalidate() from another thread) is called, onDraw() is subsequently called to redraw the entire area of the image. The Canvas object that is passed to onDraw() is backed with a bitmap that is already blank.
I realise that this doesn’t directly answer your question, but from reading your question I wonder if you’re misunderstanding the sequence of events that happen with invalidate() and onDraw(), together with the fact that you’re given a blank Canvas each time meaning you shouldn’t have a need to erase it.
It seems to me that what you're doing is you're trying to build up a Path vector representing the screen MotionEvents. Looking at your code as it stands, it seems to me that you may want to erase all drawn graphics by clearing all segments from your Path object.
canvas.drawColor(0xff000000); // i can't see why it should not work except the clip rect mentioned below
or
Paint paint = new Paint();
paint.setStyle(Style.FILL);
paint.setColor(0xff000000); // Specify the drawing color here
canvas.drawRect(0,0,w,h, paint);
always make sure that you did not set a clip that would influence the drawing behaviour.

Categories

Resources