标签:
前言:猛然知道姥姥79了,我好怕,好想哭
系列文章:
Android自定义控件三部曲文章索引:http://blog.csdn.net/harvic880925/article/details/50995268
在给大家讲解了paint的几个方法之后,我觉得有必要插一篇有关Canvas画布的知识,在开始paint之前,我们讲解了canvas绘图的几篇文章和cavas的save()、store()的知识,这篇是对Canvas的一个系统的补充,前几篇文章链接如下:
《自定义控件之绘图篇(一):概述及基本几何图形绘制》
《 自定义控件之绘图篇(二):路径及文字》
《自定义控件之绘图篇(三):区域(Range)》
《自定义控件之绘图篇(四):canvas变换与操作》
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
}可以看到onDraw、dispatchDraw在传入的参数中都有一个canvas对象。这个canvas对象是View中的Canvas对象,利用这个canvas对象绘图,效果会直接反应在View中; Canvas c = new Canvas(bitmap);或
Canvas c = new Canvas(); c.setBitmap(bitmap);其中bitmap可以从图片加载,也可以创建,有下面几种方法
//方法一:新建一个空白bitmap Bitmap bmp = Bitmap.createBitmap(width ,height Bitmap.Config.ARGB_8888); //方法二:从图片中加载 Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.wave_bg,null);这两个方法是最常用的,除了这两个方法以外,还有其它几个方法(比如构造一个具有matrix的图像副本——前面示例中的倒影图像),这里就不再涉及了,大家可以去查看Bitmap的构造函数。
public class BitmapCanvasView extends View {
private Bitmap mBmp;
private Paint mPaint;
private Canvas mBmpCanvas;
public BitmapCanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mBmp = Bitmap.createBitmap(500 ,500 , Bitmap.Config.ARGB_8888);
mBmpCanvas = new Canvas(mBmp);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setTextSize(100);
mBmpCanvas.drawText("启舰大SB",0,100,mPaint);
}
}我们先看一下运行结果:可以看到,毛线也没有,这是为什么呢?
我们仔细来看一下onDraw函数:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setTextSize(100);
mBmpCanvas.drawText("启舰大SB",0,100,mPaint);
}在onDraw函数中,我们只是将文字画在了mBmpCanvas上,也就是我们新建mBmp图片上!这个图片跟我们view没有任何关系好吧,我们需要把mBmp图片画到view上才行,所以我们在onDraw中需要加下面这句,将mBmp画到view上canvas.drawBitmap(mBmp,0,0,mPaint);所以改造后的代码为:
public class BitmapCanvasView extends View {
private Bitmap mBmp;
private Paint mPaint;
private Canvas mBmpCanvas;
public BitmapCanvasView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mBmp = Bitmap.createBitmap(500 ,500 , Bitmap.Config.ARGB_8888);
mBmpCanvas = new Canvas(mBmp);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setTextSize(100);
mBmpCanvas.drawText("启舰大SB",0,100,mPaint);
canvas.drawBitmap(mBmp,0,0,mPaint);
}
}这时候效果为:/** * 保存指定矩形区域的canvas内容 */ public int saveLayer(RectF bounds, Paint paint, int saveFlags) public int saveLayer(float left, float top, float right, float bottom,Paint paint, int saveFlags)
public class XfermodeView extends View {
private int width = 400;
private int height = 400;
private Bitmap dstBmp;
private Bitmap srcBmp;
private Paint mPaint;
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
srcBmp = makeSrc(width, height);
dstBmp = makeDst(width, height);
mPaint = new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
int layerID = canvas.saveLayer(0, 0, width * 2, height * 2, mPaint, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(dstBmp, 0, 0, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBmp, width / 2, height / 2, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerID);
}
// create a bitmap with a circle, used for the "dst" image
static Bitmap makeDst(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
c.drawOval(new RectF(0, 0, w, h), p);
return bm;
}
// create a bitmap with a rect, used for the "src" image
static Bitmap makeSrc(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
c.drawRect(0, 0, w, h, p);
return bm;
}
}这段代码大家应该很熟悉,这是我们在讲解setXfermode()时的示例代码,但在saveLayer前把整个屏幕画成了绿色,效果图如下:那么问题来了,如果我们把saveLayer给去掉,看看会怎样:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
canvas.drawBitmap(dstBmp, 0, 0, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBmp, width / 2, height / 2, mPaint);
mPaint.setXfermode(null);
}效果图就变这样了:我擦类……去掉saveLayer()居然效果都不一样了……
我们先回顾下Mode.SRC_IN的效果:在处理源图像时,以显示源图像为主,在相交时利用目标图像的透明度来改变源图像的透明度和饱和度。当目标图像透明度为0时,源图像就完全不显示。
再回过来看结果,第一个结果是对的,因为不与圆相交以外的区域透明度都是0,而第二个图像怎么就变成了这屌样,源图像全部都显示出来了。
int layerID = canvas.saveLayer(0, 0, width * 2, height * 2, mPaint, Canvas.ALL_SAVE_FLAG); canvas.drawBitmap(dstBmp, 0, 0, mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(srcBmp, width / 2, height / 2, mPaint);我们讲过,在画源图像时,会把之前画布上所有的内容都做为目标图像,而在saveLayer新生成的bitmap上,只有dstBmp对应的圆形,所以除了与圆形相交之外的位置都是空像素。
savelayer新建的画布上的图像做为目标图像,矩形所在的透明图层与之相交,计算结果画在新建的透明画布上。最终将计算结果直接盖在原始画布上,形成最终的显示效果。
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
canvas.drawBitmap(dstBmp, 0, 0, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBmp, width / 2, height / 2, mPaint);
mPaint.setXfermode(null);
}由于我们先把整个画布给染成了绿色,然后再画上了一个圆形,所以在应用xfermode来画源图像的时候,目标图像当前Bitmap上的所有图像了,也就是整个绿色的屏幕和一个圆形了。所以这时候源图像的相交区域是没有透明像素的,透明度全是100%,这也就不难解释结果是这样的原因了。 由于没有调用saveLayer,所以圆形是直接画在原始画布上的,而当矩形与其相交时,就是直接与原始画布上的所有图像做计算的。
所以有关saveLayer的结论来了:
saveLayer会创建一个全新透明的bitmap,大小与指定保存的区域一致,其后的绘图操作都放在这个bitmap上进行。在绘制结束后,会直接盖在上一层的Bitmap上显示。
public int saveLayer(RectF bounds, Paint paint, int saveFlags) public int saveLayer(float left, float top, float right, float bottom,Paint paint, int saveFlags)我们前面提到了saveLayer会新建一个画布(bitmap),后续的所有操作都是在这个画布上进行的。下面我们来分别看下saveLayer使用中的注意事项
public class SaveLayerUseExample_3_1 extends View{
private Paint mPaint;
private Bitmap mBitmap;
public SaveLayerUseExample_3_1(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.dog);;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap,0,0,mPaint);
int layerID = canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.ALL_SAVE_FLAG);
canvas.skew(1.732f,0);
canvas.drawRect(0,0,150,160,mPaint);
canvas.restoreToCount(layerID);
}
}效果图如下:在onDraw中,我们先在view的原始画布上画上了小狗的图像,然后利用saveLayer新建了一个图层,然后利用canvas.skew将新建的图层水平斜切45度。所以之后画的矩形(0,0,150,160)就是斜切的。
而正是由于在新建画布后的各种操作都是针对新建画布来操作的,不会对以前的画布产生影响,从效果图中也明显可以看出,将画布水平斜切45度也只影响了saveLayer的新建画布,并没有对之前的原始画布产生影响。
public class SaveLayerUseExample_3_1 extends View {
private Paint mPaint;
private Bitmap mBitmap;
public SaveLayerUseExample_3_1(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog);
;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
int layerID = canvas.saveLayer(0, 0, 100, 100, mPaint, Canvas.ALL_SAVE_FLAG);
canvas.drawRect(0, 0, 500, 600, mPaint);
canvas.restoreToCount(layerID);
}
}效果图如下:
public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) public int saveLayerAlpha(float left, float top, float right, float bottom,int alpha, int saveFlags)相比saveLayer,多一个alpha参数,用以指定新建画布透明度,取值范围为0-255,可以用16进制的oxAA表示;
public class SaveLayerAlphaView extends View {
private Paint mPaint;
public SaveLayerAlphaView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setColor(Color.RED);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(100,100,300,300,mPaint);
int layerID = canvas.saveLayerAlpha(0,0,600,600,0x88,Canvas.ALL_SAVE_FLAG);
mPaint.setColor(Color.GREEN);
canvas.drawRect(200,200,400,400,mPaint);
canvas.restoreToCount(layerID);
}
}效果图如下:在saveLayerAlpha以后,我们画了一个绿色的矩形,由于把saveLayerAlpha新建的矩形的透明度是0x88(136)大概是50%透明度,从效果图中也可以看出在新建图像与上一画布合成后,是具有透明度的。
好了,这篇文章就先到这里,下一篇详细给大家讲解有关参数中各个Flag的意义。
如果本文有帮到你,记得加关注哦
源码下载地址:http://download.csdn.net/detail/harvic880925/9510189 (与第二篇对应资源合并在一个工程中)
请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/51317746 谢谢
如果你喜欢我的文章,那么你将会更喜欢我的微信公众号,将定期推送博主最新文章与收集干货分享给大家(一周一次)
自定义控件三部曲之绘图篇(十三)——Canvas与图层(一)
标签:
原文地址:http://blog.csdn.net/harvic880925/article/details/51317746