I want to draw a line using Canvas in Android. I tired this task in the ontouchevent using touchesmove, touchesup and touchesdown. The line appears when I touch end the mouse button. I want to draw a line like freehand drawing.
Here is my code,
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
{
//mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
invalidate();
break;
case MotionEvent.ACTION_UP:
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
mPath = new Path();
paths.add(mPath);
mPath.reset();
invalidate();
break;
}
As far as I can see the drawPath() method is executed upon ACTION_UP. You'd need to call this method in ACTION_MOVE as well so your Path gets drawn on every move you make. For this to work you'd have to copy mPath.lineTo(mX, mY) to ACTION_MOVE as well of course.
Related
I am creating an Android app where useres can draw and write using an active pen on a tablet.
The user can choose between different modes (e.g. pen, eraser, line, circle...) which offers them different tools.
The line and circle tools let the user draw a line/circle with the length/radius and direction where the user draws it. This is working quite well, but every time the user moves the pen another line/circle is drawn and it fills up the screen.
Image:
Code:
#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_8888);
canvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas c){
c.drawBitmap(mBitmap, 0, 0, bitmapPaint);
}
private float mX, mY, firstX, firstY;
private void touch_start(float x, float y) { //called from MotionEvent.ACTION_DOWN
path.moveTo(x, y);
firstX = x;
firstY = y;
mX = x;
mY = y;
}
private void touch_move(float x, float y) { //called from MotionEvent.ACTION_MOVE
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
switch(mode){
case "line":
canvas.drawLine(firstX, firstY, x, y, paint);
break;
case "circle":
canvas.drawCircle(firstX, firstY, (Math.abs(firstX-x) + Math.abs(firstY-y))/1.5f, paint); // divisor 1.5 just a rough value
break;
default:
canvas.drawPath(path, paint);
break;
}
mX = x;
mY = y;
}
Has anyone an idea how I could fix this?
Thanks in advance.
I found a solution by myself.
I created a new Canvas animationCanvas with its own animationBitmap for showing the circle while it is drawn.
The final circle is drawn in the method at MotionEvent.ACTION_UP.
This is the new touch_move:
private void touch_move(float x, float y) { //called from MotionEvent.ACTION_MOVE
path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
switch(mode){
case "line":
animationCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // clear previously drawn line (for animation)
animationCanvas.drawLine(firstX, firstY, x, y, paint);
break;
case "circle":
animationCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); // clear previously drawn circle (for animation)
animationCanvas.drawCircle(firstX, firstY, (Math.abs(firstX-x) + Math.abs(firstY-y))/1.5f, paint); // divisor 1.5 just a rough value
break;
default:
canvas.drawPath(path, paint);
break;
}
mX = x;
mY = y;
}
Maybe not the best solution but it works :)
I have an app in which it has a main canvas and i have added another canvas of bitmap on top of it. In the main canvas I have eraser in which it will detect when the user touches the bitmap area. I want to know the x and y inside the bitmap where the eraser touches and so on while moving from main canvas since the bitmap canvas is different from the main canvas. I want the x and y of the main canvas be the same with the bitmap canvas to be move. Thanks
Here's my snippet
public 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) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
if(istouchingBitmap(x, y) == true){
float xRatio = (float)bitmap.getWidth() / parent.getWidth();
float yRatio = (float)bitmap.getHeight() / parent.getHeight();
float xPos = lastX * xRatio;
float yPos = lastY * yRatio;
eraseBitmap(bitmap, xPos , yPos , 5);
}
}
function to detect the bitmap when touch
/**
*
* #param x The X location of the cursor in main View.
* #param y The Y location of the cursor in main View.
* #return This is only used to detect if the cursor is touching the Bitmap Image.
*/
public boolean istouchingBitmap(float x, float y) {
matrix.invert(inverse);
float[] pts = {x, y};
inverse.mapPoints(pts);
if (!bounds.contains(pts[0], pts[1])) {
return false;
}
// copy the location
lastX = x;
lastY = y;
return Color.alpha(bitmap.getPixel((int) pts[0], (int) pts[1])) != 0;
}
You only need to get the X and Y values from the touch event.
Then sustract the top,left margin/offset values respectively.
The result is the X,Y coordinate relative to the start of the bitmap.
PD: When doing this, i was having problems handling the status bar height, since the way of measuring it changes between Android version and device model.
Hope this helps.
Finally :). This code will map the screen X and Y to the x and y inside the bitmap. Hope it help others. Good luck
float[] point = new float[] {lastX, lastY};
Matrix inverse = new Matrix();
matrix.invert(inverse);
inverse.mapPoints(point);
bitmapX = point[0];
bitmapY = point[1];
canvas.drawCircle(bitmapX, bitmapY, radius, mPaint); // punch a hole
I want to implement eraser for my painting app . I am able to erase with the following code
paint.setColor(0x00000000);
paint.setAlpha(0x00);
But after erasing when you start painting again it does not paint properly so any idea to erase paint please suggest.
I had same issue.
Need to set view's setLayerType.
yourView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
You can set it in constructor or by view's object.
Its done.
Try the below code.
paint.setAlpha(0xFF);//transperent color
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));//clear the draw
Also have a look at the sample FingerPaint.java in the api demos under the folder graphics.
setAlpha(int a)
Helper to setColor(), that only assigns the color's alpha value, leaving its r,g,b values unchanged.
http://developer.android.com/reference/android/graphics/Paint.html. Have a look a the
documentation.
Edit:
Also check this
https://code.google.com/p/android/issues/detail?id=54105#c1
This is should resolve this issue
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) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
mPath.moveTo(mX, mY);
mX = x;
mY = y;
}
}
in touch_up() change it to:
private void touch_up() {
mPath.reset();
}
I'm newbee in android-game developing and I would appreciate your help. I'd like to paint some graphics on the canvas which is supposed to be way larger than screen. So some scaling and moving would be great. I've read some questions but they usually answers only some details - not the whole concept.
I've tried use Camera
cam.save();
cam.translate(0f, 0f, -8f);
cam.applyToCanvas(canvas);
cam.restore();
This scales perfectly, but I am unable to decode the real touch coord.
I don't want to use openGL (it's overkill and also I'd like to start with sth simple)
Anyway, I tried canvas.scale(int, int) as well, but didn't work. I believe the Camera is the right way, but I'm lost.
So the question is: how to determinate real coord? Furthermore, It would be nice if some could share a piece of tutorial etc. or some concept of using canvas transformation. (Or maybe there more appropriate ways how to solve it)
Thanks in advance
you have to override the onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
private void touch_start(float x, float y) {
mPath.reset();
mPath.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) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
I am trying to build a paint application.
The application is divided into 3 parts:
Initially the application is having a white background and user can paint on the canvas using different brush sizes and different colors
User can save the canvas image to sd card
User can upload the image from sd card and again paint on the image
Point 1 and 2 are working fine and I am able to paint on a white background.
But point 3, when I upload any image from Gallery and try to paint on the image, then the previous path painted is getting disappeared and only new path is displayed.
code :
onDraw(Canvas canvas){
mBitmap = Bitmap.createBitmap(320, 480, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
canvas.drawColor(Color.WHITE);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
onTouch - >
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
invalidate();
break;
case MotionEvent.ACTION_UP:
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
break;
mBitmap = BitmapFactory.decodeFile(filePath); -----this is from sd card
I am not sure that your app is keeping the previous drawn path and also drawing the new path. The reason for this is that each time when the onDraw() method is called you are creating a new bitmap and drawing path on that and you are also not saving the previous paths as you are calling mpath.reset().
There is one more point and it's very important: Never Never! call createBitmap() method in iteration. It consumes a lot of memory and will lead to outOfMemory exception.
The solution to you'r problem is:
Create a bitmap(should be mutable) from any stream(SD card,drawable,etc) or an empty bitmap in the constructor of your custom view and then create a canvas and use the same bitmap to draw into. In your touch_up() method draw paths on this canvas. In the onDraw() you should use this bitmap as the specified bitmap for the canvas(passed as parameter, no need to create a new canvas here) to draw into.
Here you can follow this link to the android api demo code.