Update:
May 5, 2011 - If saving the image is not that fine, this could come in handy http://stackoverflow.com/questions/5243547/decode-byte-array-to-bitmap-that-has-been-compressed-in-java
Introduction
We draw and saw our drawings are good, now lets save them
Notes
• The files are uploaded in http://goo.gl/ecHpE
• The project was build in IntelliJ and it should be easy to import to Eclipse
•
What Do I Need
DrawingSurface
public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback { private Bitmap mBitmap; private ... class DrawThread extends Thread{ ... @Override public void run() { Canvas canvas = null; while (_run){ try{ canvas = mSurfaceHolder.lockCanvas(null); if(mBitmap == null){ mBitmap = Bitmap.createBitmap (1, 1, Bitmap.Config.ARGB_8888);; } final Canvas c = new Canvas (mBitmap); c.drawColor(0, PorterDuff.Mode.CLEAR); commandManager.executeAll(c); canvas.drawBitmap (mBitmap, 0, 0,null); } finally { mSurfaceHolder.unlockCanvasAndPost(canvas); } } } } public Bitmap getBitmap(){ return mBitmap; } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub mBitmap = Bitmap.createBitmap (width, height, Bitmap.Config.ARGB_8888);; } }
DrawingActivity
public class DrawingActivity extends Activity implements View.OnTouchListener{ ... private static File APP_FILE_PATH = new File("/sdcard/TutorialForAndroidDrawings"); public void onClick(View view){ switch (view.getId()){ ... case R.id.saveBtn: final Activity currentActivity = this; Handler saveHandler = new Handler(){ @Override public void handleMessage(Message msg) { final AlertDialog alertDialog = new AlertDialog.Builder(currentActivity).create(); alertDialog.setTitle("Saved 1"); alertDialog.setMessage("Your drawing had been saved :)"); alertDialog.setButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { return; } }); alertDialog.show(); } } ; new ExportBitmapToFile(this,saveHandler, drawingSurface.getBitmap()).execute(); break; } } private class ExportBitmapToFile extends AsyncTask{ private Context mContext; private Handler mHandler; private Bitmap nBitmap; public ExportBitmapToFile(Context context,Handler handler,Bitmap bitmap) { mContext = context; nBitmap = bitmap; mHandler = handler; } @Override protected Boolean doInBackground(Intent... arg0) { try { if (!APP_FILE_PATH.exists()) { APP_FILE_PATH.mkdirs(); } final FileOutputStream out = new FileOutputStream(new File(APP_FILE_PATH + "/myAwesomeDrawing.png")); nBitmap.compress(Bitmap.CompressFormat.PNG, 90, out); out.flush(); out.close(); return true; }catch (Exception e) { e.printStackTrace(); } //mHandler.post(completeRunnable); return false; } @Override protected void onPostExecute(Boolean bool) { super.onPostExecute(bool); if ( bool ){ mHandler.sendEmptyMessage(1); } } } }
Quick Explanation
Ads from Amazon:
Explanationif(mBitmap == null){
mBitmap = Bitmap.createBitmap (1, 1, Bitmap.Config.ARGB_8888);;
}
final Canvas c = new Canvas (mBitmap);
c.drawColor(0, PorterDuff.Mode.CLEAR);
commandManager.executeAll(c);
This is the core of the saving part, in order for our drawing to be exported we have to create a canvas with bitmap to draw upon to, See Canvas Class for more info.
canvas.drawBitmap (mBitmap, 0, 0,null);
We put our bitmap to our original canvas (the surfaceView's canvas) so that we can see our drawings
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mBitmap = Bitmap.createBitmap (width, height, Bitmap.Config.ARGB_8888);;
}
We would have to recreate our bitmap when we change the orientation (The drawings will currently be removed)
final Activity currentActivity = this;
Handler saveHandler = new Handler(){ ... }
We cant call the UI tread from another thread so we pass a handler to our AsyncTask, see Using Handler in Android for more information on Handlers
private class ExportBitmapToFile extends AsyncTask
private Context mContext;
private Handler mHandler;
private Bitmap nBitmap;
public ExportBitmapToFile(Context context,Handler handler,Bitmap bitmap) {
mContext = context;
nBitmap = bitmap;
mHandler = handler;
}
@Override
protected Boolean doInBackground(Intent... arg0) {
try {
if (!APP_FILE_PATH.exists()) {
APP_FILE_PATH.mkdirs();
}
final FileOutputStream out = new FileOutputStream(new File(APP_FILE_PATH + "/myAwesomeDrawing.png"));
nBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
return true;
}catch (Exception e) {
e.printStackTrace();
}
//mHandler.post(completeRunnable);
return false;
}
@Override
protected void onPostExecute(Boolean bool) {
super.onPostExecute(bool);
if ( bool ){
mHandler.sendEmptyMessage(1);
}
}
}
In order for us to export the bitmap and not hard the UX of our app, we need to create a background thread for it, for more information on AsyncTask see the AsyncTask Class and Painless Threading
final FileOutputStream out = new FileOutputStream(new File(APP_FILE_PATH + "/myAwesomeDrawing.png"));
nBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
This is default Java feature, if your now from Java then this just mean that you create a buffer where when we use that in mBitmap.compress the compressed bitmap would be buffered into that FileOutputStream (i hope i was clear in this. haha)
Reference
Most of the stuff here were back in 2009, most of them are in Google Groups.
8 comments:
Sir i hav downloaded your code and went through it there is a Main class that extends activity and also DrawingActivity class that extends activity , where we should put the class and why two class to extend activity.
@Aditya on the codes, the main activity is like an activity where you can do anything (you can make a menu on that activity). The DrawingActivity meanwhile is the core of the app, see whole package, the codes should be there as well. On why we need extends activity, i suggest you to read the Activity lifecycle at http://developer.android.com/reference/android/app/Activity.html :)
sometimes when I save to a file from this, the images have portions that are missing (like someone used an eraser to rub out areas)
Any idea why I might be having this problem?
@ben try to stop the thread inside the drawingSurface, then try to change the density of the bitmap on the drawingsurface as well
Hi sir, I posted a question related to your code here, do you have a code fixed on this one? http://stackoverflow.com/questions/7016126/android-bitmap-compression-is-not-good thank you very much.
Hi sir, how do I stop the thread inside drawing surface and change the density of the bitmap? sorry for the noob question im kinda new in java/android dev...thanks a lot.
Hello! I habe the same problem of Ben... How can I solve that?
How to change density in thread?
Post a Comment