关于Android中的画笔画画

我们自己画的时候,通常要用到三个类, Canvas, Bitmap, Paint 这里主要是讲这三个是啥。

  • bitmap, canvas, paint 区别, 联系, 使用关系

关系简述

Canvas 与 paint的关系,可以用一句话描述, 就是Canvas相当于一个画匠,可以决定画出一个什么轮廓,画出来的是一个人物也好,一个方框也好,总之他就决定要画什么东西。而paint就相当于画匠手中的笔, 你在画笔上蘸上红色的颜料也好,绿色的颜料也好,总之画笔中总有一些属性能够对应现实中的这些要求。

对于一个画匠而言, 你如果不告诉他应该画到哪里的话,显然是无法动工的。 画到哪里其实指的就是画到哪段内存空间上。如果不知道应该画到哪块内存去(简单来说,就不知道开辟哪块空间,以便于canvas画形状,用画笔上色的时候, 底层算法到底要根据哪块空间的数据计算以及计算出来的数据放在哪块空间)那就歇菜了。 这也就是canvas有个构造方法,必须传入一个参数的原因。 因为这个参数是十分重要的,根据这个就能找到对应的空间,就能根据自己画什么而计算出对应空间的值。 而这个参数就是Bitmap!

/**
     * Construct a canvas with the specified bitmap to draw into. The bitmap
     * must be mutable.
     *
     * <p>The initial target density of the canvas is the same as the given
     * bitmap's density.
     *
     * @param bitmap Specifies a mutable bitmap for the canvas to draw into.
     */
public Canvas(@NonNull Bitmap bitmap){
        ...
}

所以我认为网上将 canvas比喻成一个画布(尽管canvas就是画布的意思),其实是不太贴切的。如果是画布,肯定会有一个画匠的角色会依赖这个“画布”,并且依赖一支笔来对画布进行操作的。 而画布,实际上就是一块空间,再联系canvas的成员方法看看,显然将canvas比喻成画布着实不太符合我自身对事物的影射认知。反而bitmap却可以很好的对应一段空间。他当画布我认为没毛病!

Bitmap

Bitmap最最最重要的是,通过它可以找到图片数据的存储位置。在不同的Android 版本,尽管这段数据存储会有不同, 比如最初存到native那块,后来改成了存到了java相关的堆中, 再后来又该到了native那里, 尽管变来变去,但是始终不变的是,通过这个类,可以找到图片数据的内存在哪里。
Bitmap的相关api,实际上存在了大量的重载方法,各种花式createBitmap()重载。其本质大部分是开辟空间,这个建议看下Android源码,这个类java代码量并不是很多。

Canvas

对于这个类,不熟悉, 但绝对见过!而且十分令人印象深刻的是在Android 源码中, 一个举世闻名的类–View:

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
	此处省略一万多行代码。。。。
	/*直至一处举世闻名的方法*/
/**
     * Implement this to do your drawing.
     *
     * @param canvas the canvas on which the background will be drawn
     */
    protected void onDraw(Canvas canvas) {
    }
}

这个方法中的参数就是Canvas!
当我们在自定义布局的时候, 尤其是碰到比较奇葩的界面, 普通布局加上各种style怎么拼凑也出不来效果, 或者是碰到UI比较奇葩的要求的时候。 我们往往会重写这个方法, 并且利用这个Canvas参数去画各种图形。

但是至少我是这样的,尽管我写过不止一次这样的布局, 也同时用到了 Canva, Bitmap, paint这些工具类。 但我那么多次之后依然不长记性,搞不清这仨什么联系什么用法,影射现实中,它究竟是个啥,这我也没有弄得很清楚, 学渣惭愧啊。以至于每次写相关代码全靠百度。 频频被用到但从未被记住! Canvas好好思考一下为什么没有被本尊记住吧。

Canvas是什么

Canvas在现实理解上应该对应的是一个画匠。
我们通过看Canvas的源码得知,Canvas实际上相当于一个包装类而已,其功能聚焦于“画什么东西(形状?图片?路径?都可以!)”其内部的实现,画各种各样的图形,实际上最后调用的是native方法。而这些native方法,说白了就是对一块内存区域的数据进行计算,使得数据最后经屏幕绘制的时候,正好是你想要的那些图形。你可以理解为Canvas是一个强大的计算接口,是数据操刀者。

Canvas api 以及注意项

Canvas 的api总的来说是很简单很容易理解的,但是有些api的参数需要说明一下,因为这些参数跟平常理解的可能有偏差,尽管最终都能找到正确的解决办法,但是记下来这个可以省下试错的时间。

  • public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)
  • 之所以记录这个方法,是因为我用到了,并且感觉有点怪。
  • 方法的含义是,将参数中的bitmap, 画到Canvas本身持有的Bitmap上,实际上就是以Canvas的Bitmap为底的基础上再画一个bitmap。
  • bitmap 参数, 指的是用户想要添加的新增的新的bitmap。
  • left, top参数
  • 1 坐标与Android屏幕坐标标准一样,
  • 但是不同的是 left top 很明显指的是一个点。而这个点是 Canvas本身持有的那个bitmap里面的某个点。 然后以这个点为起点,开始画新的bitmap, 如果新的bitmap过大,那么很可能结果是只展示一部分的。 这块理解不好,很容易出现画错位置的情况。不是说它很重要,而是如果你记住了,写代码的时候可以省去很多试错的时间。
  • public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, @Nullable Paint paint)
  • 这个方法意思可以描述为, 截取要画的源图片的某些部分Rect, 画到Canvas本身持有的Bitmap的某个位置RectF。 至于Rect 与RectF 这俩类的区别,是另外一个话题。先不关注。
  • bitmap参数 指的是用户想要添加的新增的新的bitmap。
  • Rect src 指的是新要画的这个bitmap, 要截取的是哪个区域,这个类就是描述这种区域的。
  • RectF dst 指的是要画到Canvas本身持有的Bitmap的哪块区域里,这个类是描述目标区域的。 (这个方法显然要比上面的那个方法灵活一些, 但是写的时候也相对复杂些)
  • paint 稍后研究用处
  • canvas的api侧重于画图形,画区域。画文字等等。其主要是将画画成什么样。

paint

是一个画笔, 就像photoshop中的画笔工具似的,可以设置颜色,粗细等等。

paint api 以及注意项

paint这个类的api给出的是十分丰富的。这个建议用到的时候再研究。