关于ondraw你该知道的(一)

目录

1.'画板'canvas

2.画布canvas的操作

0)canvas绘制前后,加入操作

1)平移

2)旋转

3)截取

4)画布的锁定

5)图层Layer合成

6)图层Layout创建和移除

3.PorterDuffXfermode

1)交集区域的16种处理及展示

2)利用PorterDuffXfermode制作圆角图

4.颜色矩阵ColorMatrix

1)原理

2)利用封装类ColorMatrix处理颜色

3)矩阵值改变原色

5.处理bitmap像素点


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);
    }

https://i-blog.csdnimg.cn/blog_migrate/dbeaf95f98473d5d4e7e41f66f6fcdeb.png

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);
}

https://i-blog.csdnimg.cn/blog_migrate/028d0f9aed06ad53415c13f394461f7c.png

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);
    }

https://i-blog.csdnimg.cn/blog_migrate/3ccab990eeb3c4e6eb26c90a4ea37926.png

注意:

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);
}

https://i-blog.csdnimg.cn/blog_migrate/4365947b89dcda80cdcb8d3d326ff096.png

绘制初始文字和图片的2个图层合成之后,canvas的旋转属性也消失了,通过最后画的文字的旋转角度可知。

6)图层Layout创建和移除

可以通过saveLayout()或者saveLayoutAlpha()去创建图层,创建的新图层遵循'先入后出'的概念,即新创建的图层永远在最上层,因此,我们画的内容也是绘制在最上层。

同样通过restore和restoreTocount()将图层出栈,出栈后,绘制的内容也是绘制在最上层的Layout图层上。

3.PorterDuffXfermode

1)交集区域的16种处理及展示

PorterDuffXfermode可以给画笔加上一些高级属性,可以设置两个图层交集区域的显示方式。

下面看看PorterDuffXfermode的16个定义常量对绘图效果的最终影响:

https://i-blog.csdnimg.cn/blog_migrate/697f26c371161ff9ec8e2e1f66cd7e10.png

解释如下:

https://i-blog.csdnimg.cn/blog_migrate/3eea6bede6629c9c3ed322700fd11efa.png

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);
    }

 原图:

https://i-blog.csdnimg.cn/blog_migrate/b2a1241c32ac394d3d0f109b868995c9.png

处理后:

https://i-blog.csdnimg.cn/blog_migrate/355fd04e831c77191563164924b20ded.png

4.颜色矩阵ColorMatrix

1)原理

图像描述由3部分组成:

  1. 色调(物体传播的颜色)
  2. 饱和度(颜色的纯度)    由0(灰)----->100%(饱和)
  3. 亮度

而在android中,系统用一个4×5的颜色矩阵ColorMatrix来处理图像。

https://i-blog.csdnimg.cn/blog_migrate/10b143d19f5d4cc86f9f0f7da9dc4166.png

https://i-blog.csdnimg.cn/blog_migrate/35c6742b6bcae0c67dc04c94a62127f6.png

初始颜色矩阵,不对原有的颜色值进行任何变化

https://i-blog.csdnimg.cn/blog_migrate/fcc14056f00c7be629fba890721c2baa.png

分别可以通过改变系数  或者 偏移量去修改原有颜色值

https://i-blog.csdnimg.cn/blog_migrate/d55d3bd0cf9a6297f85b578790e8217a.png

https://i-blog.csdnimg.cn/blog_migrate/97c57d3066ef5ffdc15696122683b832.png

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);
    }

原图:

https://i-blog.csdnimg.cn/blog_migrate/b2a1241c32ac394d3d0f109b868995c9.png

 

变化后:

https://i-blog.csdnimg.cn/blog_migrate/6e6535d0134f6535b9f2c13d3ca5ac9b.png

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

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);

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值