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.
- Using third party libraries on github
- 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.