お絵かきアプリで書いた絵を保存する。

次に前回までに利用したお絵かきアプリで書いた画像を保存してみたいと思います。

今回はviewを別のクラスに分けているので、クラス間の値の引渡しなども重要です。

 

まず、最初にメニューを作成して、そのメニューに関数を割り当てましょう。

作成をするメニューはページの初期化と、保存と、終了です。

まずstring.xmlに値を作成します。

string.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <string name="hello">Hello World, DrawBm!</string>
 <string name="app_name">drawbm</string>
 <string name="clean">DELETE</string>
 <string name="save">SAVE</string>
 <string name="finish">FINISH</string>
</resources>

次にmenu.xmlを作成してメニューを作ります。

<?xml version="1.0" encoding="utf-8"?>
<menu
 xmlns:android="http://schemas.android.com/apk/res/android">
 <item 
 android:id="@+id/item1"
 android:title="@string/clean"
 ></item>
 <item 
 android:id="@+id/item2"
 android:title="@string/save"
 ></item>
 <item 
 android:id="@+id/item3"
 android:title="@string/finish"
 ></item>
</menu>

メニューの生成はActivityで作成します。

package in.andante.drawbm;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class DrawBm extends Activity {
 penView penview;
 
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 penview= new penView(this);
 setContentView(penview);
 }
 /** メニューの生成イベント */
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
 super.onCreateOptionsMenu(menu);
 getMenuInflater().inflate(R.menu.menu,menu);  
 return true;
 }
 /** メニューがクリックされた時のイベント */
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
 switch ( item.getItemId() ) {
 case R.id.item1:
 penview.clearDrawList(); 
 break;
 case R.id.item2:
 penview.saveToFile();
 break;
 case R.id.item3:
 finish();
 break;
 }
 return true;
 }

}

で、ここで、penViewで存在していない関数ですがclearDrawList()、saveToFile()を想定して作成をしています。

保存を行う為にはbitmapに一度書き写す必要があるので、bitmapを取り扱っていた

お絵かきアプリを作る。描画編3で作成をしていきたいと思います。

簡単に作成をしていた仕組みの説明ですが、bitmapとcanvasを作成して、指の動きに合わせてcanvasに描画をします。そのcanvasをviewに書く事で、お絵かきを表示しています。

clearDrawList()に関しては以下の形になります。

public void clearDrawList(){
 bmpCanvas.drawColor(Color.BLACK);
 invalidate();
}

ここで塗りつぶす色はキャンバスの色にします。色を塗りつぶして、bmpbmpCanvasをViewに反映する為にinvalidateを実行します(つまり、再描画)で現在の絵を一度破棄出来ます。

次にデータの保存を行います。データの保存に関してはmanifestに関しても書き込みを可能に変更します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="in.andante.drawbm"
 android:versionCode="1"
 android:versionName="1.0">
 <uses-sdk android:minSdkVersion="7" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

 <application android:icon="@drawable/icon" android:label="@string/app_name">
 <activity android:name=".DrawBm"
 android:label="@string/app_name">
 <intent-filter>
 <action android:name="android.intent.action.MAIN" />
 <category android:name="android.intent.category.LAUNCHER" />
 </intent-filter>
 </activity>

 </application>
</manifest>

sdcardの書き込み設定を確認します。

下記で、書き込みが可能であればtrue、可能でなければ、falseに変更を行います。

private boolean sdcardWriteReady(){
 String state = Environment.getExternalStorageState();
 return (Environment.MEDIA_MOUNTED.equals(state));
}

また、Viewを呼び出しの際にcontextをActivityのキャストして、保持しておきます。

private Activity _context;
public penView(Context context) {
 super(context);
 _context = (Activity)context;
}

sdcardが利用できなければ、Toastのデータで保存が出来なかった旨を表示して動作を終了します。

public void saveToFile(){
 if(!sdcardWriteReady()){
 Toast.makeText(_context, "SDCARDが認識されません。", Toast.LENGTH_SHORT).show();
 return;
 }
}

sdcardのpathは下記で取得ができます。

Environment.getExternalStorageDirectory().getPath()

なので、このアプリ専用のフォルダの名前をdrawbmとすると

File file = new File(Environment.getExternalStorageDirectory().getPath()+"/drawbm/");

で、ディレクトリを取得できます。このブログを最初に作成した段階では、このディレクトリは存在していないので、存在してない時にはこのディレクトリを作成します。

if(!file.exists()){
 file.mkdir();
}

このディレクトリのパスを保存する名前を作成して絶対パスを取得します。

String imgName = file.getAbsolutePath() + "/" + System.currentTimeMillis() +".jpg";
File saveFile = new File(imgName);

while(saveFile.exists()) {
 imgName = file.getAbsolutePath() + "/" + System.currentTimeMillis() +".jpg";
 saveFile = new File(imgName);
}

もしも同じ名前のファイルが存在する場合は回避をします。好きな名前で入れたい場合はここでdialogを出してもいいかもしれないです。

※上記では時間を利用して名前をつけているので基本的に同じ名前になる事はありません。

try {
 FileOutputStream out = new FileOutputStream(imgName);
 bmp.compress(CompressFormat.JPEG, 100, out);
 out.flush();
 out.close();
 Toast.makeText(_context, "保存されました。", Toast.LENGTH_SHORT).show();
} catch(Exception e) {
 Toast.makeText(_context, "エラーが発生しました。", Toast.LENGTH_SHORT).show();
}

これで画像の保存ができました。

penView.java

package in.andante.drawbm;

import java.io.File;
import java.io.FileOutputStream;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Environment;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

public class penView extends View {
 
 private float oldx = 0f;
 private float oldy = 0f;
 private Bitmap bmp = null;
 private Canvas bmpCanvas;
 private Paint paint;
 private Activity _context;
 
public penView(Context context) {
 super(context);
 _context = (Activity)context;
 paint = new Paint();
 paint.setColor(Color.MAGENTA);
 paint.setAntiAlias(true);
 paint.setStyle(Paint.Style.STROKE);
 paint.setStrokeWidth(6);
 paint.setStrokeCap(Paint.Cap.ROUND);
 paint.setStrokeJoin(Paint.Join.ROUND);
}
 
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 super.onSizeChanged(w,h,oldw,oldh);
 bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
 bmpCanvas = new Canvas(bmp);
 }
 
 
 public void onDraw(Canvas canvas) {
 canvas.drawBitmap(bmp, 0, 0, null);
 }
 
 public boolean onTouchEvent(MotionEvent e){
 switch(e.getAction()){
 case MotionEvent.ACTION_DOWN: //最初のポイント
 oldx = e.getX();
 oldy = e.getY();
 break;
 case MotionEvent.ACTION_MOVE: //途中のポイント
 bmpCanvas.drawLine(oldx, oldy, e.getX(), e.getY(), paint);
 oldx = e.getX();
 oldy = e.getY();
 invalidate();
 break;
 default:
 break;
 }
 return true;
 }
 
 public void clearDrawList(){
 bmpCanvas.drawColor(Color.BLACK);
 invalidate();
 }
 
 public void saveToFile(){
 if(!sdcardWriteReady()){
 Toast.makeText(_context, "SDcardが認識されません。", Toast.LENGTH_SHORT).show();
 return;
 }
 
 File file = new File(Environment.getExternalStorageDirectory().getPath()+"/drawbm/");
 
 
 try{
 if(!file.exists()){
 file.mkdir();
 }
 }catch(SecurityException e){}
 
 String AttachName = file.getAbsolutePath() + "/";
 AttachName += System.currentTimeMillis()+".jpg";
 File saveFile = new File(AttachName);
 while(saveFile.exists()) {
 AttachName = file.getAbsolutePath() + "/" + System.currentTimeMillis() +".jpg";
 saveFile = new File(AttachName);
 }
 try {
 FileOutputStream out = new FileOutputStream(AttachName);
 bmp.compress(CompressFormat.JPEG, 100, out);
 out.flush();
 out.close();
 Toast.makeText(_context, "保存されました。", Toast.LENGTH_SHORT).show();
 } catch(Exception e) {
 Toast.makeText(_context, "例外発生", Toast.LENGTH_SHORT).show();
 }
 }

 private boolean sdcardWriteReady(){
 String state = Environment.getExternalStorageState();
 return (Environment.MEDIA_MOUNTED.equals(state));
 }
}

以上です。

  • written on 2011.04.15
  • category : 描画

前後の記事

前の記事:

次の記事:

関連の記事

コメント

:D teas

このActivityをキャストするのはClassCastException発生しませんか?

:D nitro

メニューからclearやsaveを選択する(Viewクラスのメソッドを呼び出す)と強制終了します。activityクラスから別ファイルのクラスメソッドを呼び出す際に何か必要でしょうか?

:D ryu

SDカードに保存後、保存したものを読み込んでさらに描画を続けることは可能ですか?

:D admin

保存をしたデータでの描画も可能ですね。その際、次回の保存の際には、pathなども変更しなければ上書き保存になるので。

コメントの投稿

  • サイト内検索

新作アプリの紹介

関連サイトの紹介

アンドロイドアプリ開発TIPS
きぐるみカメラ
ふらいぱん
アンドロイドのデザイン集
Page top↑