在上一篇博客中介绍了自定义View的几个常用类,在这一篇博客中接着介绍另外的一个常用类,Paint类:
Paint翻译为“画笔”,为绘图定义各种参数:颜色、线条样式、图案样式等。通常的绘图思路是先定义Paint对象,指定绘图参数,再通过Canvas对象进行图形绘制,绘图的结果因Paint的不同而不同。绘图的方法定义在Canvas类中,Paint类用于指定绘图的各种参数。
4.Paint类
Paint类用于定义绘图时的参数,主要包含颜色、文本、图形样式、位图模式、滤镜等几个方面。 通过控制这些参数,我们就可以控制Paint的样式,绘制不同风格的文本、图片等。
颜色是指绘图时使用的颜色,在 Android 中颜色可以指定透明度,使用 16 进制来表示颜色时,格式通常为#AARRGGBB,其中,AA 表示透明度、RR 表示红色、GG 表示绿色、BB 表示蓝色,Color类定义了颜色信息,内置了常用颜色的int型常量,比如Color.RED 红色,Color.BLUE 蓝色,同时在Color类中定义的一个静态方法parseColor(String colorString)将16进制装换为color类型,比如:
- int color = Color.parseColor("#FF552E");
① 创建一个Paint对象
-
- public Paint()
-
- public Paint(int flags)
-
- public Paint(Paint paint)
-
-
- Paint paint = new Paint();
- paint.setFlags(Paint.ANTI_ALIAS_FLAG);
-
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
② Paint类与颜色相关的方法
-
- public native void setColor(int color);
-
- public native void setAlpha(int a);
-
- public void setARGB(int a, int r, int g, int b)
-
- public native int getAlpha();
-
- public native void getColor(int color);
③ Paint类与文本相关的方法
-
- public native void setTextSize(float textSize)
-
- public void setTextAlign(Paint.Align align)
-
- public native void setTextSkewX(float skewx)
-
- public native void setUnderlineText(boolean underline)
-
- public native void setFakeBoldText(boolean bold)
-
- public native void setStrikeThruText(boolean strike)
④ Paint类与图形样式相关的方法
-
- public void setStyle(Paint.Style style)
- style的可选值有:
- public static enum Style {
- FILL
- FILL_AND_STROKE,
- STROKE
- }
其中,FILL 表示实心样式,对于闭合图形来说,会用指定的颜色进行填充;STROKE表示空心样式,绘制时只有线条而无填充效果;FILL_AND_STROKE 表示同时使用实心样式和空心样式。
-
- public void setStrokeJoin(Paint.Join join)
- 可选值如下:
- public static enum Join {
- BEVEL,
- MITER,
- ROUND
- }
Join三种类型区别可以用下图表示:

-
- public void setStrokeCap(Paint.Cap cap)
- 可选值如下:
- public static enum Cap {
- BUTT,
- ROUND,
- SQUARE
- }
Cap三种类型区别可以用下图表示:

-
- public native void setStrokeWidth(float width)
-
- public void reset()
下面的代码是在canvas上面绘制一段文字和两个不同样式的矩形:
- @Override
- protected void onDraw(Canvas canvas) {
- paint.setFlags(Paint.ANTI_ALIAS_FLAG);
- paint.setColor(Color.parseColor("#FF0000"));
- paint.setTextSize(50);
- paint.setTextSkewX(0.2f);
- paint.setFakeBoldText(true);
- paint.setUnderlineText(true);
- canvas.drawText("Android自定义控件", 100, 100, paint);
-
- paint.reset();
- paint.setFlags(Paint.ANTI_ALIAS_FLAG);
- paint.setColor(Color.parseColor("#0000FF"));
- paint.setStyle(Paint.Style.STROKE);
- paint.setStrokeWidth(30);
- paint.setStrokeJoin(Paint.Join.ROUND);
-
- canvas.drawRect(100, 150, 400, 350, paint);
-
- paint.reset();
- paint.setFlags(Paint.ANTI_ALIAS_FLAG);
- paint.setColor(Color.parseColor("#00FF00"));
- paint.setStyle(Paint.Style.FILL);
-
- canvas.drawRect(450,150,850,350,paint);
- }
结果如图:

⑤ Paint类高级使用之阴影、渐变和位图
首先说明:在绘图中,有一个叫layer(层)的概念,默认情况下,我们的文字和图形绘制在主层(mainlayer)上,其实也可以将内容绘制在新建的layer上。而上阴影就是在main layer的下面添加了一个阴影层(shaderlayer),可以为阴影指定模糊度、偏移量和阴影颜色。
阴影:
- Paint类的setShadowLayer()方法,可以设置阴影效果:
- public void setShadowLayer(float radius, float dx, float dy, int shadowColor)
- radius:阴影半径
- dx:x方向阴影的偏移量
- dy:y方向阴影的偏移量
- shadowColor:阴影的颜色
阴影layer显示阴影时,
shader layer有两种类:View.LAYER_TYPE_SOFTWARE、View.LAYER_TYPE_HARDWARE
。
layer的默认类型为LAYER_TYPE_HARDWARE,但阴影只能在View.LAYER_TYPE_SOFTWARE环境下工作,所以,我们如果需要显示阴影想过,就要调用View类的public void setLayerType(int layerType, Paint paint)方法为Paint对象指定层的类型:
setLayerType(View.LAYER_TYPE_SOFTWARE, paint);
我在一个View的onDraw()方法中编写了一下代码:
- @Override
- protected void onDraw(Canvas canvas) {
-
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setColor(Color.parseColor("#f0f5f9"));
- paint.setStyle(Paint.Style.FILL);
- paint.setStrokeWidth(5);
- paint.setTextSize(100);
-
- this.setLayerType(LAYER_TYPE_SOFTWARE, paint);
- paint.setShadowLayer(10, 5, 5, Color.RED);
- canvas.drawText("Android", 50, 200, paint);
- paint.setShadowLayer(5,3,3,Color.GREEN);
- canvas.drawText("自定义组件",50,320,paint);
-
- }
运行的到的结果如图所示:

需要注意的是,一旦定义了阴影层,接下来的所有绘制都会带阴影效果了,如果想取消阴影,请将setShadowLayer()方法的radius参数设置为0
渐变:
渐变(Gradient)是绘图过程中颜色或位图以特定规律进行变化,能增强物体的质感和审美情趣。生活中的渐变非常多,例如公路两边的电线杆、树木、建筑物的阳台、铁轨的枕木延伸到远方等等,很多的自然理象都充满了渐变的形式特点。Android同样对渐变进行了完善支持,通过渐变,可以绘制出更加逼真的效果。
Graphics2D渐变种类有:
线性渐变:LinearGradient
径向渐变:RadialGradient
扫描渐变:SweepGradient
位图渐变:BitmapShader
混合渐变:ComposeShader
定义渐变时,必须指定一个渐变区域,根据定义的渐变内容和渐变模式填满该区域。每一种渐变都被定义成了一个类,他们都继承自同一个父类——Shader。绘图时,调用Paint类的setShader(Shader shader)方法指定一种渐变类型,绘制出来的绘图填充区域都将使用指定的渐变颜色或位图进行填充。
线性渐变、径向渐变和扫描渐变属于颜色渐变,指定2种或2种以上的颜色,根据颜色过渡算法自动计算出中间的过渡颜色,从而形成渐变效果,对于开发人员来说,无需关注中间的渐变颜色。
同时,这三种渐变有三种渐变模式可以选择(A、B表示两种颜色):
ABAB型:A、B两种颜色重复变化,通过TileMode类的REPEAT常量来表示;
ABBA型:A、B两种颜色镜像变化,通过TileMode类的MIRROR常量来表示;
AABB型:A、B两种颜色只出现一次,通过TileMode类的CLAMP常量来表示。

线性渐变:
线性渐变(LinearGradient)根据指定的角度、颜色和模式使用渐变颜色填充绘图区域。我们必须定义两个点(x0,y0)和(x1,y1),渐变的方向与这两个点的连线垂直。
LinearGradient的构造方法如下:
- publicLinearGradient(floatx0,floaty0,floatx1,floaty1,intcolor0,intcolor1,TileModetile):本方法用于两种颜色的渐变,各参数意义如下:
- x0、y0:用于决定线性方向的第一个点的坐标(x0,y0);
- x1、y1:用于决定线性方向的第二个点的坐标(x1,y1);
- color0:第一种颜色;
- color1:第二种颜色;
- tile:渐变模式
-
- publicLinearGradient(floatx0,floaty0,floatx1,floaty1,intcolors[],floatpositions[],TileMode tile),这是一个功能更加强大的构造方法,我们来看看该构造方法参数的作用:
- x0、y0:起始点的坐标
- x1、y1:终止点的坐标
- colors:多种颜色
- positions:颜色的位置(比例)
- TileMode:渐变模式
以下代码都是以Shader.TileMode.CLAMP模式,并且都是2种渐变颜色,只是改变了渐变矩形的大小,能得到不同的效果
- private void linerGradientTest(Canvas canvas) {
- canvas.save();
- Rect rect = new Rect(50, 50, 200, 200);
-
- LinearGradient lg = new LinearGradient(rect.left, rect.top, rect.right, rect.bottom, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
- paint.setShader(lg);
- canvas.drawRect(rect, paint);
-
- Rect rect1 = new Rect(rect);
- rect1.inset(70, 70);
- canvas.translate(rect.width() + 20, 0);
- LinearGradient lg1 = new LinearGradient(rect1.left, rect1.top, rect1.right, rect1.bottom, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
- paint.setShader(lg1);
- canvas.drawRect(rect, paint);
-
- Rect rect2 = new Rect(rect);
- rect1.inset(-70, -70);
- canvas.translate(rect.width() + 20, 0);
- LinearGradient lg2 = new LinearGradient(rect2.left, rect2.top, rect2.right, rect2.bottom, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
- paint.setShader(lg2);
- canvas.drawRect(rect, paint);
-
- canvas.restore();
- }
下面的代码分别使用了两种渐变颜色和三种渐变颜色:
- private void linerGradient(Canvas canvas) {
- canvas.save();
- Rect rect = new Rect(30, 30, 450, 450);
-
- LinearGradient lg = new LinearGradient(rect.left, rect.top + rect.height() / 2, rect.right, rect.top + rect.height() / 2, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
- paint.setShader(lg);
- canvas.drawRect(rect, paint);
-
- canvas.translate(rect.width() + 20, 0);
-
- LinearGradient lg1 = new LinearGradient(rect.left, rect.top + rect.height() / 2, rect.right, rect.top + rect.height() / 2, new int[]{Color.RED, Color.BLUE,Color.YELLOW},new float[]{0.3f,0.6f,0.8f}, Shader.TileMode.CLAMP);
- paint.setShader(lg1);
- canvas.drawRect(rect, paint);
-
- canvas.restore();
- }
径向渐变
径向渐变是以指定的点为中心,向四周以渐变颜色进行圆周扩散,和线性渐变一样,支持两种或多种颜色。
径向渐变的主要构造方法如下:
- public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile),该构造方法支持两种颜色,下面是参数的作用:
- x、y:中心点坐标
- radius:渐变半径
- color0:起始颜色
- color1:结束颜色
- TileMode:渐变模式
- public RadialGradient(float x, float y, float radius, int colors[], float positions[], TileMode tile),该构造方法支持3种或3种以上颜色的渐变,各参数的作用如下:
- x、y:中心点坐标
- radius:渐变半径
- colors:多种颜色
- positions:颜色的位置(比例)
- TileMode:渐变模式
以下代码定义了径向渐变:
- private void radiaGradientTest(Canvas canvas) {
- canvas.save();
-
- RadialGradient rg = new RadialGradient(200,200,180,Color.RED,Color.GREEN, Shader.TileMode.CLAMP);
- paint.setShader(rg);
- canvas.drawCircle(200, 200, 180, paint);
-
- canvas.translate(420,0);
-
- RadialGradient rg1 = new RadialGradient(200,200,180,Color.RED,Color.GREEN, Shader.TileMode.CLAMP);
- paint.setShader(rg1);
- canvas.drawRect(20, 0, 380, 360, paint);
-
- canvas.translate(-420, 450);
-
- RadialGradient rg2 = new RadialGradient(200,200,220,new int[]{Color.RED, Color.GREEN,Color.YELLOW},new float[]{0.3f,0.6f,0.8f}, Shader.TileMode.CLAMP);
- paint.setShader(rg2);
- canvas.drawCircle(200, 200, 180, paint);
-
- canvas.translate(420,0);
-
- RadialGradient rg3 = new RadialGradient(200,200,220,new int[]{Color.RED, Color.GREEN,Color.YELLOW},new float[]{0.3f,0.6f,0.8f}, Shader.TileMode.CLAMP);
- paint.setShader(rg3);
- canvas.drawRect(20,0,380,360,paint);
- canvas.restore();
- }
运行结果如图:

扫描渐变
SweepGradient类似于军事题材电影中的雷达扫描效果,固定圆心,将半径假想为有形并旋转一周而绘制的渐变颜色。
SweepGradient定义了两个主要的构造方法:
- publicSweepGradient(floatcx,floatcy,intcolor0,intcolor1)
- 支持两种颜色的扫描渐变,参数的作用如下:
- cx、cy:圆点坐标;
- color0:起始颜色;
- color1:结束颜色。
- publicSweepGradient(floatcx,floatcy,intcolors[],floatpositions[])
- 支持多种颜色的扫描渐变,参数的作用如下:
- cx、cy:圆点坐标;
- colors:多种颜色;
- positions:颜色的位置(比例)
以下代码定义了扫描渐变:
- private void sweepGradientTest(Canvas canvas) {
- canvas.save();
- SweepGradient sg = new SweepGradient(300,300,new int[]{Color.GREEN,Color.YELLOW,Color.BLUE,Color.GREEN}, null);
- paint.setShader(sg);
- canvas.drawCircle(300, 300, 280, paint);
-
- SweepGradient sg1 = new SweepGradient(300,300,new int[]{Color.GREEN,Color.YELLOW,Color.BLUE,Color.GREEN}, new float[]{0.2f,0.5f,0.8f,0.9f});
- paint.setShader(sg1);
- canvas.drawCircle(300, 300, 280, paint);
- canvas.restore();
- }
运行结果如图:

位图渐变
位图渐变其实就是在绘制的图形中将指定的位图作为背景,如果图形比位图小,则通过渐变模式进行平铺,TileMode.CLAMP模式不平铺,TileMode.REPEAT模式表示平铺,TileMode.MIRROR模式也表示平铺,但是交错的位图是彼此的镜像,方向相反。可以同时指定水平和垂直两个方向
的渐变模式。
BitmapShader只有一个构造方法:
public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY),参数如下:
bitmap:位图;
tileX:x方向的重复模式;
tileY:y方向的重复模式。
以下代码定义了一个水平和竖直方向都平铺的位图渐变:
- private void bitmaGradientTest(Canvas canvas) {
- Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
- BitmapShader bs = new BitmapShader(bmp,Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setShader(bs);
- canvas.drawRect(new Rect(0, 0,1000, 1000), paint);
- }
运行结果如图:

混合渐变
混合渐变ComposeShader是将两种不同的渐变通过位图运算后得到的一种更加复杂的渐变。
ComposeShader有两个构造方法:
- publicComposeShader(ShadershaderA,ShadershaderB,Xfermodemode)
- publicComposeShader(ShadershaderA,ShadershaderB,Modemode)
shaderA和shaderB是两个渐变对象,mode为位图运算类型,16种运算模式如图(图片来源网络):

以下代码定义了位图渐变和扫描渐变相结合:
- private void composeGradientTest(Canvas canvas) {
- Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
- BitmapShader bs = new BitmapShader(bmp,Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);
-
- SweepGradient sg = new SweepGradient(500, 525, new int[]{Color.GREEN, Color.YELLOW, Color.BLUE, Color.GREEN}, null);
- ComposeShader sc = new ComposeShader(bs,sg, PorterDuff.Mode.SRC_IN);
- Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setShader(sc);
- canvas.drawRect(new Rect(0, 0,1000, 1000), paint);
- }
运行结果如下图:

位图运算PorterDuffXfermode
位图运算为位图的功能繁衍ᨀ供了强大的技术基础,增强了位图的可塑性和延伸性,使很多看起来非常复杂的效果和功能都能轻易实现,比如圆形头像、不规则图片、橡皮擦、稀奇古怪的自定义进度条等等。
位图运算模式定义在PorterDuff类的内部枚举类型Mode中,对应了16个不同的枚举值
- publicenumMode{
-
- CLEAR(0),
-
- SRC(1),
-
- DST(2),
-
- SRC_OVER(3),
-
- DST_OVER(4),
-
- SRC_IN(5),
-
- DST_IN(6),
-
- SRC_OUT(7),
-
- DST_OUT(8),
-
- SRC_ATOP(9),
-
- DST_ATOP(10),
-
- XOR(11),
-
-
- DARKEN(16),
-
-
- LIGHTEN(17),
-
- MULTIPLY(13),
-
- SCREEN(14),
-
- ADD(12),
- OVERLAY(15);
-
- Mode(intnativeInt){
- this.nativeInt=nativeInt;
- }
-
-
-
-
- publicfinalintnativeInt;
- }
具体使用哪一种模式才能达到效果,可以参考下图(图片来源网络):

以下代码通过位图运算简单实现了圆形图片的制作:
- public class CircleImage extends View {
- private Paint paint;
- private Bitmap bitmap;
- private int width,height,centerX,centerY,radius;
-
- public CircleImage(Context context) {
- this(context, null);
- }
-
- public CircleImage(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public CircleImage(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
- }
-
- private void init() {
- paint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.a);
-
- width = bitmap.getWidth();
- height = bitmap.getHeight();
- centerX = width / 2;
- centerY = height / 2;
- radius = Math.min(width,height) / 2;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
-
-
-
- int saveLayer = canvas.saveLayer(0, 0, width, height, paint, Canvas.ALL_SAVE_FLAG);
-
- canvas.drawCircle(centerX,centerY,radius,paint);
-
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(bitmap, 0, 0, paint);
-
- paint.setXfermode(null);
-
- paint.setStrokeWidth(3);
- paint.setStyle(Paint.Style.STROKE);
- paint.setColor(Color.parseColor("#65f692"));
- canvas.drawCircle(centerX,centerY,radius,paint);
-
- canvas.restoreToCount(saveLayer);
- }
- }
运行结果如图所示:
