绘图知识
随记
- onDraw绘制背景(一般View用)
- dispatchDraw绘制子View(一般ViewGroup用)
获取画布canvas
//方法一:新建一个空白bitmap
Bitmap bmp = Bitmap.createBitmap(width ,height Bitmap.Config.ARGB_8888);
//方法二:从图片中加载
Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.wave_bg,null);
Canvas c = new Canvas(bitmap);
- 注意,如果我们用这个画布绘制东西,那么他就会保存在bitmap中,而不会显示在View中,而如果想让我们画的这个bitmap图片显示在界面上,我们还需要再用onDraw传进来的canvas将他画一下
对画布的操作
public int saveLayer (RectF bounds, Paint paint)
public int saveLayer(float left, float top, float right, float bottom,Paint paint)
- 这个方法还有一个带saveFlags参数的重载方法,不过在api 26 的时候已经被弃用,所以在这里不在阐述
- 关于这个方法我大概说明一下
- 先说一下图层的含义,其实可以理解成画布,可以这么理解
- 我们每次在用canvas(可以理解成画板,画布被固定在画板)画画的时候,就是先拿一块透明的布,然后在上面画出我们想要的东西
- (然后再设置或者不设置他的图像混合模式)然后再把这个透明的布根据图像混合规则(我们设置的图像混合规则或者默认的混合规则)覆盖到画板上的画布上
- 那么,在调用saveLayer这个方法之后,他就相当于在空中构建了一块透明画布(后面我把这个图层叫做目标图层),在接下来的绘制中除了第一次的绘制会直接在这个目标图层上画东西,比如说画矩形,这个目标图层上就会留下矩形,其余部分为透明,之后的绘制都是拿着一个个透明图层往这个目标图层上根据覆盖规则进行覆盖
- 重点看一下这个方法的用法
- 在这里说一下个人的理解吧,我们每次savelayer的时候,就会相当于创建了一个透明画布,也可以理解成是浮在上面的透明画布,那么我们当前的操作画布的对象就变成了新创建的这个画布,同时,我们在创建这个新画布的时候还将他的信息保存在了一个栈中
- 当我们在调用restore这个方法的时候,从栈顶拿到下层画布的信息,然后将最上面的画布根据我们当时保留的信息中设置的覆盖规则覆盖在下层的画布上面,这个时候我们操作的画布对象就变到了下层这个画布
- 或者在调用restoreToCount这个方法的时候,我们给他显示设置恢复到哪一层,此时的过程就像是调用restore这个方法的循环,知道拿到我们想恢复的那一层画布的操作句柄
- 这里需要注意下,当我们没调用恢复这两个方法的时候,我们所draw的内容也是可以看到的,可以想象成有好多层画布,但是我们当前可以操作的只是最上面一层,想要操作下层画布,只能是将上层画布与下层画布融合,我们才能拿到下层画布的操作对象
- 这里给点代码看一下具体效果
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GRAY);
int layerId0 = canvas.saveLayer(0, 0, 800, 800, paint);
paint.setColor(Color.RED);
canvas.drawRect(100,100,500,500,paint);
int layerId1 = canvas.saveLayer(150,150,450,450,paint);
paint.setColor(Color.BLUE);
canvas.drawRect(150,150,300,300,paint);
int layerId2 = canvas.saveLayer(200,200,450,450,paint);
paint.setColor(Color.GREEN);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
canvas.drawRect(250,250,350,350,paint);
canvas.restore();
paint.setColor(Color.YELLOW);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
canvas.drawRect(200,200,450,450,paint);
}
- 看一下他的效果
- 我先画一个大的红矩形,然后savelayer(图层一)
- 然后画一个蓝色矩形,再saveLayer(图层二)
- 然后设置下一层覆盖的时候的规则是丢弃上层不与下层重合的部分,再画一个绿色的矩形
- 然后再恢复一下,恢复的过程是将图层二的内容按照我们设置的规则覆盖,所以
![000](http://chuantu.biz/t6/316/1526909215x-1404793411.png)
- 然后我在设置一下覆盖规则,再画一个黄色的矩形,所以
![image](http://chuantu.biz/t6/316/1526909351x-1404793411.png)
- 可以看到,本来我们图层二是只有一个蓝色的矩形的,但是他的覆盖规则之下,明显还多了一个矩形,这个矩形当然就是我们刚才画的矩形了,在restore的时候,他将最上层的图层覆盖,等于说这时的下层的图层信息已经不是我们保存的那个了,而是变成了上层覆盖上去之后的信息
- 嗯,应该有个大概理解吧,具体的读者可以根据我的思路试一下就会明白
- 这里好像那个覆盖规则跟官方文档说明的有些不一样,这里就不深究了,重点在明白图层之间的操作关系
- 额,注意 savelayer是非常浪费空间的,所以一定不要说为了简单直接生成全屏幕的目标画布,不然程序很容易因为占用内存太大而被kill掉,一定要选择适合自己想要的大小生成
- 有透明度的新生画布
public int saveLayerAlpha(RectF bounds, int alpha)
public int saveLayerAlpha(float left, float top, float right, float bottom,int alpha)
- 这个方法增加了一个指定新生画布透明度的参数,这个表示我们在新生画布上的所有操作在合并到原始画布的时候会以这个透明度展示,这个值的范围是0-255