Android Capture Digital Signature And Save |Using Canavs

android capture digital signature, android capture signature using canvas

I will make Android capture digital signature tutorial with example in this article.

Table of Content

1. Android Capture Digital Signature And Save In Android Studio

2. Android Capture Digital Signature Using Canvas And Save In PNG Image

1. Android Capture Digital Signature And Save In Android Studio

You will learn how to get and save human digital signature in android studio.

After drawing and making the digital signature, we will also save it in the image format.

View Digital Signature

Our signature paper will look like below on android device.

Digital Signature

Digital Signature capture is a technique for signing electronic document files with a handwritten signature.

It holds the equal importance as of the paper signature.

Digital Signature pad is useful when you want to get real signature from human on android device.

For example, courier person use this technique most effectively. You can also use this feature in an online registration platform and information gathering systems.

It saves paper and you also do not need to maintain every single paper for every individual.

Signatures of all persons is saved in one single device so it is easy to store and for transportation.

Step 1. Integrating Library

We will use SignatureView Library to accomplish our task.

Write down the following line in your build.gradle(Module: app) file.

 implementation 'com.kyanogen.signatureview:signature-view:1.0'

Above line will download all the necessary files which are required to create signature view in xml layout file.

Step 2. Permissions

We are going to save the human digital signature in an image format. For this task, we have to define following two permissions in AndroidManifest.xml file

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

Note : I am targeting the sdk version 22 here so I do not need to check for runtime permissions.

Step 3. Last Edits

  • Now all we need is to write some source code in activity_main.xml and MainActivity.java file.

Add the below source code in activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.kyanogen.signatureview.SignatureView
        xmlns:sign="http://schemas.android.com/apk/res-auto"
        android:id="@+id/signature_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        sign:penSize="5dp"
        sign:backgroundColor="#ffffff"
        sign:penColor="#000000"
        sign:enableSignature="true"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/clear"
        android:text="clear"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/save"
        android:text="save"/>
</LinearLayout>

In the main layout structure, I have added one signature view. It will create a white space on which user can write his signature.

Then I have added two buttons.

One button will clear the signature view when you click it.

Other one will save the image of the digital signature view.

Now it’s time to write the MainActivity.java file

Copy the below source code in MainActivity.java file

import android.graphics.Bitmap;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.kyanogen.signatureview.SignatureView;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;

public class MainActivity extends AppCompatActivity {

    Bitmap bitmap;
    Button clear,save;
    SignatureView signatureView;
    String path;
    private static final String IMAGE_DIRECTORY = "/signdemo";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        signatureView =  (SignatureView) findViewById(R.id.signature_view);
        clear = (Button) findViewById(R.id.clear);
        save = (Button) findViewById(R.id.save);

        clear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                signatureView.clearCanvas();
            }
        });

        save.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bitmap = signatureView.getSignatureBitmap();
                path = saveImage(bitmap);
            }
        });

    }
    public String saveImage(Bitmap myBitmap) {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bytes);
        File wallpaperDirectory = new File(
                Environment.getExternalStorageDirectory() + IMAGE_DIRECTORY /*iDyme folder*/);
        // have the object build the directory structure, if needed.
        if (!wallpaperDirectory.exists()) {
            wallpaperDirectory.mkdirs();
            Log.d("hhhhh",wallpaperDirectory.toString());
        }

        try {
            File f = new File(wallpaperDirectory, Calendar.getInstance()
                    .getTimeInMillis() + ".jpg");
            f.createNewFile();
            FileOutputStream fo = new FileOutputStream(f);
            fo.write(bytes.toByteArray());
            MediaScannerConnection.scanFile(MainActivity.this,
                    new String[]{f.getPath()},
                    new String[]{"image/jpeg"}, null);
            fo.close();
            Log.d("TAG", "File Saved::--->" + f.getAbsolutePath());

            return f.getAbsolutePath();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return "";

    }
}

Diving Deep

After implementing library, we are able to generate a signature view like ImageView as we have already added it in activity_main.xml

In MainActivity.java file, I have made an object of it using findViewById().

Consider below code

    Bitmap bitmap;
    Button clear,save;
    SignatureView signatureView;
    String path;
    private static final String IMAGE_DIRECTORY = "/signdemo";

Here I have created necessary objects.

IMAGE_DIRECTORY HOLDS THE FOLDER NAME where the image will be generated.

Look at the below source code

clear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                signatureView.clearCanvas();
            }
        });

Compiler will run this code when the user clicks clear button.

This will clear the signature view means that any existing writings will be erased from the signature pad.

 save.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bitmap = signatureView.getSignatureBitmap();
                path = saveImage(bitmap);
            }
        });

When the user clicks the save button, compiler will execute the above code.

It will save the existing writings of signature view in an image format.

For creating the image of the signature, we need to have bitmap.

Following line will give us bitmap.

bitmap = signatureView.getSignatureBitmap();

We will use this bitmap to save the image.

Below line will save the image.

path = saveImage(bitmap);

Here, I have used saveImage() method to create the image. After creating the image, this method will return path of the saved image in string format.

path (String variable) will hold the image path returned by the saveImage() method.

Code for saveImage() method is as per following

  public String saveImage(Bitmap myBitmap) {
       
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        myBitmap.compress(Bitmap.CompressFormat.JPEG, 90, bytes);
        File wallpaperDirectory = new File(
                Environment.getExternalStorageDirectory() + IMAGE_DIRECTORY /*folder*/);
        // have the object build the directory structure, if needed.
        if (!wallpaperDirectory.exists()) {
            wallpaperDirectory.mkdirs();
            Log.d("hhhhh",wallpaperDirectory.toString());
        }

        try {
            File f = new File(wallpaperDirectory, Calendar.getInstance()
                    .getTimeInMillis() + ".jpg");
            f.createNewFile();
            FileOutputStream fo = new FileOutputStream(f);
            fo.write(bytes.toByteArray());
            MediaScannerConnection.scanFile(MainActivity.this,
                    new String[]{f.getPath()},
                    new String[]{"image/jpeg"}, null);
            fo.close();
            Log.d("TAG", "File Saved::--->" + f.getAbsolutePath());

            return f.getAbsolutePath();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return "";

    }

This method will save the image from the bitmap.

This method uses IMAGE_DIRECTORY (string variable which holds the folder name), to create the image path.

Change it’s value to create the folder of your choice.

If you do not want to use third party library then you can use canvas class to create signature view.

Below is the example to use canvas for digital signature.





2. Android Capture Digital Signature Using Canvas And Save In PNG Image

Android capture digital signature using canvas example will guide you today.

This example will guide you how to get and save human digital signature in android studio.

After drawing and making the digital signature, we will also save it in the png format.

Final Signature

After all above coding writings, we will get the output as the below video

You can develop digital signature example with two methods.

  1. Using third party libraries on github
  2. And using Canvas Class

We will use second method in this tutorial.

What is Digital Signature

It is mandatory to have some signing documents for several transactions, contracts and business solutions.

In this fast era, getting physical signature on every paper and send that paper to other place or office is little lengthy process.

Digital signature helps us here to simplify this process. User just need to give it’s signature on android device screen instead of real paper. That’s all.

We can save this digital signature as an image and can send it anywhere across the globe.

Now follow all the below steps to create digital signature using canvas in android.

Step 1. Manifest Changes

For storing the image in android device, we need storage permissions.

Write down below two lines in the AndroidManifest.xml file

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

Note : If your app is targeting sdk version greater than 22 then you have to ask for runtime permissions in android.

I am targeting sdk version 22, So I have not asked for runtime permissions here.

Step 2. Main Updates

We need to add some source code in the main files to achieve our final goal.

Copy the following source code in activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/canvasLL"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#fff"
        android:orientation="vertical" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="10dp"
        android:orientation="horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btnclear"
            android:text="Clear"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btnsave"
            android:text="Save"/>

    </LinearLayout>

</LinearLayout>

I have taken one LinearLayout which will work as a signature pad. User will have to write his signature on this pad.

LinearLayout with id canvasLL will be our signature pad in the above code.

I have also taken two buttons : Clear and Save

When the user will click the Clear button, compiler will erase the existing writing from the signature pad.

Save button will save the signature in png format when the user will click it.

Write down the following code in MainActivity.java file.

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
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.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    private Button btnClear, btnSave;
    private File file;
    private LinearLayout canvasLL;
    private View view;
    private signature mSignature;
    private Bitmap bitmap;

    // Creating Separate Directory for saving Generated Images
    String DIRECTORY = Environment.getExternalStorageDirectory().getPath() + "/Signature/";
    String pic_name = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    String StoredPath = DIRECTORY + pic_name + ".png";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        canvasLL = (LinearLayout) findViewById(R.id.canvasLL);
        mSignature = new signature(getApplicationContext(), null);
        mSignature.setBackgroundColor(Color.WHITE);
        // Dynamically generating Layout through java code
        canvasLL.addView(mSignature, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        btnClear = (Button) findViewById(R.id.btnclear);
        btnSave = (Button) findViewById(R.id.btnsave);

        view = canvasLL;

        btnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSignature.clear();
            }
        });

        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                view.setDrawingCacheEnabled(true);
                mSignature.save(view,StoredPath);
                Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();

            }
        });

        // Method to create Directory, if the Directory doesn't exists
        file = new File(DIRECTORY);
        if (!file.exists()) {
            file.mkdir();
        }

    }

    public class signature extends View {

        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 signature(Context context, AttributeSet attrs) {
            super(context, attrs);
            paint.setAntiAlias(true);
            paint.setColor(Color.BLACK);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setStrokeWidth(STROKE_WIDTH);
        }

        public void save(View v, String StoredPath) {
            Log.v("log_tag", "Width: " + v.getWidth());
            Log.v("log_tag", "Height: " + v.getHeight());
            if (bitmap == null) {
                bitmap = Bitmap.createBitmap(canvasLL.getWidth(), canvasLL.getHeight(), Bitmap.Config.RGB_565);
            }
            Canvas canvas = new Canvas(bitmap);
            try {
                // Output the file
                FileOutputStream mFileOutStream = new FileOutputStream(StoredPath);
                v.draw(canvas);

                // Convert the output file to Image such as .png
                bitmap.compress(Bitmap.CompressFormat.PNG, 90, mFileOutStream);
                mFileOutStream.flush();
                mFileOutStream.close();

            } catch (Exception e) {
                Log.v("log_tag", e.toString());
            }

        }

        public void clear() {
            path.reset();
            invalidate();

        }

        @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);
                    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:
                    debug("Ignored touch event: " + event.toString());
                    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 debug(String string) {

            Log.v("log_tag", string);

        }

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

Understandings

Look at the below source code

 String DIRECTORY = Environment.getExternalStorageDirectory().getPath() + "/Signature/";
 String pic_name = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
 String StoredPath = DIRECTORY + pic_name + ".png";

First line will define the String variable DIRECTORY. It is defining a path where we need to create a folder.

We will use this variable to create a folder named “Signature” in the external storage.

Second line is defining the pic_name String variable. This variable holds the date format in string variable.

Third line is defining the StoragePath named String variable. It will completely create a name of the image.

Consider the below code

 canvasLL.addView(mSignature, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

Above line will add view to the signature pad (LinearLayout).

Here mSignature is the object of the signature class. We have defined this signature class in the Main Activity itself as written in the above MainActivity.java file.

Following code defines the click event of the Clear button.

  btnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSignature.clear();
            }
 });

When the user clicks the Clear button, compiler will clear the surface of the signature pad and erase the existing writings.

Look at the below code

btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                view.setDrawingCacheEnabled(true);
                mSignature.save(view,StoredPath);
                Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();

            }
 });

Compiler will execute the above code when the user will click the save button.

It will save the image with the png format in the path defined by the “StoredPath” variable.

clear() and save() method

signature class have two methods clear() and save().

clear() method will erase the writings of the signature pad.

save() method will save the image in the external storage->Signature directory.

Using canvas class is little complex method as it has very long and deep coding lines.

Customization in this method is a big pain you know.