保存画板中绘制的轨迹,有两种方法:
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>