Monday 8 December 2014

How to capture Signature in android

If you're app is consisting signature capturing/drawing feature fromuser then you can refer the below code.I have done it using View and Canvas class. In this approach you don't need to add any third party libraries or jar.This app is also saving the files into External database in JPEG image format.
  
      
   


Go through the following steps , Lets design the UI first for Signature and Home screen.

Step-1: Create Home screen.

         I have just added on TextView and one Button. OnClick of button will take user to next          screen to draw signature. 

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    android:background="#58FAF4" >

    <Button
        android:id="@+id/bsignature"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="53dp"
        android:text="Get Signature" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/signature"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="14dp"
        android:layout_marginTop="19dp"
        android:text="@string/hello_world" />

</RelativeLayout>

Step-2: Design signature draw screen.
         Here i have added three buttons Save,Cancel and Clear for several operations. User            need to put their name in EditText.
         Blank space is LinearLayout where we will add canvas to draw signature.

signature.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linearLayout1"
    android:layout_width="match_parent" 
    android:layout_height="match_parent"
    android:orientation="vertical"    
    android:background="#F6CEEC">
    
    <LinearLayout android:layout_height="wrap_content"
        android:id="@+id/linearLayout2" 
        android:layout_width="match_parent"
        android:weightSum="1">
        
        <Button android:layout_height="50dp" 
            android:layout_weight=".30"
            android:text="Cancel" 
            android:layout_width="0dp" 
            android:id="@+id/cancel" />
        <Button android:layout_height="50dp" 
            android:layout_weight=".35"
            android:text="Clear"
             android:layout_width="0dp" 
             android:id="@+id/clear" />
        <Button android:layout_height="50dp" 
            android:layout_weight=".35"
            android:text="Save"
             android:layout_width="0dp"
              android:id="@+id/getsign" />
    </LinearLayout>
    
    <TableLayout android:layout_height="wrap_content"
        android:id="@+id/tableLayout1" 
        android:layout_width="match_parent">
        
        <TableRow android:id="@+id/tableRow1" 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <TextView android:layout_height="wrap_content" 
                android:id="@+id/textView2"
                android:text="Your Name" 
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:layout_width="wrap_content" 
                android:paddingLeft="10sp"
                android:layout_gravity="right" />
            <EditText android:layout_height="wrap_content" 
                android:id="@+id/yourName"
                android:layout_weight="1"
                 android:layout_width="match_parent"
                android:maxLength="30">
                <requestFocus />
            </EditText>
        </TableRow>
        
        <TableRow android:id="@+id/tableRow3" 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <TextView android:layout_height="wrap_content"
                android:id="@+id/textView2"
                android:text="" android:maxLength="30"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:layout_width="wrap_content" />
            <TextView android:layout_height="wrap_content" android:id="@+id/textView2"
                android:text="Please Sign below ..." android:maxLength="30"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:layout_width="wrap_content" />
        </TableRow>
        
    </TableLayout>
    
    <LinearLayout android:layout_height="match_parent"
        android:id="@+id/linearLayout" android:layout_width="match_parent" />
</LinearLayout>



Step-3: Home screen java code
                                 This file is having onclick event to goto signature draw page and it also receives the result in return.

MainActivity.java

package com.allandroidcodes.signatureinput;

import com.allandroidcodes.signatureinput.R;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Gravity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity { 
public static final int SIGNATURE_ACTIVITY = 1;

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

    Button getSignature = (Button) findViewById(R.id.bsignature);
    getSignature.setOnClickListener(new View.OnClickListener() {
        public void onClick(View view) {
            Intent intent = new Intent(MainActivity.this, TakeSignature.class); 
            startActivityForResult(intent,SIGNATURE_ACTIVITY);
        }
    });
}
//Return result to this activity
 protected void onActivityResult(int requestCode, int resultCode, Intent data)
 {
    switch(requestCode) {
    case SIGNATURE_ACTIVITY
        if (resultCode == RESULT_OK) {

            Bundle bundle = data.getExtras();
            String status  = bundle.getString("status");
            if(status.equalsIgnoreCase("done")){
                Toast toast = Toast.makeText(this"Signature captured successfully",0);
                toast.setGravity(Gravity.TOP, 105, 50);
                toast.show();
            }
        }
        break;
    }

}  

}


Step-4: Signature draw code
       Here signature drawing and saving procces is coded.

TakeSignature.java

package com.allandroidcodes.signatureinput;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;

public class TakeSignature extends Activity {

    LinearLayout mContent;
    signature mSignature;
    Button mClearmGetSignmCancel;
    public static String tempDir;
    public int count = 1;
    public String current = null;
    private Bitmap mBitmap;
    View mView;
    File mypath;
 
    private String uniqueId;
    private EditText yourName;
    private Uri fileUri// file url to store image
// directory name to store captured images 
private static final String IMAGE_DIRECTORY_NAME = "Signaure";
File myExternalFile;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
        setContentView(R.layout.signature);
        
        mContent = (LinearLayout) findViewById(R.id.linearLayout);
        mSignature = new signature(thisnull);
        mSignature.setBackgroundColor(Color.WHITE);
        mContent.addView(mSignature, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
        mClear = (Button)findViewById(R.id.clear);
        mGetSign = (Button)findViewById(R.id.getsign);
        mGetSign.setEnabled(false);
        mCancel = (Button)findViewById(R.id.cancel);
        mView = mContent;

        yourName = (EditText) findViewById(R.id.yourName);

        //clear the signature
        mClear.setOnClickListener(new OnClickListener() 
        {        
            public void onClick(View v) 
            {
                Log.v("log_tag""Panel Cleared");
                mSignature.clear();
                mGetSign.setEnabled(false);
            }
        });
        
        //save the signature
        mGetSign.setOnClickListener(new OnClickListener() 
        {        
            public void onClick(View v) 
            {
                Log.v("log_tag""Panel Saved");
                boolean error = captureSignature();
                if(!error){
                    mView.setDrawingCacheEnabled(true);
                    mSignature.save(mView);
                    Bundle b = new Bundle();
                    b.putString("status""done");
                    Intent intent = new Intent();
                    intent.putExtras(b);
                    setResult(RESULT_OK,intent);   
                    finish();
                }
            }
        });
  
        //cancel and back to previous screen
        mCancel.setOnClickListener(new OnClickListener() 
        {        
            public void onClick(View v) 
            {
                Log.v("log_tag""Panel Canceled");
                Bundle b = new Bundle();
                b.putString("status""cancel");
                Intent intent = new Intent();
                intent.putExtras(b);
                setResult(RESULT_OK,intent);  
                finish();
            }
        });
        
      
}
//method to for name validation
 private boolean captureSignature() {
    
        boolean error = false;
        String errorMessage = "";


        if(yourName.getText().toString().equalsIgnoreCase("")){
            errorMessage = errorMessage + "Please enter your Name\n";
            error = true;
        }   

        if(error){
            Toast toast = Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT);
            toast.setGravity(Gravity.TOP, 105, 50);
            toast.show();
        }

        return error;
    }

//class to captupre signature
 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) 
        {
   if(mBitmap == null)
            {
                mBitmap =  Bitmap.createBitmap (mContent.getWidth(), mContent.getHeight(), Bitmap.Config.RGB_565);;
            }
            Canvas canvas = new Canvas(mBitmap);
         
            v.draw(canvas);
            
        
            File myDir = new File(
     Environment
     .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
     IMAGE_DIRECTORY_NAME);
            
         // Create the storage directory if it does not exist
     if (!myDir.exists()) {
     if (!myDir.mkdirs()) {
     Toast.makeText(getApplicationContext(), "Oops..fail to create directory"0).show();
    
     }
     }
            
     // Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(new Date());
        
     String fname = "IMG_"+timeStamp +".jpg";
         File file = new File (myDir, fname);
         if (file.exists ()) file.delete (); 
         try {
                FileOutputStream out = new FileOutputStream(file);
                mBitmap.compress(Bitmap.CompressFormat.JPEG,100, out);
                out.flush();
                out.close();
         } catch (Exception e) {
                e.printStackTrace();
         }          
       }
 
  public void clear() 
        {
            path.reset();
            invalidate();
        }
 
//methods for motions of touch events   
  protected void onDraw(Canvas canvas) 
        {
            canvas.drawPath(pathpaint);
        }
      
  public boolean onTouchEvent(MotionEvent event) 
        {
            float eventX = event.getX();
            float eventY = event.getY();
            mGetSign.setEnabled(true);
 
            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){
        }
 
        
  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);
        }        
    }
}