自定义view——Paint 之颜色处理

前言

这一篇介绍Paint对颜色的处理,也是 自定义控件——Paint画笔 的补充。

这里对颜色的处理,分为如下:

这里写图片描述

将 setShape、SetColoFilter、SetXFermode、PorterDuff.Mode等都归纳一起….

下面开始介绍具体的使用。

使用

1.基本颜色

Canvas.drawColors()

指定颜色,绘制整个画布的颜色(上一篇已经操作过)

Canvas.drawBitmap( )

指定Bitmap对象,直接由 Bitmap 对象来提供的颜色

Paint的方法

跟踪Paint的设置颜色参数,来设置绘制内容(图像、文本)的颜色(上面2个是设置画布颜色)

其又可分为2种:

第1种:直接设置颜色值:

setColor(int color)
setAlpha(int a)
setARGB(int a, int r, int g, int b)

这些方法是不是很眼熟?在介绍Canvas里面多有这些方法介绍。这里就不说了。

第2种:setShader(Shader shader)

首先,什么是Shader?
Shader是一种图像学,它不是Android特有的,在很多平台领域多有,比如:Direct3D、OpenGL等。对于这里,他就是着色器,一种着色方案,可以设置线性渐变、圆形渐变、角度渐变等方案设置颜色。

使用:
参数shader,为Shader的子类对象,如果传入参数为null时,则清除以前的设置。
Shader的子类有如下几个,如图:

下面逐个介绍每个子类的使用:

LinearGradient 线性渐变

构造函数:

LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[], TileMode tile)
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile) 

参数:

x0,y0:渐变开始端点的坐标
x1,y1:渐变结束端点的坐标
tile:2个端点形成矩形之外的着色规则,一共有 3 个值可选: CLAMP, MIRROR 和 REPEAT
color0,color1:渐变起始和终止的颜色。

1.颜色值必须使用 0xAARRGGBB 形式的16进制,透明度也需要,不然没有效果。

2.如果使用Color.parseColor(“#E91E63”),可以不添加透明度,因为parseColor()方法将透明度设置为ff

colors[]:多颜色渐变的集合

positions[]:与colors[]对应,取值是0-1的float类型,表示在每一个颜色在整条渐变线中的百分比位置。该值为null时,均匀分布。

注意:在设置了 Shader 的情况下, Paint.setColor/ARGB() 所设置的颜色就不再起作用。

使用:

/**
 * 两色渐变
 */
Shader shader = new LinearGradient(100,100,500,500, Color.RED,Color.GREEN,Shader.TileMode.CLAMP);
//       Shader shader = new LinearGradient(100,100,getWidth()-100,300, Color.parseColor("#E91E63"),Color.parseColor("#E91E63"),Shader.TileMode.CLAMP);
mPaint.setShader(shader);
canvas.drawRect(100,100,600,500,mPaint);

/**
 * 多色渐变
 */
int[] colors = {Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW,Color.MAGENTA};
float[] position = {0f, 0.2f, 0.4f, 0.6f, 1.0f}; //颜色位置比例
Shader shader1 = new LinearGradient(100,850,600,850,colors,position,Shader.TileMode.CLAMP);
mPaint.setShader(shader1);
canvas.drawRect(100,600,600,1100,mPaint);

效果图:

图片描述

这里补充一个点:

水平渐变 和 对角线渐变

通过上面图形的对比,可以看出:
问题1:图中 对角线渐变中,怎么红色和粉色那么一点,不是均匀分布吗?

要清楚的是,这里说的是颜色的位置,不是渐变填充的区域。只要屏幕足够大,填充的区域也是无限大的。

问题2:颜色渐变、颜色填充是按照什么方向?

渐变的方向,就是图中的黑色箭头,开始端点指向结束端点的方向;填充的方向,就是图中灰色的线条,垂直于渐变方向,向2边无限填充。

TileMode 模式介绍
连接渐变的2个端点,再根据填充的方向,会形成矩形,矩形里面正常填充,但对于矩形之外的填充模式,就是根据TileMode的模式来填充。有如下3个值:

  1. TileMode.CLAMP 边缘拉伸模式(咋翻译?)
  2. TileMode.REPEAT 重复模式
  3. TileMode.MIRROR 镜像模式

具体效果看图:

RadialGradient 放射渐变

构造函数:

RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)
RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)

参数:

centerX、centerY:渐变开始的圆点坐标
radius:圆形的半径
centerColor:圆形中心点渐变的颜色,即开始的颜色(同线性渐变)
edgeColor:圆形边缘的渐变的颜色,即结束的颜色(同线性渐变)
colors、stops、tileMode:分别是多色渐变的颜色集合,颜色位置,填充区域外的填充模式(同线性渐变)

使用:

int[] colors = {Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW,Color.MAGENTA};
Shader shader1 = new RadialGradient(400,400,200,colors,null,Shader.TileMode.CLAMP);//均匀分布
mPaint.setShader(shader1);
canvas.drawCircle(400,400,200,mPaint);

效果图:

图片描述

TileMode 模式效果图:

图片描述

SweepGradient 扫描渐变

构造函数:

SweepGradient(float cx, float cy, int color0, int color1)
SweepGradient(float cx, float cy, int[] colors, float[] positions)

参数:

cx、cy:扫描点的坐标
color0、color1、colors、positions:(同线性渐变)

注:这里不需指定TileMode 模式,扫描渐变是顺时针旋转360°,全方位的扫描,所以也没有存在空白的区域。

使用:

int[] colors = {Color.RED,Color.GREEN,Color.BLUE,Color.YELLOW,Color.MAGENTA};
Shader shader1 = new SweepGradient(400,400,colors,null);//均匀分布
mPaint.setShader(shader1);
canvas.drawCircle(400,400,200,mPaint); //圆形
//canvas.drawRect(100,200,600,600,mPaint);矩形

效果图:

图片描述

BitmapShader Bitmap着色
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

参数:

bitmap:Bitmap 位图对象
tileX:在X轴方向的TileMode 模式
tileY:在Y轴方向的TileMode 模式

使用:

/**
* 将矩形绘制区域大小,设置为bitmap的大小,这里TileMode则没有效果
* 看起来,和drawBitmap()几乎是一样的
*/
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.bitmap);
Shader shader1 = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader1);
canvas.drawRect(0,0,bitmap.getWidth(),bitmap.getHeight(),mPaint);

效果图:

图片描述

如果最后绘制的区域是图片的bitmap的大小,那么该方法和Canvas的drawBitmap( ) 没什么区别;
如果绘制的区域是整个屏幕,再根据TileMode模式,区别就出来了。

如图:
因为,X、Y多可以分别设置TileMode 模式,所以可以有很多种效果,这里举出几个:

1.X、Y设置相同模式:

图片描述

2.X、Y设置不同模式

图片描述

这个方法的使用,比较常见到的就是,和截取图中一部分图片显示,比如显示圆形头像:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.bitmap);
Shader shader1 = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader1);
canvas.drawCircle(100,100,100,mPaint);

效果图:

图片描述

其实,显示圆形头像的方法有很多(貌似面试也常会问到),还有使用Canvas.clipPath(),还有使用Xfermode(后面介绍)。

ComposeShader 混合着色

构造函数:

ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)

参数:

shaderA:dst 目标图像,即首先绘制的图像
shaderB:src 源图像,下一个绘制的图像
Xfermode mode:指定2个shader重叠的 Xfermode 组合模式
PorterDuff.Mode mode:指定2个shader重叠的 PorterDuff.Mode组合模式

这里先简单提一下:Xfermode的模式目前只有一个子类:PorterDuffXfermode。而该子类真正核心就是指定PorterDuff.Mode模式。所以,混合模式就只用第2个构造方法就可以了。

Thomas Porter 和 Tom Duff 发明了alpha合成模式,所以该类的名字就是这样子来的。

PorterDuff一共有17种模式,还有其他5种混合模式,但没有被限制到alpha通道。这几种不是Porter和Duff定义的,但为了方便起见,也一起放在这个类中。

那么,这里可以分为2类来介绍:

  1. Alpha compositing modes(Alpha 合成模式)
  2. Blending modes(混合模式)

他们混合模式有什么效果呢?看官方图:

Alpha compositing modes:

图片描述

Blending modes:

图片描述

对于详细的介绍,这里可以先看看别人的文章:具体介绍

刚开始,我也觉得,这些多没怎么用到?不用学。。。吧!看了人家的,才知道:你多不知道有这个技术,你会用到才怪!

这里给个小案例:

Bitmap bitmapA = BitmapFactory.decodeResource(getResources(), R.mipmap.composeshader_bg);
Bitmap bitmapB = BitmapFactory.decodeResource(getResources(), R.mipmap.bitmap6);

Shader shaderA = new BitmapShader(bitmapA, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Shader shaderB = new BitmapShader(bitmapB, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

Shader shader1 = new ComposeShader(shaderA,shaderB, PorterDuff.Mode.DST_IN);

mPaint.setShader(shader1);
canvas.drawRect(0,0,bitmapA.getWidth(),bitmapA.getHeight(),mPaint);

效果图:
这里写图片描述

简单的来说,就是将2个Bitmap通过Mode整合之后,得到你想要的结果。用到的时候,在详细了解每个Mode的效果,这里先了解下。至于混合模式,如果精通PS的,应该比较容易理解,这里就先不研究了…

2. setColorFilter(ColorFilter filter)

设置颜色过滤。为绘制的内容设置一个统一的过滤策略,然后 Canvas.drawXXX() 方法会对每个像素都进行过滤后再绘制出来。
参数filter,也是不直接使用ColorFilter ,而是使用其子类对象,有3种:

LightingColorFilter

光照颜色过滤器,可以简单的完成色彩过滤和色彩增强功能。

构造函数:

LightingColorFilter(int mul, int add)

参数:
mul:颜色值(格式为 0xRRGGBB,透明度不变),用于和目标像素相乘(过滤)
add:颜色值(格式为 0xRRGGBB,透明度不变),用于和目标像素相加(增强)

计算公式是这样的:

R' = R * mul.R / 0xff + add.R
G' = G * mul.G / 0xff + add.G
B' = B * mul.B / 0xff + add.B

其中,R’ ,G’, B’是最终的结果值,取值范围在[0…255],即其值超过255,最终也是255。

使用:

Bitmap bitmapA = BitmapFactory.decodeResource(getResources(), R.mipmap.d);
mPaint.setColorFilter(new LightingColorFilter(0xff8800,0x00000));//过滤 B,削弱G
canvas.drawBitmap(bitmapA, 0, 0, mPaint);

Bitmap bitmapA = BitmapFactory.decodeResource(getResources(), R.mipmap.button_blue);
canvas.drawBitmap(bitmapA, 20, 20, mPaint);

canvas.translate(20,200);

mPaint.setColorFilter(new LightingColorFilter(0xffffff,0xff0000)); //增强红色
canvas.drawBitmap(bitmapA, 0, 0, mPaint);

canvas.translate(0,200);
mPaint.setColorFilter(new LightingColorFilter(0xffff00,0x000000)); //过滤蓝色
canvas.drawBitmap(bitmapA, 0, 0, mPaint);

图片描述

其实,最终得到理想的效果,也不是很好搞。一般需要和设计师商量,或者自己慢慢调,调到自己想要的。。。

PorterDuffColorFilter

图形混合滤镜,是使用一个指定的颜色和一种指定的 PorterDuff.Mode 来与绘制对象进行合成。

构造函数

PorterDuffColorFilter(int color, PorterDuff.Mode mode)

参数:
color:指定混合的颜色值
mode:混合模式,前面有介绍了

使用:

Bitmap bitmapA = BitmapFactory.decodeResource(getResources(), R.mipmap.composeshader_bg);
canvas.drawBitmap(bitmapA, 20, 20, mPaint);
canvas.translate(20,bitmapA.getHeight()+100);
//增强红色
mPaint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.ADD));
canvas.drawBitmap(bitmapA, 0, 0, mPaint);

效果图:

图片描述

前面说过,PorterDuff.Mode的模式有很多种,但在这里使用的时候,最终效果是过滤颜色,所以一般常用的是如下:

PorterDuff.Mode.ADD              //饱和度相加
PorterDuff.Mode.DARKEN           //变暗
PorterDuff.Mode.LIGHTEN))        //变亮
PorterDuff.Mode.MULTIPLY));      //正片叠底
PorterDuff.Mode.OVERLAY));       //叠加
PorterDuff.Mode.SCREEN));        //滤色

ColorMatrixColorFilter

通过 ColorMatrix 设置滤镜。

构造函数:
ColorMatrixColorFilter(ColorMatrix matrix)
ColorMatrixColorFilter(float[] array)

参数:
matrix :对于ColorMatrix的使用,这里先暂不介绍了,另开一篇来介绍。这里简单介绍如何使用。
array:指定4*5的数组,其实也是matrix 。

使用:

Bitmap bitmapA = BitmapFactory.decodeResource(getResources(), R.mipmap.composeshader_bg);
 canvas.drawBitmap(bitmapA, 20, 20, mPaint);
 canvas.translate(20,bitmapA.getHeight()+100);

 ColorMatrix colorMatrix = new ColorMatrix(new float[]{
         1, 0, 0, 0, 0,
         0, 1, 0, 0, 0,
         0, 0, 1, 0, 0,
         0, 0, 0, 0.5f, 0,
 });
 mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
 canvas.drawBitmap(bitmapA, 0, 0, mPaint);

效果图:

图片描述

如果想先了解ColorMatrix ,具体可以参考这里点这里

3.setXfermode(Xfermode xfermode)

“Xfermode” 其实就是 “Transfer mode”,用 “X” 来代替 “Trans”。

一样,参数xfermode,不直接使用Xfermode ,而是使用它的子类,但只有1种:

在早期的版本,还有其他的子类,但已经废弃了,所以,使用的时候,直接使用PorterDuffXfermode 就可以了。

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

真是因为Xfermode 只有一个子类,其实原理其实和前面说的 ComposeShader 混合着色 几乎一样。

使用:(需要使用到 离屛缓存 ,具体可以参考上一篇Canvas的介绍)

Bitmap bitmapA = BitmapFactory.decodeResource(getResources(), R.mipmap.composeshader_bg);
Bitmap bitmapB = BitmapFactory.decodeResource(getResources(), R.mipmap.bitmap6);
int save = canvas.saveLayer(0, 0, bitmapB.getWidth(), bitmapB.getHeight(), mPaint,Canvas.ALL_SAVE_FLAG); //离屛缓存

canvas.drawBitmap(bitmapA, 0, 0, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//设置 Xfermode
canvas.drawBitmap(bitmapB, 0, 0, mPaint);
mPaint.setXfermode(null); //用完之后及时清理

canvas.restoreToCount(save);

效果图最终和ComposeShader 混合着色的效果是一样的。

到这里,通过Paint来设置颜色的处理,介绍完了。整体的流程就是:参考HenCoder
这里写图片描述

结束

对于颜色的处理,很多以前多没有用,看起来很高大上哦!这次总结,可以说收获很多啦~
这次的介绍就到这里。


欢迎 点赞

主要参考:

https://juejin.im/post/596baf5f6fb9a06bb15a3df9#heading-17

https://blog.csdn.net/harvic880925/article/details/50995268

猜你喜欢

转载自blog.csdn.net/pzm1993/article/details/81054409