自定义View Paint 详解-1

本文为专项介绍Paint类

Paint 类

image.png

1.颜色

canvas 绘制内容,有三层颜色处理
image.png

1.1基本颜色

Canvas的颜色填充方法:drawColor/drawRGB/drawARGB() ,直接将颜色值作为参数传入。
drawBitmap() 填充bitmap的颜色,是有Bitmap对象来提供。
图形和文字的颜色由Paint的参数来设置。


image.png

注:Paint设置颜色的两种方式:
1.Paint.setColor/ARGB()来设置颜色。
2.通过设置Shader(调色板)来设置颜色方案。

1.1.1 直接设置颜色

--paint.setColor(Color.RED);
--paint.setColor(Color.parseColor("#003000"));
--paint.setARGB(int a,int b,int g,int b)

1.1.2 设置Shader

介绍 Shader : Shader 为着色器,用于设置绘制有颜色。他是图形领域通用的概念。他与直接设置颜色不同的是,着色器设置的是一个颜色方案。

在Android中,Shader的子类:LinearGradient RadialGradient SweepGradient BitmapShader ComposeShader

1.1.2.1 LinearGradient 线性渐变

先设置两个点,然后有以这两个点为断电,使用两种颜色的渐变来绘制
例:

Shadershader=newLinearGradient(100,100,500,500,Color.parseColor("#E91E63"),Color.parseColor("#2196F3"),Shader.TileMode.CLAMP);

canvas.drawRect(100,100,500,500,paint);


image.png

注:在设置Shader后,Paint.setColor/ARGB() 所设置的颜色就不会再起作用。

构造方法:

LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)
参数:
        --x0,y0  x1,y1:渐变的两个端点的位置。
        --color0,color1:端点的颜色。
        --title 端点范围之外的着色规则。

TileMode 是个枚举类,共三个值:


image.png
CLAMP:用彩色边缘来填充多余空间
image.png
MIRROR:重复使用镜像模式来的图像来填充多余空间
image.png
REPEAT:重复图像来填充多余空间
image.png
1.1.2.2 RadialGradient 辐射渐变

从中心来向四周辐射
例:

Shadersharder=newRadialGradient(300,300,200,Color.parseColor("#E91E63"),Color.parseColor("#2196F3"),Shader.TileMode.CLAMP);

canvas.drawCircle(300,300,200,paint);


image.png
构造方法:
RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)。
        参数:
        centerX centerY:辐射中心的坐标 
        radius:辐射半径 
        centerColor:辐射中心的颜色 
        edgeColor:辐射边缘的颜色 
        tileMode:辐射范围之外的着色模式。
1.1.2.3 SweepGradient 扫描渐变

类似与雷达扫描
例:

  Shadershader=newSweepGradient(300,300,Color.parseColor("#E91E63"),Color.parseColor("#2196F3"));

canvas.drawCircle(300,300,200,paint);


image.png

构造方法
SweepGradient(float cx, float cy, int color0, int color1)
参数:
cx cy :扫描的中心
color0:扫描的起始颜色
color1:扫描的终止颜色

1.1.2.4 BitmapShader

用Bitmap 的像素来作为图形或者文字的填充。
例:

Bitmapbitmap=BitmapFactory.decodeResource(getResources(),R.drawable.what_the_fuck);
Shadershader=newBitmapShader(bitmap,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
canvas.drawCircle(200,200,200,paint);
image.png
image.png

实际上他与 Canvas.drawBitmap()效果类似。
绘制圆形Bitmap,就可以使用drawCircle() + BitmapShader()

构造方法:

BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
        参数: 
        bitmap:用来做模板的 Bitmap 对象 
        tileX:横向的 TileMode 
        tileY:纵向的 TileMode。
        
        注:这里的title,需要设置纵向跟横向的。
CLAMP:
image.png
MIRROR:
image.png
REPAT:
image.png
1.1.2.5 ComposeShader 混合着色器

简而言之:将两个Shader 一起用。
例:

setLayerType(LAYER_TYPE_SOFTWARE,null);

Bitmapbitmap=BitmapFactory.decodeResource(getResources(),R.drawable.what_the_fuck);
Shadershader=newBitmapShader(bitmap,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
        
Bitmapbitmap1=BitmapFactory.decodeResource(getResources(),R.drawable.batman_logo);
Shadershader1=newBitmapShader(bitmap1,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
        
Shadershader2=newComposeShader(shader,shader1,PorterDuff.Mode.DST_IN);
        
paint.setShader(shader2);
image.png
注:这里需要禁用硬件加速。ComposeShader() 在硬件加速条件下不支持两个同类型的Shader。

构造方法:

ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
        参数: 
        shaderA, shaderB:两个相继使用的 Shader 
        mode: 两个 Shader 的叠加模式,即 shaderA 和 shaderB 应该怎样共同绘制。它的类型是PorterDuff.Mode 
PorterDuff.Mode
image.png

ProterDuff.Mode是一个枚举类,他是用来指定两个图像共有绘制时的颜色策略。他通过不同mode类型类指定不同的策略。(简单的说,就是两个图形,指定他们以什么样的方式合并成一个图形)

PorterDuff.Mode 共有17个类型,分为两类
1.Alpha 合成  12种类
2.混合(Blending)

Alpha 合成:其实就是 「PorterDuff」 这个词所指代的算法。 「PorterDuff」 并不是一个具有实际意义的词组,而是两个人的名字(准确讲是姓)。这两个人当年共同发表了一篇论文,描述了 12 种将两个图像共同绘制的操作(即算法)。而这篇论文所论述的操作,都是关于 Alpha 通道(也就是我们通俗理解的「透明度」)的计算的,后来人们就把这类计算称为Alpha 合成 ( Alpha Compositing ) 。

Google 官方效果
源图像和目标图像


image.png
Alpha合成
image.png

混合:也就是 Photoshop 等制图软件里都有的那些混合模式(multiply darken lighten 之类的)。这一类操作的是颜色本身而不是 Alpha 通道,并不属于 Alpha 合成,所以和 Porter 与 Duff 这两个人也没什么关系,不过为了使用的方便,它们同样也被 Google 加进了 PorterDuff.Mode 里。


image.png

由上图可看出:Alpha合成类的效果直观,混合类的效果比较抽象。当我们掌握了Alpha类的合成,记住混合类的名字,当我们再看到复杂UI的设计图的时候,自己马上就能知道能不能做,怎么做了。

1.2 setColorFilter(ColorFilter colorFilter)

ColorFilter类:为绘制设置颜色过滤(就是为绘制的内容设置一个统一的过滤策略,然后Canvas.drawXXX()方法对每个像素都进行过滤后再绘制出来)。

在 Paint 里设置 ColorFilter ,使用的是 Paint.setColorFilter(ColorFilter filter) 方法。 ColorFilter 并不直接使用,而是使用它的子类。它共有三个子类:LightingColorFilter PorterDuffColorFilter 和 ColorMatrixColorFilter。

1.2.1 LightingColorFilter(模拟简单的光照效果)

LightingColorFilter 的构造方法是 LightingColorFilter(int mul, int add) ,参数里的 mul 和 add 都是和颜色值格式相同的 int 值,其中 mul 用来和目标像素相乘,add 用来和目标像素相加:


image.png

一个「保持原样」的「基本 LightingColorFilter 」,mul 为 0xffffff,add 为 0x000000(也就是0),那么对于一个像素,它的计算过程就是:

image.png

基于这个「基本 LightingColorFilter 」,你就可以修改一下做出其他的 filter。比如,如果你想去掉原像素中的红色,可以把它的 mul 改为 0x00ffff (红色部分为 0 ) ,那么它的计算过程就是:


image.png

ColorFilter lightingColorFilter = new LightingColorFilter(0x00ffff, 0x000000);
paint.setColorFilter(lightingColorFilter);

image.png

1.2.2 PorterDuffColorFilter

PorterDuffColorFilter 他的作用是使用一个指定的颜色和一种指定的PorterDuff.Mode来与绘制对象进行合成。

构造方法:

PorterDuffColorFilter(int color, PorterDuff.Mode mode)
    参数:
    color 参数是指定的颜色
    mode 参数是指定的 Mode。同样也是 PorterDuff.Mode ,不过和 ComposeShader 不同的是,PorterDuffColorFilter 作为一个 ColorFilter,只能指定一种颜色作为源,而不是一个 Bitmap。
1.2.3 ColorMatrixColorFilter

ColorMatrixColorFilter 他是使用一个ColorMatrix来对颜色进行处理。ColorMatrix类,他的内部是一个4*5的矩阵。


image.png

通过计算,ColorMatrix 可以把要绘制的像素进行转换。对于颜色【R,G,B,A】,转换算法:


image.png

关于ColorMatrix,推荐StyleImageView
image.png

1.3 setXfermode(Xfermode xfermode)

Xfermode 指的是你要绘制的内容和 Canvas 的目标位置的内容应该怎样结合计算出最终的颜色。
其实就是将你要绘制的内容作为源图像,以View中已有的内容作为目标图像,选取一个PorterDuffer.Mode绘制内容的颜色处理方案。
例:


image.png

image.png

image.png

又是 PorterDuff.Mode 。 PorterDuff.Mode 在 Paint 一共有三处 API ,它们的工作原理都一样,只是用途不同:


image.png
注:Xfermode 只有一个子类(PorterDuffXfermode)。
Xfermode 注意事项
1.使用离屏缓冲(Off-screen Buffer)

上面的代码如果直接执行不会绘制出图中效果。程序在执行的时候,也不会是上面的流程。


image.png

按照逻辑我们会认为,在第二步画圆的时候,跟它共同计算的是第一步绘制的方形。但实际上,却是整个 View 的显示区域都在画圆的时候参与计算,并且 View 自身的底色并不是默认的透明色,而且是遵循一种迷之逻辑,导致不仅绘制的是整个圆的范围,而且在范围之外都变成了黑色。就像这样:


image.png

要想使用setXfermode()正常绘制,必须使用离屏缓冲(Off-screen Buffer)把绘制的内容绘制在额外的图层上,再将绘制好的内容贴回到View中。


image.png

离屏缓冲的方式:
1.Canvas。saveLayer() saveLayer() 可以做短时的离屏缓冲


image.png

2.View.setLayerType() 是直接把整个 View 都绘制在离屏缓冲中
setLayerType(LAYER_TYPE_HARDWARE) 是使用 GPU 来缓冲, setLayerType(LAYER_TYPE_SOFTWARE) 是直接直接用一个 Bitmap 来缓冲。

注:
如果没有特殊需求,可以选用第一种方法 Canvas.saveLayer() 来设置离屏缓冲,以此来获得更高的性能。

2.控制好透明区域


image.png

如图所示,由于透明区域过小而覆盖不到的地方,将不会受到 Xfermode 的影响。
所以,我们在使用Xfermode绘制内容时候,除了注意离屏缓冲,还需要注意控制透明区域的大小,要让他足够覆盖到要和他结合绘制的内容。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容