自定义View之Paint(画笔)的详解

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明作者和出处。 https://blog.csdn.net/Jsagacity/article/details/81670488

Android提供了2D图形绘制的各种工具,如Canvas(画布)、Point(点)、Paint(画笔)、Rectangles(矩形)等,利用这些工具可以直接在界面上进行绘制。

在自定义View中,我们经常用到的Canvas(画布)和Paint(画笔),像我们画画一样,需要画布和画笔,在View中绘制控件,Canvas就代表着画布,Paint就代表着画笔。

这是的Android的的的官网里画的API:https://developer.android.com/reference/android/graphics/Paint

官网中的API有很多,下面是比较常用的一些API:

Paint.setAntiAlias(boolean flag);//设置抗锯齿效果 设置true的时边缘会将锯齿模糊化
Paint.setDither(boolean flag);//设置防抖动,设置true的时图片看上去会更柔和点
Paint.setColor(int color);//设置画笔颜色

Paint.setARGB(int a, int r, int g, int b); //设置画笔的ARGB值
Paint.setAlpha(int alpha);//设置画笔的Alpha值
Paint.setStyle(); //设置画笔的style (三种:FILL填充 FILL_AND_STROKE填充加描边 STROKE描边 )
Paint.setStrokeWidth(float width);//设置描边宽度
Paint.setXfermode(Xfermode xfermode);//设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
Paint.setShader(Shader shader);//设置图像效果,使用Shader可以绘制出各种渐变效果
Paint.setShadowLayer(float radius ,float dx,float dy,int color);//在图形下面设置阴影层,产生阴影效果,radius为阴影的半径,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色 

//下面写文本的时候经常用到的
Paint.setTextSize(float textSize);//设置画笔文字大小
Paint.measureText(String text);//测试文本的长度
Paint.setTextAlign(Paint.Align align);// CENTER(文本居中) LEFT(文本左对齐) RIGHT(文本右对齐)

下面就演示一下上面这几个API的效果。


Paint.setStyle()

Paint.setStyle() //设置画笔的style,有三种

  • Paint.Style.FILL //将填充使用此样式绘制的几何和文本,忽略绘画中与笔划相关的所有设置
  • Paint.Style.FILL_AND_STROKE //使用此样式绘制的几何和文本将同时填充和描边,尊重绘画中与笔划相关的字段
  • Paint.Style.STROKE //使用此样式绘制的几何和文本将被描边,尊重绘画上与笔划相关的字段

演示一个小demo:

        paint = new Paint();
        paint.setColor(Color.RED);//画笔颜色为红色
        paint.setStrokeWidth(80); //描边宽度为80(为了区分效果,特意设置特别大)

        float radius = 100f;

        //将填充使用此样式绘制的几何和文本,忽略绘画中与笔划相关的所有设置
        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(400, 200, radius, paint);


        //使用此样式绘制的几何和文本将同时填充和描边,尊重绘画中与笔划相关的字段
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.drawCircle(400, 500, radius, paint);


        //使用此样式绘制的几何和文本将被描边,尊重绘画上与笔划相关的字段
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(400, 900, radius, paint);

结果:
这里写图片描述


Paint.setShader(Shader shader)

Paint.setShader(Shader shader) //设置图像效果,使用Shader可以绘制出各种渐变效果

Shader:着色器,用来给图像着色,此类是基类, Shader的API 。有5个子类:

  • BitmapShader
  • ComposeShader
  • LinearGradient
  • RadialGradient
  • SweepGradient

在了解上面5个类之前,先了解一下Shader.TileMode这个枚举,有三个值:

  • Shader.TileMode.CLAMP :如果着色器在其原始边界之外绘制,则复制边缘颜色
  • Shader.TileMode.MIRROR :水平和垂直重复着色器的图像,交替镜像,使相邻的图像始终接缝
  • Shader.TileMode.REPEAT :水平和垂直重复着色器的图像


BitmapShader

其实这个Shader用于绘制bitmap作为纹理,然后通过平铺模式进行填充

        /**
         * 构造函数
         * @bitmap 用来填充图形的Bitmap
         * @tileX X轴Bitmap用Shader.TileMode模式填充
         * @tileY Y轴Bitmap用Shader.TileMode模式填充
         */
        BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

演示一下:

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.flower);
        BitmapShader shader = new BitmapShader(bitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.MIRROR);
        paint.setShader(shader);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);

结果:
这里写图片描述
X轴用Shader.TileMode.CLAMP模式,就是用bitmap的右边缘去填充X轴的其余空间
Y轴用Shader.TileMode.MIRROR模式,就是在用相邻两张图像互为镜像的方式填充整个Y轴其余空间

接下来XY轴换一下模式:

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.flower);
        BitmapShader shader = new BitmapShader(bitmap, BitmapShader.TileMode.MIRROR, BitmapShader.TileMode.REPEAT);
        paint.setShader(shader);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);

结果:
这里写图片描述
X轴用Shader.TileMode.MIRROR模式,就是在用相邻两张图像互为镜像的方式填充整个X轴其余空间
Y轴用Shader.TileMode.REPEAT模式,就是用相同的图像重复填充整个Y轴其余空间


LinearGradient

LinearGradient:它是沿一条直线用来创建线性渐变效果,(x0,y0),(x1,y1)分别是起始坐标和终止坐标,color0,color1分别是起始颜色和终止颜色,tile为Shader.TileMode(CLAMP,REPEAT,MIRROR)模式中的一个。

        /**
         * 构造函数
         * @x0 渐变线起始坐标的X坐标
         * @y0 渐变线起始坐标的Y坐标
         * @x1 渐变线终止坐标的X坐标
         * @y1 渐变线终止坐标的Y坐标
         * @color0 渐变线起始颜色
         * @color1 渐变线终止颜色
         * @tile 渐变线用Shader.TileMode模式填充
         */
        LinearGradient( float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile);

演示一下:

        LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, Color.GREEN, Color.RED, Shader.TileMode.MIRROR);
        paint.setShader(linearGradient);
        canvas.drawRect(200, 200, 600, 600, paint);

结果:
这里写图片描述

修改一下,扩大范围:

        LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, Color.GREEN, Color.RED, Shader.TileMode.MIRROR);
        paint.setShader(linearGradient);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);

然后在LinearGradient构造函数里面的Shader.TileMode模式,分别改成CLAMP、MIRROR和REPEAT。结果为:
这里写图片描述

LinearGradient的另一个构造函数:

        /**
         * 构造函数
         * @colors[] 用colors数组线性填充
         * @positions[] 每个position取值范围[0,1],并且和colors数组中对应位置的color一一对应
         */
        LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)

演示一下:

        int[] colors = {Color.GREEN, Color.GRAY, Color.RED, Color.BLUE};
        float[] positions = {0f, 0.5f, 0.75f, 1f};
        LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, colors, positions, Shader.TileMode.REPEAT);
        paint.setShader(linearGradient);
        canvas.drawRect(200, 200, 600, 600, paint);

结果:
这里写图片描述

修改一下,扩大范围:

        int[] colors = {Color.GREEN, Color.GRAY, Color.RED, Color.BLUE};
        float[] positions = {0f, 0.5f, 0.75f, 1f};
        LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, colors, positions, Shader.TileMode.REPEAT);
        paint.setShader(linearGradient);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);

一样的转换三种模式看看:
这里写图片描述


RadialGradient

RadialGradient:它也用来创建渐变效果,和LinearGradient不同的是,LinearGradient是线性渐变,而RadialGradient是径向渐变,也就是从中心向四周发散渐变,RadialGradient也有两个构造函数。先来看看第一个:

        /**
         *构造函数
         * @centerX 圆中心的X轴坐标
         * @centerY 圆中心的Y轴坐标
         * @radius 圆半径
         * @centerColor 圆中心颜色
         * @edgeColor 圆边缘颜色
         * @tileMode 径向渐变Shader.TileMode模式填充
         */
        RadialGradient( float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)

演示一下:

        float radius = 400f;
        RadialGradient gradient = new RadialGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, Color.RED, Color.YELLOW, Shader.TileMode.CLAMP);
        paint.setShader(gradient);
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, paint);

结果:
这里写图片描述
扩大一下范围:

        float radius = 300f;
        RadialGradient gradient = new RadialGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT);
        paint.setShader(gradient);
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);

同样转换三种模式看看:
这里写图片描述

RadialGradient的另一个构造函数:

        /**
         * 构造函数
         * @colors[] color数组分布在圆的中心和边缘之间
         * @stops[] 取值范围在[0.0f,1.0f],并且和colors数组中对应位置的color一一对应,如果为null,颜色均匀的分布在中心和边缘之间
         */
        RadialGradient(float centerX, float centerY, float radius,int colors[], float stops[],TileMode tileMode)

结果类似LinearGradient第二个构造函数,下来自己去演示。


SweepGradient

SweepGradient:用来创建围绕一个中心点360度沿顺时针旋转渐变效果。

        /**
         * 构造函数
         * @cx 圆中心的X轴坐标
         * @cy 圆中心的Y轴坐标
         * @color0 开始旋转起始颜色,起始点在3点钟方向,顺时针
         * @color1 结束旋转终止颜色,终止点也在3点钟方向
         */
        SweepGradient( float cx, float cy, int color0, int color1)

演示一下:

        SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, Color.GREEN, Color.RED);
        paint.setShader(gradient);
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);

结果:
这里写图片描述

修改一下形状:

        SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, Color.GREEN, Color.RED);
        paint.setShader(gradient);
        canvas.drawRect(0, (getMeasuredHeight() - getMeasuredWidth()) / 2, getMeasuredWidth(), (getMeasuredHeight() + getMeasuredWidth()) / 2, paint);

结果:
这里写图片描述

SweepGradient的另一个构造函数:

        /**
         *  构造函数
         * @colors[] color数组顺时针分布
         * @positions[] 每个position取值范围[0,1],并且和colors数组中对应位置的color一一对应
         */
        SweepGradient(float cx, float cy, int colors[], float positions[])

演示一下:

        int[] colors = {Color.GREEN, Color.YELLOW, Color.BLACK, Color.BLUE, Color.RED};
        float[] positions = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};
        SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, colors, positions);
        paint.setShader(gradient);
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);

结果:
这里写图片描述


ComposeShader

ComposeShader结合Xfermode模式,是两个Shader的组合模式,ComposeShader有两个构造函数:

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

Xfermode可以用于实现新绘制的像素与Canvas上对应位置已有的像素按照混合规则进行颜色混合,Xfermode 有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,前两个已废弃,PorterDuffXfermode初始化时需要传入PorterDuff.Mode 即:PorterDuffXfermode(PorterDuff.Mode mode),所以上面第一个构造函数是第二个构造函数的一种情况,我们只看第一个构造函数就可以了:

        /**
         * 构造函数
         * @shaderA 目标像素DST
         * @shaderB 源像素SRC
         * @mode 新绘制的像素与Canvas上对应位置已有的像素按照混合规则进行颜色混合
         */
        ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)

演示一下:

 // 创建BitmapShader对象
        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR,
                Shader.TileMode.MIRROR);
        // 创建LinearGradient并设置渐变颜色数组,平铺效果为镜像
        LinearGradient linearGradient = new LinearGradient(0, 0, 0, 100, new int[] {
                Color.WHITE, Color.LTGRAY, Color.TRANSPARENT, Color.GREEN }, null,
                Shader.TileMode.MIRROR);
        ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
        paint.setShader(composeShader);
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);

结果:
这里写图片描述

介绍完了,继续学习API

Paint.setShadowLayer(float radius ,float dx,float dy,int color)

Paint.setShadowLayer(float radius ,float dx,float dy,int color) //在图形下面设置阴影层,产生阴影效果

        /**
         * @radius radius为阴影半径,半径越大,阴影面积越大,越模糊;反之,半径越小,阴影面积越小,也越清晰,radius=0时,阴影消失
         * @dx dx为阴影在x轴上的偏移值
         * @dy dy为阴影在y轴上的偏移值
         * @color color为阴影的颜色
         */
        Paint.setShadowLayer( float radius, float dx, float dy, int color);

演示一下:

        paint.setColor(Color.RED);
        paint.setShadowLayer(20, 0, 0, Color.YELLOW);
        paint.setTextSize(100);
        canvas.drawText("I am Layne", 200, 300, paint);

结果:
这里写图片描述

改一下:

paint.setShadowLayer(20,50, 50, Color.YELLOW);

结果:
这里写图片描述

改一下:

paint.setShadowLayer(1,50, 50, Color.YELLOW);

结果:
这里写图片描述

再改一下:

paint.setShadowLayer(0,50, 50, Color.YELLOW);

结果:
这里写图片描述

添加阴影:

        paint.setColor(Color.RED);
        paint.setShadowLayer(30, 0, 0, Color.BLACK);
        setLayerType(LAYER_TYPE_SOFTWARE, paint);//要注意加上这句
        canvas.drawCircle(400, 800, 100, paint);

结果:
这里写图片描述




关注个人公众号,主推 Android 技术文章



参考资料:https://www.jianshu.com/p/2aa0b8585f68

猜你喜欢

转载自blog.csdn.net/Jsagacity/article/details/81670488