儲存畫板中繪製的軌跡,有兩種方法:
1.給canvas設定Bitmap,將軌跡等繪製在Bitmap上,在儲存圖片即可;下次重新進入模組時,載入圖片到Bitmap,再通過canvas繪製出來即可。
2.只儲存軌跡點,下次進入進入時重新繪製;
方法1,當圖片很大時,容易出現OOM異常,這個很難避免。而方法2,可以避免OOM的問題。
下面的代碼是按照方法2來實現的,
工程源碼:
http://download.csdn.net/detail/victoryckl/4519210
:
繪製時通過在onTouchEvent()中,記錄觸摸點,產生Path,在onDraw()繪製即可。
package org.ckl.path;import java.util.ArrayList;import java.util.List;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.ImageView;public class MyView extends ImageView {private static final String TAG = "MyView";private List<PathAndPaint> mPaths = new ArrayList<PathAndPaint>();//儲存每條軌跡的Path和Paint,便於繪製,不可序列化private Path mPath = new Path();private Paint mPaint = new Paint();private PathInfo mPathInfo;//儲存每條軌跡的點座標,可序列化,便於儲存到檔案private int[] mColors = new int[]{Color.BLACK, Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.YELLOW};private void init() {Log.i(TAG, "init()");mPaint.setAntiAlias(true);mPaint.setColor(Color.BLACK);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(3);}public MyView(Context context) {super(context);init();}public MyView(Context context, AttributeSet attrs) {super(context, attrs);init();}public MyView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}public void setPathInfo(PathInfo info) {mPath = new Path();mPathInfo = info;mPaths = mPathInfo.transfer();invalidate();}private int getColor() {int index = (int) (Math.round(Math.random() * mColors.length) % mColors.length);return mColors[index];}private boolean mHasMove = false;private float mX,mY;public boolean onTouchEvent(MotionEvent e) {boolean ret = false;switch (e.getAction()) {case MotionEvent.ACTION_DOWN:mHasMove = false;mX = e.getX();mY = e.getY();mPaint.setColor(getColor());mPath.reset();mPath.moveTo(mX, mY);if (mPathInfo != null) {mPathInfo.lineStart(mX, mY);}//Log.i(TAG, "mPath.moveTo("+mX+"f,"+mY+"f);");invalidate();ret = true;break;case MotionEvent.ACTION_UP:mX = e.getX();mY = e.getY();mPath.lineTo(mX, mY);if (mPathInfo != null && mHasMove) {mPathInfo.lineEnd(mX, mY, mPaint.getColor());}//Log.i(TAG, "mPath.lineTo("+mX+"f,"+mY+"f);");mPaths.add(new PathAndPaint(new Path(mPath), new Paint(mPaint)));invalidate();ret = true;break;case MotionEvent.ACTION_MOVE:mHasMove = true;float x = e.getX();float y = e.getY();mPath.quadTo(mX, mY, (mX + x)/2, (mY + y)/2);if (mPathInfo != null) {mPathInfo.lineMove(x, y);}//Log.i(TAG, "mPath.quadTo("+mX+"f,"+mY+"f,"+(mX + x)/2+"f,"+(mY + y)/2+"f);");mX = x;mY = y;ret = true;invalidate();break;default:ret = super.onTouchEvent(e);break;}return ret;}protected void onDraw(Canvas canvas) {super.onDraw(canvas);for (PathAndPaint pp : mPaths) {canvas.drawPath(pp.getPath(), pp.getPaint());}canvas.drawPath(mPath, mPaint);}}
在android中,android.graphics.Path是不可序列化的,所以不能直接通過ObjectOutputStream儲存。
這裡用PathAndPaint儲存每條軌跡的Path和Paint,便於繪製,不可序列化,PathAndPaint實現如下:
package org.ckl.path;import android.graphics.Paint;import android.graphics.Path;public class PathAndPaint {private Path mPath;private Paint mPaint;public PathAndPaint(Path path, Paint paint) {mPath = path;mPaint = paint;}public Path getPath() {return mPath;}public Paint getPaint() {return mPaint;}}
PathInfo是軌跡的可以序列化表示,為每條軌跡儲存顏色和一系列的座標點,並實現儲存到檔案,從檔案載入,及通過這些資訊恢複PathAndPaint的功能:
package org.ckl.path;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.io.StreamCorruptedException;import java.util.ArrayList;import java.util.List;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.Log;public class PathInfo implements Serializable {private static final long serialVersionUID = -5568568529548959041L;private static final String TAG = "PathInfo";class SerPoint implements Serializable {private static final long serialVersionUID = -2262755099592284491L;private float x;private float y;public SerPoint(float x, float y) {this.x = x;this.y = y;}}class SerPath implements Serializable {private static final long serialVersionUID = -900016536427010833L;private int mColor = Color.BLACK;private List<SerPoint> mPoints = new ArrayList<SerPoint>();}List<SerPath> mSerPaths = new ArrayList<PathInfo.SerPath>();private SerPath mCurPath;public void lineStart(float x, float y) {mCurPath = new SerPath();mCurPath.mPoints.add(new SerPoint(x, y));}public void lineMove(float x, float y) {mCurPath.mPoints.add(new SerPoint(x, y));}public void lineEnd(float x, float y, int color) {mCurPath.mPoints.add(new SerPoint(x, y));mCurPath.mColor = color;mSerPaths.add(mCurPath);}private PathInfo() {}//-轉換為 PathAndPaint -------------------------------private Paint transferPaint(SerPath sp) {Paint paint = new Paint();paint.setAntiAlias(true);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(3);paint.setColor(sp.mColor);return paint;}private Path transferPath(SerPath sp) {Path path = new Path();SerPoint p;int size = sp.mPoints.size();if (size < 3) {return path;}p = sp.mPoints.get(0);path.moveTo(p.x, p.y);float ox = p.x;float oy = p.y;for (int i = 1; i < size-1; i++) {p = sp.mPoints.get(i);path.quadTo(ox, oy, (ox + p.x)/2, (oy + p.y)/2);ox = p.x;oy = p.y;}p = sp.mPoints.get(size-1);path.lineTo(p.x, p.y);return path;}public List<PathAndPaint> transfer() {List<PathAndPaint> pps = new ArrayList<PathAndPaint>();//Log.i(TAG, "mSerPaths.size() = " + mSerPaths.size());for (SerPath sp : mSerPaths) {Paint paint = transferPaint(sp);Path path = transferPath(sp);pps.add(new PathAndPaint(path, paint));}return pps;}//-載入、儲存、清空軌跡------------------------------private static String mSavePath = "/sdcard/.pathinfo";public static PathInfo load() {PathInfo pi = null;ObjectInputStream ois = null;try {ois = new ObjectInputStream(new FileInputStream(mSavePath));pi = (PathInfo)ois.readObject();Log.i(TAG, "load ok, size = " + pi.mSerPaths.size());} catch (StreamCorruptedException e) {e.printStackTrace();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {if (ois != null) {try {ois.close();} catch (IOException e) {e.printStackTrace();}ois = null;}if (pi == null) {pi = new PathInfo();}}return pi;}public void save() {ObjectOutputStream oos = null;try {oos = new ObjectOutputStream(new FileOutputStream(mSavePath));oos.writeObject(this);Log.i(TAG, "save ok, size = " + mSerPaths.size());} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (oos != null) {try {oos.close();} catch (IOException e) {e.printStackTrace();}oos = null;}}}public void clean() {File f = new File(mSavePath);if (f.exists()) {f.delete();}mSerPaths = new ArrayList<PathInfo.SerPath>();}}
package org.ckl.path;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.Button;public class SavePathActivity extends Activity {private PathInfo mPathInfo;private MyView mMyView;private Button mClean, mBack; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mPathInfo = PathInfo.load(); mMyView = (MyView)findViewById(R.id.myview); mMyView.setPathInfo(mPathInfo); mClean = (Button)findViewById(R.id.clean); mClean.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {mPathInfo.clean();mMyView.setPathInfo(mPathInfo);}}); mBack = (Button)findViewById(R.id.back); mBack.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {SavePathActivity.this.finish();}}); } protected void onDestroy() { if (mPathInfo != null) { mPathInfo.save(); mPathInfo = null; } super.onDestroy(); }}
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_weight="1" android:layout_width="0dip" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/clean" android:text="clean" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:id="@+id/back" android:text="back" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout><org.ckl.path.MyView android:id="@+id/myview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:src="@drawable/mj" android:background="@android:color/white"/></LinearLayout>