Simple Android Signature Capture Tutorial and Source Code


Hello my friends! Today’s code is about capturing a user’s signature and saving it as a PNG image in the device SD card.

This android signature capture tutorial is useful if your app requires some signature for purposes like purchases, receipts or any transactions that needs a signature as a proof .

Our simple program below will have two buttons, a ‘save’ and ‘clear’ buttons and a canvas where the user can draw his signature. The program output is simple, so the code should be simple too! So…

The content of this post includes:

1.0 Java and XML Files

1.1 MainActivity.java

1.2 SignatureMainLayout.java

1.3 AndroidManifest.xml

2.0 Source Code Download

 

Alright, let’s get started!

1.0 Java and XML Files

1.1 MainActivity.java

On our main activity, we are just going to set SignatureMainLayout as the primary content view.

package com.example.takesignature;

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

import com.signature.SignatureMainLayout;

public class MainActivity extends Activity {

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

        setContentView(new SignatureMainLayout(this));
    }
}

1.2 SignatureMainLayout.java

This file actually contains two classes: The SignatureMainLayout that returns the user interfaces, and, the SignatureView that handles the drawing in the signature canvas.

package com.signature;

import java.io.File;
import java.io.FileOutputStream;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Environment;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

public class SignatureMainLayout extends LinearLayout implements OnClickListener {

	LinearLayout buttonsLayout;
	SignatureView signatureView;

	public SignatureMainLayout(Context context) {
		super(context);

		this.setOrientation(LinearLayout.VERTICAL);

		this.buttonsLayout = this.buttonsLayout();
		this.signatureView = new SignatureView(context);

		// add the buttons and signature views
		this.addView(this.buttonsLayout);
		this.addView(signatureView);

	}

	private LinearLayout buttonsLayout() {

		// create the UI programatically
		LinearLayout linearLayout = new LinearLayout(this.getContext());
		Button saveBtn = new Button(this.getContext());
		Button clearBtn = new Button(this.getContext());

		// set orientation
		linearLayout.setOrientation(LinearLayout.HORIZONTAL);
		linearLayout.setBackgroundColor(Color.GRAY);

		// set texts, tags and OnClickListener
		saveBtn.setText("Save");
		saveBtn.setTag("Save");
		saveBtn.setOnClickListener(this);

		clearBtn.setText("Clear");
		clearBtn.setTag("Clear");
		clearBtn.setOnClickListener(this);

		linearLayout.addView(saveBtn);
		linearLayout.addView(clearBtn);

		// return the whoe layout
		return linearLayout;
	}

	// the on click listener of 'save' and 'clear' buttons
	@Override
	public void onClick(View v) {
		String tag = v.getTag().toString().trim();

		// save the signature
		if (tag.equalsIgnoreCase("save")) {
			this.saveImage(this.signatureView.getSignature());
		} 

		// empty the canvas
		else {
			this.signatureView.clearSignature();
		}

	}

	/**
	 * save the signature to an sd card directory
	 * @param signature bitmap
	 */
	final void saveImage(Bitmap signature) {

		String root = Environment.getExternalStorageDirectory().toString();

		// the directory where the signature will be saved
		File myDir = new File(root + "/saved_signature");

		// make the directory if it does not exist yet
		if (!myDir.exists()) {
			myDir.mkdirs();
		}

		// set the file name of your choice
		String fname = "signature.png";

		// in our case, we delete the previous file, you can remove this
		File file = new File(myDir, fname);
		if (file.exists()) {
			file.delete();
		}

		try {

			// save the signature
			FileOutputStream out = new FileOutputStream(file);
			signature.compress(Bitmap.CompressFormat.PNG, 90, out);
			out.flush();
			out.close();

			Toast.makeText(this.getContext(), "Signature saved.", Toast.LENGTH_LONG).show();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * The View where the signature will be drawn
	 */
	private class SignatureView extends View {

		// set the stroke width
		private static final float STROKE_WIDTH = 5f;
		private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;

		private Paint paint = new Paint();
		private Path path = new Path();

		private float lastTouchX;
		private float lastTouchY;
		private final RectF dirtyRect = new RectF();

		public SignatureView(Context context) {

			super(context);

			paint.setAntiAlias(true);
			paint.setColor(Color.BLACK);
			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeJoin(Paint.Join.ROUND);
			paint.setStrokeWidth(STROKE_WIDTH);

			// set the bg color as white
			this.setBackgroundColor(Color.WHITE);

			// width and height should cover the screen
			this.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

		}

		/**
		 * Get signature
		 *
		 * @return
		 */
		protected Bitmap getSignature() {

			Bitmap signatureBitmap = null;

			// set the signature bitmap
			if (signatureBitmap == null) {
				signatureBitmap = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.RGB_565);
			}

			// important for saving signature
			final Canvas canvas = new Canvas(signatureBitmap);
			this.draw(canvas);

			return signatureBitmap;
		}

		/**
		 * clear signature canvas
		 */
		private void clearSignature() {
			path.reset();
			this.invalidate();
		}

		// all touch events during the drawing
		@Override
		protected void onDraw(Canvas canvas) {
			canvas.drawPath(this.path, this.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);

				lastTouchX = eventX;
				lastTouchY = eventY;
				return true;

			case MotionEvent.ACTION_MOVE:

			case MotionEvent.ACTION_UP:

				resetDirtyRect(eventX, eventY);
				int historySize = event.getHistorySize();
				for (int i = 0; i < historySize; i++) {
					float historicalX = event.getHistoricalX(i);
					float historicalY = event.getHistoricalY(i);

					expandDirtyRect(historicalX, historicalY);
					path.lineTo(historicalX, historicalY);
				}
				path.lineTo(eventX, eventY);
				break;

			default:

				return false;
			}

			invalidate((int) (dirtyRect.left - HALF_STROKE_WIDTH),
					(int) (dirtyRect.top - HALF_STROKE_WIDTH),
					(int) (dirtyRect.right + HALF_STROKE_WIDTH),
					(int) (dirtyRect.bottom + HALF_STROKE_WIDTH));

			lastTouchX = eventX;
			lastTouchY = eventY;

			return true;
		}

		private void expandDirtyRect(float historicalX, float historicalY) {
			if (historicalX < dirtyRect.left) {
				dirtyRect.left = historicalX;
			} else if (historicalX > dirtyRect.right) {
				dirtyRect.right = historicalX;
			}

			if (historicalY < dirtyRect.top) {
				dirtyRect.top = historicalY;
			} else if (historicalY > dirtyRect.bottom) {
				dirtyRect.bottom = historicalY;
			}
		}

		private void resetDirtyRect(float eventX, float eventY) {
			dirtyRect.left = Math.min(lastTouchX, eventX);
			dirtyRect.right = Math.max(lastTouchX, eventX);
			dirtyRect.top = Math.min(lastTouchY, eventY);
			dirtyRect.bottom = Math.max(lastTouchY, eventY);
		}

	}

}

1.3 AndroidManifest.xml

Since we are saving the image as PNG in the device SD card, we have to put a certain permission on the manifest. You AndroidManifest.xml should look like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.takesignature"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="14" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.takesignature.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 

2.0 Source Code Download

Buy now

Note: The code is working but this write up is not yet in its final form. I posted it right away because some people might need this kind of code at the moment.


30 responses to “Simple Android Signature Capture Tutorial and Source Code”

  1. Jesus christ, this just saved me so much time that I will drink a beer for your sake, and I would pay you one if I’d have the chance.

    Thank you so much for your work, and for sharing it, keep going with your awesome job!

  2. I just want to ask, what if i have 2 activities and this is called in the second activity, how do i go back to the main activity whenever i click on another button that i added which is back. I understand the the whole program is in a java class, not in an activity. You just used “setContentView”.

    • Hello @Ryan, the program used an activity, so you won’t have any problems using the back button.

      As you can see in section 1.1 above.


      public class MainActivity extends Activity {...

  3. Hi Mike,

    How do I connect it to a Linear layout that I have an XML and not have it cover the entire screen?

  4. This is a huge help Mike.
    How do you set the content view back or move to another layout after the user press the save button?

    Thank You in Advance!

  5. I feel really stupid right now, still very new to android app programming, but I purchased this app but can not figure out how to actually use it. No Example code included with my ha.

  6. Hi Mike, this helps a lot thank you.
    But if I want to save the signature in jpeg format, is there a way to icrease the resolution? Let’s say 120ppi?

  7. I am happy as well but I like to extend it sending the png via BT.
    What is the recommended enviroment to compile the code my self? Or what has been used?

  8. Thanks for such a wonderful code,
    The application is running but unable to save There is no error also but still i am having saving, and just wanted to know where i can find the saved signature.

  9. Hey,
    Great code.
    Do you have android instrumental test cases for your above code using espresso tool?
    If you have test cases please share.

    Thanks regards

  10. Wow…last app I had to add a library from binary solutions biz which does exactly this and not customizable! I just go it running and tested it already all works perfect and on the the server side it’s generating the pdf with the signature already. Have a question for the coder.
    Im well versed in android except anything to do with graphics. If I need to put a fixed sized for the canvas and centered it what can I do.? Because I saw another app that does about the same of my app and when it’s time to sign there is the canvas but smaller in the ceter of the screen…

Leave a Reply

Your email address will not be published. Required fields are marked *