标签:
前言:猛然知道姥姥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