Drawing on Canvas and save image

33,782

Solution 1

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.

Solution 2

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)

Share:
33,782
Rahul Kalidindi
Author by

Rahul Kalidindi

Solutions Architect and Dev Android iOS Mobile Apps Chatbots

Updated on January 26, 2022

Comments

  • Rahul Kalidindi
    Rahul Kalidindi over 2 years

    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.

  • Rahul Kalidindi
    Rahul Kalidindi over 12 years
    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?
  • Rahul Kalidindi
    Rahul Kalidindi over 12 years
    Thanks for the Edit Martin. One last thing. How can achieve smooth drawing without the breakages?
  • DonGru
    DonGru over 12 years
    use the drawLine() method of the Canvas class instead of drawCircle - api doc
  • Rahul Kalidindi
    Rahul Kalidindi over 12 years
    The same problem is occurring even when I use the drawLine() method... :-(
  • snapfractalpop
    snapfractalpop about 12 years
    @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).
  • jigspatel
    jigspatel over 11 years
    Thanks for grate Answer. Realy help me.
  • Zahid H
    Zahid H over 10 years
    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.
  • Zahid H
    Zahid H over 10 years
    What if I have a TextView that I want to make it into a drawing board to allow free draw?
  • KarenAnne
    KarenAnne over 10 years
    @ZahidH You could try creating your own gradient. :)