Canvas

Posted by HaoChen Blog on June 23, 2016

自定义View

  • invalidate() 请求重绘 View 树,即 draw 过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些调用了invalidate()方法的 View。
  • requestLayout() 当布局变化的时候,比如方向变化,尺寸的变化,会调用该方法,在自定义的视图中,如果某些情况下希望重新测量尺寸大小,应该手动去调用该方法,它会触发measure()和layout()过程,但不会进行 draw。

1. Android Paint API总结和使用方法

void reset();//重置
void set(Paint src);//相当于在构造函数里使用Paint(src)
void setCompatibilityScaling(float factor);//这里指缩放比例
void setBidiFlags(int flags);//设置BIDI的标记
void setFlags(int flags);//设置paint的 标记
void setHinting(int mode);//设置Hinting的模式
//是否抗锯齿
void setAntiAlias(boolean aa);
//设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰,就是防抖动  
void setDither(boolean dither);
//设置线性文本
void setLinearText(boolean linearText);
//设置该项为true,将有助于文本在LCD屏幕上的显示效果  
void setSubpixelText(boolean subpixelText);
//设置下划线
void setUnderlineText(boolean underlineText);
//设置带有删除线的效果 
void setStrikeThruText(boolean strikeThruText);
//设置伪粗体文本,设置在小字体上效果会非常差  
void setFakeBoldText(boolean fakeBoldText);
//如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作
//加快显示速度,本设置项依赖于dither和xfermode的设置  
void setFilterBitmap(boolean filter);
//设置画笔风格,空心或者实心 FILL,FILL_OR_STROKE,或STROKE
//Paint.Style.STROKE 表示当前只绘制图形的轮廓,而Paint.Style.FILL表示填充图形。  
void setStyle(Style style);
 //设置颜色值
void setColor(int color);
//设置透明图0~255,要在setColor后面设置才生效
void setAlpha(int a);   
//设置RGB及透明度
void setARGB(int a, int r, int g, int b);  
//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度  
void setStrokeWidth(float width);
void setStrokeMiter(float miter);
//当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷末端的图形样式
//如圆形样式Cap.ROUND,或方形样式Cap.SQUARE  
void setStrokeCap(Cap cap);
//设置绘制时各图形的结合方式,如平滑效果等  
void setStrokeJoin(Join join);
//设置图像效果,使用Shader可以绘制出各种渐变效果  
Shader setShader(Shader shader);
//设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果 
ColorFilter setColorFilter(ColorFilter filter);
//设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果 
Xfermode setXfermode(Xfermode xfermode);
//设置绘制路径的效果,如点画线等 
PathEffect setPathEffect(PathEffect effect);
//设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等  
MaskFilter setMaskFilter(MaskFilter maskfilter);
//设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等  
Typeface setTypeface(Typeface typeface);
//设置光栅化
Rasterizer setRasterizer(Rasterizer rasterizer);
//在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
//注意:在Android4.0以上默认开启硬件加速,有些图形的阴影无法显示。关闭View的硬件加速 view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
void setShadowLayer(float radius, float dx, float dy, int color);
//设置文本对齐
void setTextAlign(Align align);
//设置字体大小
void setTextSize(float textSize);
//设置文本缩放倍数,1.0f为原始
void setTextScaleX(float scaleX);
//设置斜体文字,skewX为倾斜弧度  
void setTextSkewX(float skewX);	

Paint.setStrokeJoin(Join join)设置结合处的样子,Miter:结合处为锐角, Round:结合处为圆弧:BEVEL:结合处为直线。
setStrokeMiter(float miter )是设置笔画的倾斜度,如:小时候用的铅笔,削的时候斜与垂直削出来的笔尖效果是不一样的对吧?吼吼....

常用方法:

方法一:

  1. //设置绘制的颜色,a代表透明度,r,g,b代表颜色值。
  2. setARGB(int a,int r,int g,int b);
    这个不多说了,还有两个类似的方法,将设置alpha和rgb分割开来了。注意的是这里的a值是0~255的范围,不是小数。

方法二:

//设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。 setAntiAlias(boolean aa);
也不多说,你可以试验一下效果,设置后会平滑一些;

方法三:

  1. //设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
  2. setDither(boolean dither);

方法四:

  1. //设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等
  2. setMaskFilter(MaskFilter maskfilter);
    MaskFilter类可以为Paint分配边缘效果。

对MaskFilter的扩展可以对一个Paint边缘的alpha通道应用转换。Android包含了下面几种MaskFilter:

  • BlurMaskFilter 指定了一个模糊的样式和半径来处理Paint的边缘。
  • EmbossMaskFilter 指定了光源的方向和环境光强度来添加浮雕效果。

要应用一个MaskFilter,可以使用setMaskFilter方法,并传递给它一个MaskFilter对象。下面的例子是对一个已经存在的Paint应用一个EmbossMaskFilter:

 // 设置光源的方向
  float[] direction = new float[]{ 1, 1, 1 };
  
  //设置环境光亮度
  float light = 0.4f;
  
  // 选择要应用的反射等级
  float specular = 6;
  
 // 向mask应用一定级别的模糊
 float blur = 3.5f;

 EmbossMaskFilter emboss=new EmbossMaskFilter(direction,light,specular,blur);
 
 // 应用mask 
 myPaint.setMaskFilter(emboss);
  1. //设置绘制路径的效果,如点画线等
  2. setPathEffect(PathEffect effect);
    又是一个很好玩的方法:

到目前为止,所有的效应都会影响到Paint填充图像的方式;PathEffect是用来控制绘制轮廓(线条)的方式。PathEffect对于绘制Path基本图形特别有用,但是它们也可以应用到任何Paint中从而影响线条绘制的方式。 使用PathEffect,可以改变一个形状的边角的外观并且控制轮廓的外表。Android包含了多个PathEffect,包括:

1)CornerPathEffect 可以使用圆角来代替尖锐的角从而对基本图形的形状尖锐的边角进行平滑。

2)DashPathEffect 可以使用DashPathEffect来创建一个虚线的轮廓(短横线/小圆点),而不是使用实线。你还可以指定任意的虚/实线段的重复模式。

3) DiscretePathEffect 与DashPathEffect相似,但是添加了随机性。当绘制它的时候,需要指定每一段的长度和与原始路径的偏离度。

4)PathDashPathEffect 这种效果可以定义一个新的形状(路径)并将其用作原始路径的轮廓标记。 下面的效果可以在一个Paint中组合使用多个Path Effect。

1)SumPathEffect 顺序地在一条路径中添加两种效果,这样每一种效果都可以应用到原始路径中,而且两种结果可以结合起来。

2)ComposePathEffect 将两种效果组合起来应用,先使用第一种效果,然后在这种效果的基础上应用第二种效果。 对象形状的PathEffect的改变会影响到形状的区域。这就能够保证应用到相同形状的填充效果将会绘制到新的边界中。 使用setPathEffect方法可以把PathEffect应用到Paint对象中,如下所示:

  1. paint.setPathEffect(new CornerPathEffect(10)); 其他效果懒得测试了,这个在模拟器上跑的时候效果也不明显,但是真机上跑的时候的确圆滑了许多,看上去很舒服

方法五:

  1. //设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果
  2. setColorFilter(ColorFilter colorfilter);
    这个方法也值得试验一下:

MaskFilter是对一个Paint的alpha通道的转换,而ColorFilter则是对每一个RGB通道应用转换。所有由ColorFilter所派生的类在执行它们的转换时,都会忽略alpha通道。 这个貌似比较麻烦,改天再说。

方法六:

  1. //设置绘制路径的效果,如点画线等
  2. setPathEffect(PathEffect effect);
    又是一个很好玩的方法:

到目前为止,所有的效应都会影响到Paint填充图像的方式;PathEffect是用来控制绘制轮廓(线条)的方式。PathEffect对于绘制Path基本图形特别有用,但是它们也可以应用到任何Paint中从而影响线条绘制的方式。 使用PathEffect,可以改变一个形状的边角的外观并且控制轮廓的外表。Android包含了多个PathEffect,包括:

1)CornerPathEffect 可以使用圆角来代替尖锐的角从而对基本图形的形状尖锐的边角进行平滑。

2)DashPathEffect 可以使用DashPathEffect来创建一个虚线的轮廓(短横线/小圆点),而不是使用实线。你还可以指定任意的虚/实线段的重复模式。

3) DiscretePathEffect 与DashPathEffect相似,但是添加了随机性。当绘制它的时候,需要指定每一段的长度和与原始路径的偏离度。

4)PathDashPathEffect 这种效果可以定义一个新的形状(路径)并将其用作原始路径的轮廓标记。

下面的效果可以在一个Paint中组合使用多个Path Effect。

1)SumPathEffect 顺序地在一条路径中添加两种效果,这样每一种效果都可以应用到原始路径中,而且两种结果可以结合起来。

2)ComposePathEffect 将两种效果组合起来应用,先使用第一种效果,然后在这种效果的基础上应用第二种效果。

对象形状的PathEffect的改变会影响到形状的区域。这就能够保证应用到相同形状的填充效果将会绘制到新的边界中。 使用setPathEffect方法可以把PathEffect应用到Paint对象中,如下所示:

  1. paint.setPathEffect(new CornerPathEffect(10)); 其他效果懒得测试了,这个在模拟器上跑的时候效果也不明显,但是真机上跑的时候的确圆滑了许多,看上去很舒服 方法七:
  2. //设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
  3. setXfermode(Xfermode xfermode);
    橡皮擦,这是个好方法啊,看看。

可以通过修改Paint的Xfermode来影响在Canvas已有的图像上面绘制新的颜色的方式。 在正常的情况下,在已有的图像上绘图将会在其上面添加一层新的形状。如果新的Paint是完全不透明的,那么它将完全遮挡住下面的Paint;如果它是部分透明的,那么它将会被染上下面的颜色。下面的Xfermode子类可以改变这种行为:

1)AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。

2)PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素XOR操作。

3)PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。

要应用转换模式,可以使用setXferMode方法,如下所示:

1 AvoidXfermode avoid = new AvoidXfermode(Color.BLUE, 10, AvoidXfermode.Mode. AVOID); 

2 borderPen.setXfermode(avoid);
这里可以实现完美的橡皮擦功能!代码异常简单:

1 Xfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);

2 paint.setXfermode(xFermode); 这是使用的最后一个子类,关于16条Porter-Duff规则,如下:

	  private static final Xfermode[] sModes = {
	        new PorterDuffXfermode(PorterDuff.Mode.CLEAR),      //清空所有,要闭硬件加速,否则无效
	        new PorterDuffXfermode(PorterDuff.Mode.SRC),        //显示前都图像,不显示后者
	        new PorterDuffXfermode(PorterDuff.Mode.DST),        //显示后者图像,不显示前者
	        new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),   //后者叠于前者
	        new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),   //前者叠于后者
	        new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),     //显示相交的区域,但图像为后者
	        new PorterDuffXfermode(PorterDuff.Mode.DST_IN),     //显示相交的区域,但图像为前者
	        new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),    //显示后者不重叠的图像
	        new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),    //显示前者不重叠的图像
	        new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),   //显示前者图像,与后者重合的图像
	        new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),   //显示后者图像,与前者重合的图像
	        new PorterDuffXfermode(PorterDuff.Mode.XOR),        //显示持有不重合的图像
	        new PorterDuffXfermode(PorterDuff.Mode.DARKEN),     //后者叠于前者上,后者与前者重叠的部份透明。要闭硬件加速,否则无效
	        new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),    //前者叠于前者,前者与后者重叠部份透明。要闭硬件加速,否则无效
	        new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),   //显示重合的图像,且颜色会合拼
	        new PorterDuffXfermode(PorterDuff.Mode.SCREEN) };   //显示持有图像,重合的会变白

Mask 虚化

public static void disableHardwareRendering(View v) { if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { v.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } }

在onDraw方法里调用disableHardwareRendering(this);更好点

REPEAT 重复