1.聊一聊双缓存技术
为什么叫“双缓存”,其实就是有两个绘图区,一个是Bitmap的Canvas,另一个则是当前View的Canvas。
使用双缓存的意义:
1)提高绘图的性能
2)可以在屏幕上展示绘图的过程
3)保存绘图历史
(1)下面是使用双缓存来绘制图形的代码
private Paint paint;
/**
* 上一个点的坐标
*/
private int preX, preY;
/**
* 当前点的坐标
*/
private int currentX, currentY;
/**
* Bitmap 缓存区
*/
private Bitmap bitmapBuffer;
private Canvas bitmapCanvas;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (bitmapBuffer == null) {
int width = getMeasuredWidth();//获取 View 的宽度
int height = getMeasuredHeight(); //获取 View 的高度
//新建 Bitmap 对象
bitmapBuffer = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmapBuffer);
}
}
// public void run() {
// new Timer().schedule(new TimerTask() {
// @Override
// public void run() {
// postInvalidate();
// }
// }, 0, 1000);
// }
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//将 Bitmap 中的内容绘制在 View 上
canvas.drawBitmap(bitmapBuffer, 0, 0, null);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 手指按下,记录第一个点的坐标
preX = x;
preY = y;
break;
case MotionEvent.ACTION_MOVE:
// 手指移动,记录当前点的坐标
currentX = x;
currentY = y;
bitmapCanvas.drawLine(preX, preY, currentX, currentY, paint);
this.invalidate();
// 当前点的坐标成为下一个点的起始坐标
preX = currentX;
preY = currentY;
break;
case MotionEvent.ACTION_UP:
invalidate();
break;
default:
break;
}
return true;
}
}
首先我们创建了一个bitmapBuffer的Bitmap对象,为了在上面绘图,我们又创建了一个与之关联的Canvas对象bitmapCanvas。
在创建Bitmap时需要考虑他的大小,但是此时myView尚未创建,所以重写了onSizeChanged(),该方法在组件创建后且大小发生改变时回调(在第一次显示时肯定会调用)。
在上面的代码中我们使用的是直接绘制直线的方法,但是更好的做法是通过Path来绘图。
1)可以保存实时的绘图坐标。
2)可以用来绘制更加复杂的图形。
3)绘图效率更高。
public class MyView extends View {
private Path path;
private int preX, preY;
private Paint paint;
private Bitmap bitmapBuffer;
private Canvas bitmapCanvas;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
path = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmapBuffer, 0, 0, null);
canvas.drawPath(path, paint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (bitmapBuffer == null) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
bitmapBuffer = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmapBuffer);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.reset();
preX = x;
preY = y;
path.moveTo(x, y);
break;
case MotionEvent.ACTION_MOVE:
path.quadTo(preX, preY, x, y);
invalidate();
preX = x;
preY = y;
break;
case MotionEvent.ACTION_UP:
bitmapCanvas.drawPath(path, paint);
invalidate();
break;
default:
break;
}
return true;
}
}
在这里我们绘制曲线的时候我们采用的是二阶贝塞尔曲线,其中贝塞尔曲线的控制点和起点为同一个点。
(2)在屏幕上绘制矩形
public class MyView extends View {
private int firstX, firstY;//暂时存储手指落下的位置
private Path path;
private Paint paint;
private Bitmap bitmapBuffer;
private Canvas bitmapCanvas;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
path = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmapBuffer, 0, 0, null);
canvas.drawPath(path, paint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (bitmapBuffer == null) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
bitmapBuffer = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmapBuffer);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
firstX = x;
firstY = y;
break;
case MotionEvent.ACTION_MOVE:
//绘制矩形时,要先清除前一次的结果
path.reset();
if (firstX < x && firstY < y) {
path.addRect(firstX, firstY, x, y, Path.Direction.CCW);
} else if (firstX > x && firstY > y) {
path.addRect(x, y, firstX, firstY, Path.Direction.CCW);
} else if (firstX > x && firstY < y) {
path.addRect(x, firstY, firstX, y, Path.Direction.CCW);
} else if (firstX < x && firstY > y) {
path.addRect(firstX, y, x, firstY, Path.Direction.CCW);
}
invalidate();
break;
case MotionEvent.ACTION_UP:
bitmapCanvas.drawPath(path, paint);
invalidate();
break;
default:
break;
}
return true;
}
}
如果要实现撤销删除的功能,可以将每次绘制后的图形暂存到List中,这样每次绘制都需要遍历整个List从而绘制出全部的图形。
由于Bitmap十分占用系统资源,撤销后需要及时调用Bitmap.recycle()方法进行回收。
本文深入探讨了双缓存技术的工作原理及其在提高绘图性能、展示绘图过程和保存绘图历史方面的作用。通过具体代码示例,展示了如何利用Bitmap和Canvas实现双缓存,包括直线和曲线的绘制,以及在屏幕上绘制矩形的方法。同时,文章还讨论了如何通过Path对象优化绘图效率,以及实现撤销功能的策略。
1610

被折叠的 条评论
为什么被折叠?



