Hi,
Today i want to show you how you can save like a image file a user gestures. Also you can learn how to draw gestures on a view and also how to erase the gestures.
There is a similar post…but simply do not works plug and play like this improved example.
The following class is an Dialog activity which is just a plug and play class.
Copy and paste and it will work.
All you need are some setups. For example you need to add in your strings/ folder a item called external_dir.
Here will be saved the png image file.
First create a Hello World application. Can look like this:
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 MyApplication extends Activity { public static final int MY_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(MyActivity.this, CaptureSignature.class); startActivityForResult(intent,MY_ACTIVITY); } }); } protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch(requestCode) { case MY_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; } } }
As you saw we have called from a button the Activity CaptureSignature…
You need to declare this activity in your manifest like this:
<activity android:name=".CaptureSignature" android:theme="@android:style/Theme.Dialog" />
Also in Manifest you need to add permission to write on SDCARD like this:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
After you have done this modifications you need to create the class CaptureSignature.java
In this file you need the following code:
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.Window; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; 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 yourFileName; @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()); prepareDirectory(); yourFileName = (EditText) findViewById(R.id.yourName); 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; 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(yourFileName.getText().toString().equalsIgnoreCase("")){ errorMessage = errorMessage + "Please enter your FileName\n"; error = true; } else { uniqueId = getTodaysDate() + "_" + getCurrentTime() + "_"+yourFileName.getText().toString(); current = uniqueId + ".png"; mypath= new File(tempDir,current); } 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(); } 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); } } }
Now you will need the xml file called signature.xml
This is the code:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:id="@+id/linearLayout1" android:layout_width="fill_parent" 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="@string/cancel" android:layout_width="0dp" android:id="@+id/cancel" /> <Button android:layout_height="50dp" android:layout_weight=".35" android:text="@string/clear" android:layout_width="0dp" android:id="@+id/clear" /> <Button android:layout_height="50dp" android:layout_weight=".35" android:text="@string/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="@string/filename" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content" android:paddingLeft="10sp" android:layout_gravity="left" /> </TableRow> <TableRow android:id="@+id/tableRow4" android:layout_width="wrap_content" android:layout_height="wrap_content"> <EditText android:layout_height="wrap_content" android:id="@+id/yourName" android:layout_weight="1" android:layout_width="match_parent" android:hint="@string/signature_hint" 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/textView5" android:text="@string/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>
Here you will need to update the strings in your string/ folder
but i can give you a hint on how it looks at mine
<resources> <string name="app_name">Myapp</string> <string name="external_dir">WeJob</string> <string name="filename">Filename:</string> <string name="signature_hint">Ex: mySignature</string> <string name="sign_below">Please sign below...</string> <string name="cancel">Cancel</string> <string name="clear">Clear</string> <string name="save">Save</string> </resources>
Thanks and happy coding!
