Android capture signature using Canvas and save in png format -- Android



사인을 하는 앱을 만들어야 할 일이 있어서 아래의 글을 참고하여 구현하였다.


Signature capture has a lot of uses in enterprise applications such as proof of delivery, inspection forms, sales order agreement, etc. Here is sample program that asks for the name of the person and its signature. The program uses a canvas view to get the signature and then save it as .png Image.


Source for CaptureSignatureActivity.java


package com.as400samplecode;
  
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
  
public class CaptureSignatureActivity extends Activity {
  
    public static final int SIGNATURE_ACTIVITY = 1;
  
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
  
        Button getSignature = (Button) findViewById(R.id.signature);
        getSignature.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                Intent intent = new Intent(CaptureSignatureActivity.this, CaptureSignature.class); 
                startActivityForResult(intent,SIGNATURE_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 capture successful!", Toast.LENGTH_SHORT);
                    toast.setGravity(Gravity.TOP, 105, 50);
                    toast.show();
                }
            }
            break;
        } 
    }   
}

Source for CaptureSignature.java


package com.as400samplecode;
  
import java.io.File;
import java.io.FileOutputStream;
import java.util.Calendar;
  
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
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.Bundle;
import android.os.Environment;
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.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
  
public class CaptureSignature extends Activity { 
  
    LinearLayout mContent;
    signature mSignature;
    Button mClear, mGetSign, mCancel;
    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;
  
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.signature);
         
        tempDir = Environment.getExternalStorageDirectory() + "/" + getResources().getString(R.string.external_dir) + "/";
        ContextWrapper cw = new ContextWrapper(getApplicationContext());
        File directory = cw.getDir(getResources().getString(R.string.external_dir), Context.MODE_PRIVATE);
  
        prepareDirectory();
        uniqueId = getTodaysDate() + "_" + getCurrentTime() + "_" + Math.random();
        current = uniqueId + ".png";
        mypath= new File(directory,current);
  
  
        mContent = (LinearLayout) findViewById(R.id.linearLayout);
        mSignature = new signature(this, null);
        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);
  
        mClear.setOnClickListener(new OnClickListener() 
        {        
            public void onClick(View v) 
            {
                Log.v("log_tag", "Panel Cleared");
                mSignature.clear();
                mGetSign.setEnabled(false);
            }
        });
  
        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();
                }
            }
        });
  
        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();
            }
        });
  
    }
  
    @Override
    protected void onDestroy() {
        Log.w("GetSignature", "onDestory");
        super.onDestroy();
    }
  
    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;
    }
  
    private String getTodaysDate() { 
  
        final Calendar c = Calendar.getInstance();
        int todaysDate =     (c.get(Calendar.YEAR) * 10000) + 
        ((c.get(Calendar.MONTH) + 1) * 100) + 
        (c.get(Calendar.DAY_OF_MONTH));
        Log.w("DATE:",String.valueOf(todaysDate));
        return(String.valueOf(todaysDate));
  
    }
  
    private String getCurrentTime() {
  
        final Calendar c = Calendar.getInstance();
        int currentTime =     (c.get(Calendar.HOUR_OF_DAY) * 10000) + 
        (c.get(Calendar.MINUTE) * 100) + 
        (c.get(Calendar.SECOND));
        Log.w("TIME:",String.valueOf(currentTime));
        return(String.valueOf(currentTime));
  
    } 
    
    private boolean prepareDirectory() 
    {
        try 
        {
            if (makedirs()) 
            {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) 
        {
            e.printStackTrace();
            Toast.makeText(this, "Could not initiate File System.. Is Sdcard mounted properly?", 1000).show();
            return false;
        }
    }
  
    private boolean makedirs() 
    {
        File tempdir = new File(tempDir);
        if (!tempdir.exists())
            tempdir.mkdirs();
  
        if (tempdir.isDirectory()) 
        {
            File[] files = tempdir.listFiles();
            for (File file : files) 
            {
                if (!file.delete()) 
                {
                    System.out.println("Failed to delete " + file);
                }
            }
        }
        return (tempdir.isDirectory());
    }
  
    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) 
        {
            Log.v("log_tag", "Width: " + v.getWidth());
            Log.v("log_tag", "Height: " + v.getHeight());
            if(mBitmap == null)
            {
                mBitmap =  Bitmap.createBitmap (mContent.getWidth(), mContent.getHeight(), Bitmap.Config.RGB_565);;
            }
            Canvas canvas = new Canvas(mBitmap);
            try 
            {
                FileOutputStream mFileOutStream = new FileOutputStream(mypath);
  
                v.draw(canvas); 
                mBitmap.compress(Bitmap.CompressFormat.PNG, 90, mFileOutStream); 
                mFileOutStream.flush();
                mFileOutStream.close();
                String url = Images.Media.insertImage(getContentResolver(), mBitmap, "title", null);
                Log.v("log_tag","url: " + url);
                //In case you want to delete the file
                //boolean deleted = mypath.delete();
                //Log.v("log_tag","deleted: " + mypath.toString() + deleted);
                //If you want to convert the image to string use base64 converter
  
            }
            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();
            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);
        }
    }
}

Source for main.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:text="@string/hello"
        android:textSize="25sp" android:paddingBottom="20dp"
        android:textStyle="bold" />
    <Button android:text="Get My Signature" android:id="@+id/signature"
        android:layout_width="wrap_content" android:layout_height="wrap_content" />
</LinearLayout>

Source for signature.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/linearLayout1"
    android:layout_width="600dp" android:layout_height="400dp"
    android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout android:layout_height="wrap_content"
        android:id="@+id/linearLayout2" android:layout_width="match_parent">
        <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>

Source for AndroidManifest.xml


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.as400samplecode" android:versionCode="1"
    android:versionName="1.0">
    <uses-sdk android:minSdkVersion="13" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     
    <application android:icon="@drawable/icon" android:label="@string/app_name"
        android:theme="@android:style/Theme.Holo.Light">
        <activity android:name=".CaptureSignatureActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".CaptureSignature" android:label="Signature Confirmation"
            android:theme="@android:style/Theme.Holo.Light.Dialog" />
    </application>
</manifest>


Source for strings.xml


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, CaptureSignatureActivity!</string>
    <string name="app_name">CaptureSignature</string>
    <string name="external_dir">GetSignature</string>
</resources>

출처 : http://www.mysamplecode.com/2011/11/android-capture-signature-using-canvas.html


덧글

댓글 입력 영역



20140817_300_250

통계 위젯 (화이트)

6177
1073
1039938