Amazon Gift Cards

Sunday, November 21, 2010

Drawing with Canvas in Android, Saving your drawings

Continuing with our Drawing in Canvas series, let us see how to save your drawings
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:
Explanation
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);

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.
Post a Comment