最近在做android 涂鸦,采用的是在 FramLayout布局,后面一层加载的是背景图片,前面一层是自定义的view
public class PicEditView extends View {
private int screenWidth, screenHeight;// 屏幕長寬
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;// 画布的画笔
private Paint mPaint;// 真实的画笔
private float mX, mY;// 临时点坐标
private static final float TOUCH_TOLERANCE = 4;
public PicEditView(Context context, int w, int h) {
this(context, null);
screenWidth = w;
screenHeight = h;
mBitmap = Bitmap.createBitmap(screenWidth, screenHeight,
Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);// 设置外边缘
mPaint.setStrokeCap(Paint.Cap.SQUARE);// 形状
mPaint.setStrokeWidth(5);// 画笔宽度
}
public void setPaintColor(int color){
mPaint.setColor(color);
}
@Override
protected void onAttachedToWindow() {
// TODO Auto-generated method stub
super.onAttachedToWindow();
}
public PicEditView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0x00000000);
// 将前面已经画过得显示出来
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
if (mPath != null) {
// 实时的显示
canvas.drawPath(mPath, mPaint);
}
super.onDraw(canvas);
}
private void touch_start(float x, float y) {
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(mY - y);
//触摸间隔大于阈值才绘制路径
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
// 从x1,y1到x2,y2画一条贝塞尔曲线,更平滑(直接用mPath.lineTo也是可以的)
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
}
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 每次down下去重新new一个Path
mPath = new Path();
touch_start(x, y);
System.out.println("ACTION_DOWN");
this.postInvalidate();
// invalidate();
break;
case MotionEvent.ACTION_MOVE:
System.out.println("--MOVE--");
touch_move(x, y);
this.postInvalidate();
// invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
System.out.println("--ACTION_UP--");
this.postInvalidate();
// invalidate();
break;
}
return true;
}
}
然而,在demo上,绘制很实时流畅,整合进代码里,就有很明显的延时与卡顿,对比分析后发现,是由于从相册获取的背景层图片比较大,消耗了内存,导致绘制的延时。
因此在加载图片是,缩小了图片的大小,从而可以流畅的涂鸦。
try {
BitmapFactory.Options options = new BitmapFactory.Options();
//图片宽高都为原来的二分之一,即图片为原来的四分之一
//这样可以避免内存消耗,否则,后面绘图时,会出现很大的延时
options.inSampleSize = 2;//
bitmap = BitmapFactory.decodeStream(getContentResolver()
.openInputStream(imageFileUri),null,options);
imgView.setImageBitmap(bitmap);
// bitmap.recycle();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
保存涂鸦结果(底图+涂鸦):
layout = (FrameLayout) findViewById(R.id.container);
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
mView = new MView(this, displayMetrics.widthPixels, displayMetrics.heightPixels - dip2px(this, 40));
layout.addView(mView);
findViewById(R.id.tSave).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
layout.setDrawingCacheEnabled(true);
layout.buildDrawingCache();
Bitmap b1 = layout.getDrawingCache();
//copy 缓存图像
Bitmap b = Bitmap.createBitmap(b1);
layout.destroyDrawingCache();//这个之后,b1 就被 recycle了
System.out.println(Environment.getExternalStorageDirectory().getPath() );
File file = new File(Environment.getExternalStorageDirectory().getPath() +"/test1.png");
savePic(b, file);
}
});