转载请注明出处:王亟亟的大牛之路
上一篇把简单的一些概念理一理,还画了个圈,那这一篇讲一下图像遮盖“Xfermode”和Canvas的旋转。平移等效果
Xfermode:
AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。
PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素异或操作。
PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。
我们今天的呈现就是在PorterDuffXfermode 这部分实现的
PorterDuff.Mode为枚举类,一共有16个枚举值分别是
1.PorterDuff.Mode.CLEAR
所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC
显示上层绘制图片
3.PorterDuff.Mode.DST
显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER
正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER
上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN
取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN
取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT
取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT
取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP
取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR
异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN
取两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN
取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY
取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN
取两图层全部区域,交集部分变为透明色
在将这一系列的效果之前我们先把我们昨天画圆的例子改一下
public class TestView extends View {
Paint paint;
Context context;
Bitmap bitmap;
public TestView(Context context) {
super(context);
this.context = context;
}
public TestView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public TestView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
paint = new Paint();
// paint.setColor(getResources().getColor(R.color.SlateBlue));
paint.setColor(getResources().getColor(R.color.Gold));
paint.setStrokeWidth(3); //粗细
paint.setAntiAlias(true); //设置画笔为无锯齿
bitmap= BitmapFactory.decodeResource(getResources(),R.drawable.bg);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("--->onDraw", "onDraw()");
// canvas.drawCircle(0, 0, 90, paint);
canvas.drawBitmap(bitmap,0,0,paint);
canvas.drawCircle(100, 100, 90, paint);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.d("--->onLayout", "changed = " + changed + " left = " + left + " top = " + top + " right = " + right + " bottom " + bottom);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d("--->onMeasure", " widthMeasureSpec =" + widthMeasureSpec + " heightMeasureSpec = " + heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
}
我们在画圆之后又画了个背景图,效果如下:
在我们的圆下面有一个妹子,并且圆是会盖掉妹子那一块圆的涂抹面积的。
那我们把onDraw()方法里执行的顺序换一下呢?
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("--->onDraw", "onDraw()");
// canvas.drawCircle(0, 0, 90, paint);
canvas.drawCircle(100, 100, 90, paint);
canvas.drawBitmap(bitmap,0,0,paint);
}
如果改成这样,我们的圆就没了(效果不贴了)
利用PorterDuff.Mode就可以很好的解决这个问题当然,可能你得多绘制一次然后再做覆盖的操作了,所以在你决定先画什么后画什么之前一定理清谁在前谁在后
本来想写个大致的例子,但是想想16个都好麻烦,就拿了一个市面上比较多的图吧
Canvas静态变化
上面的代码不变,我们在新建一个Canvas然后把他里面话一点东西,来模拟我们想要的效果。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("--->onDraw", "onDraw()");
// canvas.drawCircle(0, 0, 90, paint);
canvas.drawCircle(100, 100, 90, paint1);
canvas.drawBitmap(bitmap, 120, 120, paint1);
Canvas canvas1=new Canvas(bitmap);
Paint paint2=new Paint();
paint2.setColor(getResources().getColor(R.color.LightPink));
paint2.setTextSize(50);
canvas1.drawText("Ezreal", 0, 200, paint2);
canvas1.drawText("Malzahar ",0,300,paint2);
}
效果如下:
P1.我们用画布又画了2个字。那么我们来试下各种效果(分析和方法,会分两部分罗列)
平移:1.void translate(float dx, float dy)
平移参数:2个坐标点X正向右 Y正向下,负数反之。
旋转:
1.void rotate(float degrees)
2.void rotate (float degrees, float px, float py)
旋转参数:以坐标原点(左上)为旋转中心转degrees度(正的为正转,负的为反转),px,py为以(px,py)为中心旋转degrees度.
缩放:
1.public void scale (float sx, float sy)
2.public final void scale (float sx, float sy, float px, float py)
缩放参数:sx为水平缩放,1表示不便,大于1放大,小与1缩小(负的我没试过);sy则表示垂直,逻辑同水平.
斜切:
1.void skew (float sx, float sy)
斜切参数:sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值;sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值.
P2.实现以及解释:
1.平移:
肉眼看来我们的2个字偏移了canvas1.translate(300,300);
的位置,效果类似于 2个字体本身的坐标(X坐标+300,Y坐标+300)。
但是 效果是这样吗?
为了验证确实如此我们再再右侧画一个“Akali”
//并且是在平移前执行
canvas1.drawText("Akali ",200,400,paint2);
然后我们在平移之后再画一个Katarina Du Couteau
并且跟平移前的阿卡丽是用以坐标
canvas1.drawText("Katarina Du Couteau ", 200, 400, paint2);
效果是卡特跟着整个坐标系走了而不是之前的坐标相加的结果。
所以:
1.每次调用canvas.drawXXXX系列函数来绘图进,都会产生一个全新的Canvas画布。
2.在Canvas与屏幕合成时,超出屏幕范围的图像是不会显示出来的。
2.旋转
我们还是回到最初的蚂蚱和EZ的样子
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d("--->onDraw", "onDraw()");
// canvas.drawCircle(0, 0, 90, paint);
canvas.drawCircle(100, 100, 90, paint1);
canvas.drawBitmap(bitmap, 120, 120, paint1);
Canvas canvas1 = new Canvas(bitmap);
Paint paint2 = new Paint();
paint2.setColor(getResources().getColor(R.color.LightPink));
paint2.setTextSize(50);
// canvas1.drawText("Akali ", 200, 400, paint2);
// canvas1.translate(300, 300);
canvas1.drawText("Ezreal", 0, 200, paint2);
canvas1.rotate(30);
canvas1.drawText("Malzahar ", 0, 300, paint2);
// canvas1.drawText("Katarina Du Couteau ", 200, 400, paint2);
}
然后把蚂蚱旋转30度,效果如图
EZ位置没变,蚂蚱转了,并且有一部分超出了我们的妹子Bitmap所以它不见了。
然后此时此刻,我们的阿卡丽回来了,并且在蚂蚱之后
我们的啊卡里也跟着蚂蚱转走了,说明旋转和平移是一样的整个位图转走了。
如下图向着箭头方向偏了30度然后创建了一个新的Canvas
3.缩放
缩放的图我就不画了,代码就是canvas.scale(1, 1.5f); 就是Y方向缩放1.5f
也是重绘了一个Canvas和上面都一样。
4.斜切
其实也就是可以实现我们的一个斜体的效果,这里就贴一下执行效果
还有就是int save () void restore()
相对比较简单,对堆栈的概念清晰的小伙伴一看就懂,不需要过多试验,这里就不明说了。