自定义View 1-2 Paint.md

#自定义View 1-2 Paint

###Paint 大致可以分为 4类

  • 颜色
  • 效果
  • drawText()
  • 初始化

1 颜色

1.1简单使用

paint.setColor(Color.parseColor("#009688"));  
canvas.drawRect(30, 30, 230, 180, paint);

paint.setColor(Color.parseColor("#FF9800"));  
canvas.drawLine(300, 30, 450, 180, paint);

paint.setColor(Color.parseColor("#E91E63"));  
canvas.drawText("HenCoder", 500, 130, paint);

setARGB 和 setColor 一样都是设置颜色

paint.setARGB(100, 255, 0, 0);  
canvas.drawRect(0, 0, 200, 200, paint); 

setShader(shader) 设置Shader

Shader 是一套着色规则,通常是使用它的子类

  • LinearGradient 线性渐变
    • 设置两个点和两种颜色,以这两个点作为端点,使用两种颜色的渐变来绘制颜色。就像这样:(其他形状以及文字都可以这样设置颜色)

        构造方法: 
        LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile) 。
        
        参数: 
        x0 y0 x1 y1:渐变的两个端点的位置 
        color0 color1 是端点的颜色 
        tile:端点范围之外的着色规则,类型是 TileMode。TileMode 一共有 3 个值可选: CLAMP, MIRROR 和  REPEAT。CLAMP (夹子模式???算了这个词我不会翻)会在端点之外延续端点处的颜色;MIRROR 是镜像模式;REPEAT 是重复模式。具体的看一下例子就明白。
        
        canvas.drawCircle(300, 300, 200, paint);
      

  • RadialGradient 辐射渐变
    • 辐射渐变很好理解,就是从中心向周围辐射状的渐变。大概像这样:

      	构造方法: 
      	RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)。
      	
      	参数: 
      	centerX centerY:辐射中心的坐标 
      	radius:辐射半径 
      	centerColor:辐射中心的颜色 
      	edgeColor:辐射边缘的颜色 
      	tileMode:辐射范围之外的着色模式。
      
      	canvas.drawCircle(300, 300, 200, paint); 
      

  • SweepGradient 扫描渐变

  • 又是一个渐变。「扫描渐变」这个翻译我也不知道精确不精确。大概是这样:

    构造方法:
    SweepGradient(float cx, float cy, int color0, int color1)

     参数: 
     cx cy :扫描的中心 
     color0:扫描的起始颜色 
     color1:扫描的终止颜色
    

  • BitmapShader (例如圆形头像就可以使用)

	构造方法: 
	BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
	
	参数: 
	bitmap:用来做模板的 Bitmap 对象 
	tileX:横向的 TileMode 
	tileY:纵向的 TileMode。

    canvas.drawCircle(300, 300, 200, paint);
    canvas.drawText("123456789", 300, 300, paint);
也可以将图片作为 字体的颜色
  • ComposeShader 混合着色器

  • 所谓混合,就是把两个 Shader 一起使用。

  • 想要使用两个shader 需要关闭硬件加速。

     	// 第一个 Shader:头像的 Bitmap
     	Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.batman);  
     	Shader shader1 = new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
     	
     	// 第二个 Shader:从上到下的线性渐变(由透明到黑色)
     	Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.batman_logo);  
     	Shader shader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
     	
     	// ComposeShader:结合两个 Shader
     	Shader shader = new ComposeShader(shader1, shader2, PorterDuff.Mode.SRC_OVER);  
     	paint.setShader(shader);
    

构造方法:ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)

最后一个参数PorterDuff.Mode用来控制 两个shader的叠加模式

一共有17种

大致分成两种 1 Alpha 合成 (Alpha Compositing) 2
混合 (Blending)

合成

混合

1.2 setColorFilter(ColorFilter colorFilter)

ColorFilter (颜色过滤)这个类 就是讲绘制出来的内容过滤,设置一个统一的过滤策略,Canva.drawxxx() 会将每个像素进行过滤后,在绘制出来。

效果

不过在使用中 我们通常使用它的子类

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

  • LightingColorFilter(int mul, int add)
    • 其中 mul 用来和目标像素相乘,add 用来和目标像素相加

    • 初始原图效果 mul 为 0xffffff,add 为 0x000000(也就是0)

        例如:去掉图片中的红色,
        ColorFilter lightingColorFilter = new LightingColorFilter(0x00ffff, 0x000000);  
        paint.setColorFilter(lightingColorFilter);  
      

1.2.2 PorterDuffColorFilter

PorterDuffColorFilter(int color, PorterDuff.Mode mode)
制定一种颜色,来与绘制图像进行合成,并且制定绘制模式PorterDuff.Mode
  • PorterDuff.Mode mode 与之前介绍的一样

1.2.3 ColorMatrixColorFilter (通过4*5的颜色矩阵变换来改变,绘制的效果)

  • ColorMatrix 也有一些可以直接使用的 API,setSaturation(float sat)设置饱和度。

  • 也可以对 其每一个像素进行精确调整。(遍历所有像素,对每个像素进行矩阵变换)

可以参考这个库:

https://github.com/chengdazhi/StyleImageView

1.3 setXfermode(Xfermode xfermode)

Paint 最后一层处理图像的的方法,它处理的是当 颜色遇上 View 的问题。

Xfermode 只有一个 子类 PorterduffXfermode

	Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
	...
	canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方  
	paint.setXfermode(xfermode); // 设置 Xfermode  
	canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆  
	paint.setXfermode(null); // 用完及时清除 Xfermode 

PorterDuff.Mode

      API                     用途
ComposeShader            或者两个shader
 
PorterDuffColorFilter    增加一个单色的ColorFilter

Xfermode                 试着绘制内容和View中已有的混合计算方式

Xfermoder 注意事项 两点

一 , 绘制结果不理想

![](https://i.imgur.com/6ruMp3Q.png)
  • 解决方案通过使用 离屏缓存(Off-screen Buffe) 将图形绘制在额外层在 将图形。再把绘制好的内容贴回 View 中。

    • 方式1 CanVas.saveLayer()

      saveLayer() 可以做段时间的离屏缓冲,在原本代码前后各加一行代码

       	int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        	
        canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方
        paint.setXfermode(xfermode); // 设置 Xfermode
        canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆
        paint.setXfermode(null); // 用完及时清除 Xfermode
        
        canvas.restoreToCount(saved);
      
    • 方式2 控制好透明区域 不要太小,要足够覆盖绘制内容

小结回顾


2 效果

2.1 setAntiAlias (boolean aa) 设置抗锯齿

2.2 setStyle(Paint.Style style) 填充模式

2.3 线条形状

2.3.1 setStrokeWidth(float width)

设置线条宽度,单位像素,默认值是 0。

2.3.2 setStrokeCap(Paint.Cap cap)

设置线头的形状。线头形状有三种:BUTT 平头、ROUND 圆头、SQUARE 方头。默认为 BUTT。

2.3.3 setStrokeJoin(Paint.Join join)

设置拐角的形状。有三个值可以选择:MITER 尖角、 BEVEL 平角和 ROUND 圆角。默认为 MITER。

mPaint.reset();        mPaint.setColor(Color.RED);     
 mPaint.setStyle(Paint.Style.STROKE);//描边
    mPaint.setStrokeWidth(50);  
  mPaint.setStrokeCap(Paint.Cap.ROUND);//圆的   
 //测试1        Path path = new Path();  
  path.moveTo(100, 100);       
 path.lineTo(300, 100);       
 path.lineTo(100, 300);      
  mPaint.setStrokeJoin(Paint.Join.MITER);    
    canvas.drawPath(path, mPaint);      
   path.moveTo(100, 400);      
  path.lineTo(300, 500);     
   path.lineTo(100, 700);    
    mPaint.setStrokeJoin(Paint.Join.ROUND); 
       canvas.drawPath(path, mPaint);    
     path.moveTo(100, 800);      
  path.lineTo(300, 800);     
   path.lineTo(100, 1100);    
    mPaint.setStrokeJoin(Paint.Join.BEVEL);        canvas.drawPath(path, mPaint);

---------------------

本文来自 等待着冬天的风 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/qq_32306361/article/details/72869936?utm_source=copy 

2.3.4 setStrokeMiter(float miter)

这个方法是对于 setStrokeJoin() 的一个补充,它用于设置 MITER 型拐角的延长线的最大值。所谓「延长线的最大值」,是这么一回事:

##2.4 色彩优化 (两种方案)

###2.4.1 设置图像抖动

paint.setDither(true);  

只要加这么一行代码,之后的绘制就是加抖动的了。

不过对于现在(2017年)而言, setDither(dither) 已经没有当年那么实用了,因为现在的 Android 版本的绘制,默认的色彩深度已经是 32 位的 ARGB_8888 ,效果已经足够清晰了。只有当你向自建的 Bitmap 中绘制,并且选择 16 位色的 ARGB_4444 或者 RGB_565 的时候,开启它才会有比较明显的效果。

###2.4.2 setFilterBitmap(boolean filter) 设置是否使用双线性过滤来绘制 Bitmap 。

图像在放大绘制的时候,默认使用的是最近邻插值过滤,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤,就可以让结果图像显得更加平滑。效果依然盗维基百科的图:

##2.5 setPathEffect(PathEffect effect)

Android 中的 6 种 PathEffect。PathEffect 分为两类,单一效果的 CornerPathEffect DiscretePathEffect DashPathEffect PathDashPathEffect ,和组合效果的 SumPathEffect ComposePathEffect

###2.5.1 CornerPathEffect


###2.5.2 DiscretePathEffect

###2.5.3 DashPathEffect

###2.5.4 PathDashPathEffect

###2.5.5 SumPathEffect

这是一个组合效果类的 PathEffect 。它的行为特别简单,就是分别按照两种 PathEffect 分别对目标进行绘制。

###2.5.6 ComposePathEffect

这也是一个组合效果类的 PathEffect 。不过它是先对目标 Path 使用一个 PathEffect,然后再对这个改变后的 Path 使用另一个 PathEffect。

它的构造方法 ComposePathEffect(PathEffect outerpe, PathEffect innerpe) 中的两个 PathEffect 参数, innerpe 是先应用的, outerpe 是后应用的。所以上面的代码就是「先偏离,再变虚线」。而如果把两个参数调换,就成了「先变虚线,再偏离」。至于具体的视觉效果……我就不贴图了,你自己试试看吧!

上面这些就是 Paint 中的 6 种 PathEffect。它们有的是有独立效果的,有的是用来组合不同的 PathEffect 的,功能各不一样。

注意: PathEffect 在有些情况下不支持硬件加速,需要关闭硬件加速才能正常使用:

1 Canvas.drawLine() 和 Canvas.drawLines() 方法画直线时,setPathEffect() 是不支持硬件加速的;

2 PathDashPathEffect 对硬件加速的支持也有问题,所以当使用 PathDashPathEffect 的时候,最好也把硬件加速关了。

剩下的两个效果类方法:setShadowLayer() 和 setMaskFilter() ,它们和前面的效果类方法有点不一样:它们设置的是「附加效果」,也就是基于在绘制内容的额外效果

##2.6 在绘制内容下面加一层阴影 setShadowLayer(float radius, float dx, float dy, int shadowColor)

paint.setShadowLayer(10, 0, 0, Color.RED);

...

canvas.drawText(text, 80, 300, paint);

方法的参数里, radius 是阴影的模糊范围; dx dy 是阴影的偏移量; shadowColor 是阴影的颜色。

clearShadowLayer() 清除阴影效果
注意:

  • 在硬件加速开启的情况下, setShadowLayer() 只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。

  • 如果 shadowColor 是半透明的,阴影的透明度就使用 shadowColor 自己的透明度;而如果 shadowColor 是不透明的,阴影的透明度就使用 paint 的透明度。

##2.7 setMaskFilter(MaskFilter maskfilter) 对整个画面来进行 过滤

它有两个子类 BlurMaskFilter 和 EmbossMaskFilter

###BlurMaskFilter 模糊效果的 MaskFilter。

	paint.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));
	
	...
	
	canvas.drawBitmap(bitmap, 100, 100, paint);  

它的构造方法 BlurMaskFilter(float radius, BlurMaskFilter.Blur style) 中, radius 参数是模糊的范围, style 是模糊的类型。一共有四种:

  • NORMAL: 内外都模糊绘制
  • SOLID: 内部正常绘制,外部模糊
  • INNER: 内部模糊,外部不绘制
  • OUTER: 内部不绘制,外部模糊(什么鬼?)

###2.7.2 EmbossMaskFilter 浮雕效果的 MaskFilter

paint.setMaskFilter(new EmbossMaskFilter(new float[]{0, 1, 1}, 0.2f, 8, 10));

...

canvas.drawBitmap(bitmap, 100, 100, paint);  

![](https://i.imgur.com/fzf2Vw3.png)

它的构造方法
EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) 的参数里, direction 是一个 3 个元素的数组,指定了光源的方向; ambient 是环境光的强度,数值范围是 0 到 1; specular 是炫光的系数; blurRadius 是应用光线的范围。

##2.8 获取绘制的 Path

这是效果类的最后一组方法,也是效果类唯一的一组 get 方法。

这组方法做的事是,根据 paint 的设置,计算出绘制 Path 或文字时的实际 Path。

###2.8.1 getFillPath(Path src, Path dst)

首先解答第一个问题:「实际 Path」。所谓实际 Path ,指的就是 

drawPath() 的绘制内容的轮廓,要算上线条宽度和设置的 

PathEffect。

默认情况下(线条宽度为 0、没有 PathEffect),原 Path 和实际 

Path 是一样的;而在线条宽度不为 0 (并且模式为 STROKE 模式或

FLL_AND_STROKE ),或者设置了 PathEffect 的时候,实际 Path 就和原

Path 不一样了:

通过 getFillPath(src, dst) 方法就能获取这个实际 Path。方法的参数里,src 是原 Path ,而 dst 就是实际 Path 的保存位置。 getFillPath(src, dst) 会计算出实际 Path,然后把结果保存在 dst 里。

###2.8.2 getTextPath(String text, int start, int end, float x, float y, Path path) / getTextPath(char[] text, int index, int count, float x, float y, Path path)

各项参数

  • text 需要检索的文本
  • start 需要检索的首字符位置
  • end 需要检索的结尾字符位置
  • x 绘制文字的原点 x 值
  • y 绘制文字的远点 y 值

这里就回答第二个问题:「文字的 Path」。文字的绘制,虽然是使用 Canvas.drawText() 方法,但其实在下层,文字信息全是被转化成图形,对图形进行绘制的。 getTextPath() 方法,获取的就是目标文字所对应的 Path 。这个就是所谓「文字的 Path」。

#3 drawText() 相关
###看下效果图 具体放到下一节中讲

#4 初始化类

这一类方法很简单,它们是用来初始化 Paint 对象,或者是批量设置 Paint 的多个属性的方法。

##4.1 reset()
重置 Paint 的所有属性为默认值。相当于重新 new 一个,不过性能当然高一些啦。

##4.2 set(Paint src)
把 src 的所有属性全部复制过来。相当于调用 src 所有的 get 方法,然后调用这个 Paint 的对应的 set 方法来设置它们。

##4.3 setFlags(int flags)

批量设置 flags。相当于依次调用它们的 set 方法。例如:

paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  

这行代码,和下面这两行是等价的:

paint.setAntiAlias(true);  
paint.setDither(true); 

setFlags(flags) 对应的 get 方法是 int getFlags()。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值