这一篇介绍Paint对颜色的处理,也是 自定义控件——Paint画笔 的补充。
这里对颜色的处理,分为如下:
将 setShape、SetColoFilter、SetXFermode、PorterDuff.Mode等都归纳一起….
下面开始介绍具体的使用。
指定颜色,绘制整个画布的颜色(上一篇已经操作过)
指定Bitmap对象,直接由 Bitmap 对象来提供的颜色
跟踪Paint的设置颜色参数,来设置绘制内容(图像、文本)的颜色(上面2个是设置画布颜色)
其又可分为2种:
setColor(int color)
setAlpha(int a)
setARGB(int a, int r, int g, int b)
这些方法是不是很眼熟?在介绍Canvas里面多有这些方法介绍。这里就不说了。
首先,什么是Shader?
Shader是一种图像学,它不是Android特有的,在很多平台领域多有,比如:Direct3D、OpenGL等。对于这里,他就是着色器,一种着色方案,可以设置线性渐变、圆形渐变、角度渐变等方案设置颜色。
使用:
参数shader,为Shader的子类对象,如果传入参数为null时,则清除以前的设置。
Shader的子类有如下几个,如图:
下面逐个介绍每个子类的使用:
构造函数:
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个值:
构造函数:
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(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 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(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类来介绍:
他们混合模式有什么效果呢?看官方图:
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的,应该比较容易理解,这里就先不研究了…
设置颜色过滤。为绘制的内容设置一个统一的过滤策略,然后 Canvas.drawXXX() 方法会对每个像素都进行过滤后再绘制出来。
参数filter,也是不直接使用ColorFilter ,而是使用其子类对象,有3种:
光照颜色过滤器,可以简单的完成色彩过滤和色彩增强功能。
构造函数:
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);
其实,最终得到理想的效果,也不是很好搞。一般需要和设计师商量,或者自己慢慢调,调到自己想要的。。。
图形混合滤镜,是使用一个指定的颜色和一种指定的 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)); //滤色
通过 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 ,具体可以参考这里点这里
“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