Drawing on Canvas and save image
Asked Answered
P

3

15

I am new to the Android Graphics class. I want to draw an image(actually a signature kind) using the touch events and want it to be saved on SDcard when I want to save it. I have scanned through the web for any such tutorials but I have not found any. Can anyone please tell me how to draw on canvas using the touch events and save it.

Any tutorials or sample code will be of great help.

Phillada answered 13/9, 2011 at 11:55 Comment(0)
U
28

I saw really good code on android developers, but I can't find it anymore... It's output is bezier curves so it will be pretty smooth. Here is code that I edited:

public class MyDrawView extends View {

private Bitmap  mBitmap;
private Canvas  mCanvas;
private Path    mPath;
private Paint   mBitmapPaint;
private Paint   mPaint;

public MyDrawView(Context c) {
    super(c);

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

    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setDither(true);
    mPaint.setColor(0xFF000000);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeWidth(3);
}

@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);
    mCanvas = new Canvas(mBitmap);
}

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

    canvas.drawPath(mPath, mPaint);
}

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

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();
}

@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;
}

public void clear(){
    mBitmap.eraseColor(Color.TRANSPARENT);
    invalidate();
    System.gc();
}}

then in onCreate of activity you want to use it in you just write something like this:

RelativeLayout parent = (RelativeLayout) findViewById(R.id.signImageParent);
myDrawView = new MyDrawView(this);
parent.addView(myDrawView);

This view is transparent and uses black paint to draw with your finger. So if you want see what you draw simply draw a white or gray bitmap on background of this view (you just add one line in the beginnig of onDraw), or you can use the background of the parent.

Then when you want to create an image from what you have drawn you just call

parent.setDrawingCacheEnabled(true);
Bitmap b = parent.getDrawingCache();

FileOutputStream fos = null;
try {
fos = new FileOutputStream(getFileName());
} catch (FileNotFoundException e) {
e.printStackTrace();
}

b.compress(CompressFormat.PNG, 95, fos);

It depends on what you want to have as output, you can use this code or instead of parent you can do this with myDrawView and you get just the image you have drawn without background (since we have our myDrawView background transparent).

Hope this will help. Feel free to leave feedback.

Urochrome answered 15/4, 2012 at 8:46 Comment(4)
What if I have a TextView that I want to make it into a drawing board to allow free draw?Silicate
worked like a charm with "today's API", but if you're a perfectionist, the setDrawingCacheEnabled() and getDrawingCache is deprecatedBeauregard
@Beauregard well yes, I wrote this post 11 years ago :DUrochrome
@Urochrome sure xD, stills working anyways. Good post!Beauregard
B
23

the drawing thing

Scribbler.java:

package org.yourpackage.scribble;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;

public class Scribbler extends Activity {
    DrawView drawView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        drawView = new DrawView(this);
        drawView.setBackgroundColor(Color.WHITE);
        setContentView(drawView);
        drawView.requestFocus();

    }
}

DrawView.java:

package org.yourpackage.scribble;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class DrawView extends View implements OnTouchListener {
    List<Point> points = new ArrayList<Point>();
    Paint paint = new Paint();

    public DrawView(Context context) {
        super(context);
        setFocusable(true);
        setFocusableInTouchMode(true);
        this.setOnTouchListener(this);
        paint.setColor(Color.BLACK);
    }

    @Override
    public void onDraw(Canvas canvas) {
        for (Point point : points) {
            canvas.drawCircle(point.x, point.y, 2, paint);  
        }
    }

    public boolean onTouch(View view, MotionEvent event) {
        Point point = new Point();
        point.x = event.getX();
        point.y = event.getY();
        points.add(point);
        invalidate();
        return true;
    }
}

class Point {
    float x, y;
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="org.yourpackage.scribble"
      android:versionCode="1"
      android:versionName="1.0">
    <application>
        <activity android:name=".Scribbler">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="3" />
</manifest> 

..will give you something like that:

alt text

You may want to draw lines instead of pixels

the saving thing

To implement saving you can pass a Bitmap into the Canvas constructor, then use the compress method of Bitmap to create an OutputStream which can be written to the SD card

EDIT to answer your comment:
Sure you can use XML to define your layout since DrawView extends View you can use it in your layout xml file. An example for the main.xml layout file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:id="@+id/linearLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/gradient"
    >

    <Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
    <org.yourpackage.scribble.DrawView android:id="@+id/drawView1" android:layout_width="wrap_content" android:layout_height="wrap_content">        </at.gru.android.drawdemo.DrawView>
</LinearLayout>

that gives you something like that:
enter image description here
You'll just need an additional contstructor public DrawView(Context context, AttributeSet attrSet)

Bea answered 13/9, 2011 at 12:15 Comment(7)
Do I need to create the buttons and labels from code if I want to insert any of the UI components? Can I use an XML Layout file for the UI components? Using the above example the drawing is not continuos. What should be done to achieve continuous drawing without breakages?Phillada
Thanks for the Edit Martin. One last thing. How can achieve smooth drawing without the breakages?Phillada
use the drawLine() method of the Canvas class instead of drawCircle - api docBea
The same problem is occurring even when I use the drawLine() method... :-(Phillada
@RahulVarma, if you are using drawLine, you may need to adapt the code a bit. Drawing a line from a point to itself is like drawing a circle.. you'd want to draw a line from one point to the next. Depending on what you are trying to do, this can become complex (since you may not want lines between all consecutive points).Carey
I am using the same method for drawing on a canvas for letter practicing. How do I let the user draw lines in anyway they want? Also the background from using this example isn't showing up.Silicate
@ZahidH You could try creating your own gradient. :)Alroi
S
0

I had a drawing view class set up, and doing what Belovoj suggested worked also in that class:

    setDrawingCacheEnabled(true); //in init of the class
    //when I wanted to get the image to save it
    Bitmap b = this.getDrawingCache();
Synodic answered 24/1, 2022 at 15:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.