目录
1.'画板'canvas
像画画一样,需要画板(canvas)和画笔(paint)
至于画板画什么,和用什么样的画笔,参考:https://blog.csdn.net/qq_37321098/article/details/84959425
安卓中用画板canvas画画,还需要传入bitmap,将bitmap和canvas绑定在一起(装载画布过程)。
(官方也不推荐使用无参方法构造canvas,需调用setBitmap函数为其设置一个Bitmap),看看源码验证一下:
//bitmap被限定非非空了
public Canvas(@NonNull Bitmap bitmap) {
if (!bitmap.isMutable()) {
throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
}
...
}
bitmap作用是用来存储所有绘制在canvas上的像素信息,所以通过这种方式创建的canvas,后面调用的Canvas.drawXX都是发生在这个bitmap上面。
换句话说:我们调用了许多Canvas.drawXX,但是其实并没有将图形直接绘制在canvas上,而是通过改变bitmap,让view重绘,从而显示改变后的bitmap。
问题:view的ondraw方法,为什么没看见bitmap绑定canvas过程?
这个系统传递给我们的canvas来自于ViewRootImpl的Surface,在绘图时系统将会SkBitmap设置到SkCanvas中并返回与之对应Canvas。所以,在onDraw()中也是有一个Bitmap的,只是这个Bitmap是由系统创建的罢了。
2.画布canvas的操作
0)canvas绘制前后,加入操作
@Override
protected void onDraw(Canvas canvas) {
//控件自身绘制前,如果是textview,则是绘制文字操作前
super.onDraw(canvas);
//控件自身绘制后,如果是textview,则是绘制文字操作后
}
1)平移
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
//初始文字
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(60);
canvas.drawText("画布初始位置",100,100,paint);
//平移后画的文字
canvas.translate(100,100);
paint.setColor(Color.RED);
canvas.drawText("画布变化后位置",100,100,paint);
}
2)旋转
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
//初始文字
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(60);
canvas.drawText("画布初始位置",100,100,paint);
//旋转后画的文字
canvas.rotate(15);
paint.setColor(Color.RED);
canvas.drawText("画布变化后位置",100,100,paint);
}
3)截取
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
//初始文字
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(60);
canvas.drawText("画布初始位置",100,100,paint);
//裁剪后画的文字
Rect rect = new Rect(10,200,600,500);
canvas.clipRect(rect);
canvas.drawColor(Color.BLUE);
paint.setColor(Color.RED);
canvas.drawText("画布变化后位置",10,250,paint);
}
注意:
1.调用了canvas.clipRect( )后,再继续画图那么所绘的图只会在所剪裁的范围内体现
2.除了按照矩形剪裁以外,还可以有别的剪裁方式,比如:canvas.clipPath( )和canvas.clipRegion( )
4)画布的锁定
通过canvas.save锁定画布,将已经所绘的图形锁定,之后的绘图就不会影响到原来画好的图形。
之后的操作又发生在哪里呢?会生成一个新的图层(Layer),并且这个图层是透明的。此时,所有draw的方法都是在这个图层上进行,所以不会对之前画好的图形造成任何影响。
5)图层Layer合成
通过canvas.restore将canvas.save( )时会生成一个新的图层(Layer)与底下原本的画好的图像相结合形成一个新的图像。
因此,save( )和restore( )最好配对使用,若restore( )的调用次数比save( )多可能会造成异常绘制。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.GREEN);
//初始文字
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(60);
canvas.drawText("画布初始位置",100,100,paint);
canvas.save();
//save+旋转后画的图
canvas.rotate(15);
Bitmap bit= BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
canvas.drawBitmap(bit, 100, 100, paint);
canvas.restore();
//合成后画字
paint.setColor(Color.RED);
canvas.drawText("画布变化后位置",250,250,paint);
}
绘制初始文字和图片的2个图层合成之后,canvas的旋转属性也消失了,通过最后画的文字的旋转角度可知。
6)图层Layout创建和移除
可以通过saveLayout()或者saveLayoutAlpha()去创建图层,创建的新图层遵循'先入后出'的概念,即新创建的图层永远在最上层,因此,我们画的内容也是绘制在最上层。
同样通过restore和restoreTocount()将图层出栈,出栈后,绘制的内容也是绘制在最上层的Layout图层上。
3.PorterDuffXfermode
1)交集区域的16种处理及展示
PorterDuffXfermode可以给画笔加上一些高级属性,可以设置两个图层交集区域的显示方式。
下面看看PorterDuffXfermode的16个定义常量对绘图效果的最终影响:
解释如下:
2)利用PorterDuffXfermode制作圆角图
//绘制内容
private Bitmap mBitmap;
//占位bitmap
private Bitmap mOutBitmap;
public void init() {
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.draw_img);
mOutBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);//关掉硬件加速
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawRoundRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), 30, 30, paint);
//交集只显示上层
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(mBitmap,0,0,paint);
}
原图:
处理后:
4.颜色矩阵ColorMatrix
1)原理
图像描述由3部分组成:
- 色调(物体传播的颜色)
- 饱和度(颜色的纯度) 由0(灰)----->100%(饱和)
- 亮度
而在android中,系统用一个4×5的颜色矩阵ColorMatrix来处理图像。
初始颜色矩阵,不对原有的颜色值进行任何变化
分别可以通过改变系数 或者 偏移量去修改原有颜色值
2)利用封装类ColorMatrix处理颜色
a.修改色调
ColorMatrix colorMatrix1 = new ColorMatrix();
//第一个参数表示处理哪种颜色:0代表红 1绿色 2蓝色
//第二个参数表示处理的值
colorMatrix1.setRotate(0,100);
b.修改饱和度
ColorMatrix colorMatrix2 = new ColorMatrix();
//第一个参数表示处理的值
colorMatrix2.setSaturation(0);
c.修改亮度
//利用原理:三原色已相同比例混合,会显示白色
int value = 1;
ColorMatrix colorMatrix3 = new ColorMatrix();
//4个参数分别为R-G-B-A要处理的值
colorMatrix3.setScale(value,value,value,1);
d.组合a、b、c三种效果
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.postConcat(colorMatrix1);
colorMatrix.postConcat(colorMatrix2);
colorMatrix.postConcat(colorMatrix3);
3)矩阵值改变原色
图像反转矩阵:
//绘制内容
private Bitmap mBitmap;
//占位bitmap
private Bitmap mOutBitmap;
//各种算法矩阵,可自行百度实验
//图像反转矩阵
// -1,0,0,1,1,
// 0,-1,0,1,1,
// 0,0,-1,1,1,
// 0,0,0,1,0
float value[] = {-1,0,0,1,1,0,-1,0,1,1,0,0,-1,1,1,0,0,0,1,0};
public void init() {
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.draw_img);
mOutBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
Bitmap.Config.ARGB_8888);
//关掉硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//传入颜色矩阵值
android.graphics.ColorMatrix colorMa = new android.graphics.ColorMatrix();
colorMa.set(value);
//绘图
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMa));
canvas.drawBitmap(mBitmap,0,0,paint);
}
原图:
变化后:
5.处理bitmap像素点
不会改变原图,而是拿到原图像素点,并生成新图来修改。
先拿到原图像素点,且像素点是用数组存储,函数如下:
Biamap.getPixels(int[] pixels, int offset, int stride, int x, int y, int width, int height)
参数:
pixels:接收位图颜色值的数组
offset:写入到pixels[]中的第一个像素索引值
stride:pixels[]中的行间距个数值(必须大于等于位图宽度)。不能为负数
x:从位图中读取的第一个像素的x坐标值。
y:从位图中读取的第一个像素的y坐标值
width:从每一行中读取的像素宽度
height:读取的行数
参考:https://www.cnblogs.com/fordreamxin/p/4605693.html
当我们拿到像素点数组oldPx后,还可以拿到每个像素点的RGBA值,如下:
int r=Color.red(oldPx[0]);
拿到rgba后,就可以调用一些图像处理的算法,去改变RGBA值,从而将改变后的RGBA值合成新的像素点,如下:
newPx[0]=Color.argb(a1,r1,g1,b1);
最终将新像素点数组,设置给bitmap,达到修改的目的,如下:
bitmap.setPixels(newPx,0,width,0,0,width,height);