Android自定义View【实战教程】3⃣️----Paint类、Path类以及PathEffect类详解

Paint类使用详解

Paint 代表了Canvas上的画笔、画刷、颜料等等

方法

作用

setARGB(int a, int r, int g, int b)

设置Paint对象颜色,参数一为alpha透明通道

setAlpha(int a)

设置alpha不透明度,范围为0~255

setMaskFilter(MaskFilter maskfilter)

滤镜效果

setStyle(Style style);

设置画笔风格

setStrokeWidth(int width)

设置画笔空心线宽

setDither(boolean dither)

设定是否使用图像抖动处理,使绘制出的图片颜色更平滑饱满,图像更加清晰

setAntiAlias(boolean aa)

是否抗锯齿

setColor(int color)

设置颜色,这里Android内部定义的有Color类包含了一些常见颜色定义

setFakeBoldText(boolean fakeBoldText)

设置伪粗体文本

setLinearText(boolean linearText)

设置线性文本

setPathEffect(PathEffect effect)

设置路径效果

setRasterizer(Rasterizer rasterizer

设置光栅化

setShader(Shader shader)

设置阴影

setTextAlign(Paint.Align align)

设置文本对齐

setTextScaleX(float scaleX)

设置文本缩放倍数,1.0f为原始

setTextSize(float textSize)

设置字体大小

setTypeface(Typeface typeface)

设置字体,Typeface包含了字体的类型,粗细,还有倾斜、颜色等。

setUnderlineText(boolean underlineText)

设置下划线

setStrokeCap(CAP cap)

线段末端效果

setStrokeJoin(Join join)

闭合图形的连接处效果

先看一个简单的demo:

public class DemoView extends View {
    Paint paint = new Paint();
    Path path = new Path();

    public DemoView(Context context) {
        this(context,null);
    }

    public DemoView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public DemoView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init();
    }

    private void init(){
        paint.setAntiAlias(false);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
        paint.setColor(Color.parseColor("#ff0000"));

        //path在后面进行讲解
        path = new Path();
        path.moveTo(150, 400);
        path.lineTo(450, 400);
        path.lineTo(300, 150);
        path.close();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawPath(path,paint);
    }
}

效果如图:

下面几个重要属性详细介绍:

setAlpha(int a) —透明度

paint.setAlpha(100);

setAntiAlias(boolean aa) —是否抗锯齿

false不抗锯齿

 paint.setAntiAlias(false);

true抗锯齿

 paint.setAntiAlias(true);

setStyle(Style style)—设置画笔风格

Style.FILL:实心 Style.FILL_AND_STROKE:同时显示实心和空心 Style.STROKE:空心

paint.setStyle(Paint.Style.STROKE);

效果如图:

paint.setStyle(Paint.Style.FILL);

setStrokeWidth—空心画笔宽度

setStrokeWidth(int width)

setStrokeCap(CAP cap)—线段末端效果

setStrokeJoin(Join join)—闭合图形的连接处效果

setTextAlign(Paint.Align align)—设置文本对齐

setTextSize(float testSize)—设置字体的大小

    private void init(){
        paint.setAntiAlias(true);
        paint.setColor(Color.parseColor("#ff0000"));
        paint.setTextSize(50);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText("自定义View",200,200,paint);
    }

如图:

setShader(Shader shader)—设置阴影

Shader本身是一个抽象类,它提供了如下实现类:

BitmapShader: 使用位图平铺的渲染效果. LinearGradient: 使用线性渐变来填充图形. RadialGradient: 使用圆形渐变来填充图形. SweepGradient: 使用角度渐变来填充图形. ComposeShader: 使用组合渲染效果来填充图形.

    private void init() {
        paint.setAntiAlias(true);
        paint.setColor(Color.parseColor("#ff0000"));
        paint.setTextSize(50);
        int[] colors = new int[] { Color.RED, Color.GREEN, Color.BLUE };
        Shader shader = new RadialGradient(100, 100, 80,colors, null, TileMode.REPEAT);
        paint.setShader(shader·);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawText("自定义View", 200, 200, paint);
    }

如图:

是不是有种艺术字的效果。

setTextScaleX(float scaleX)—设置缩放倍数

设置水平缩放

 paint.setTextScaleX(2);

如图:

设置字体类型

public static final Typeface DEFAULT; // 默认字体 public static final Typeface DEFAULT_BOLD; //加粗 public static final Typeface SANS_SERIF; //sans serif字体类型 public static final Typeface SERIF; //SERIF字体 public static final Typeface MONOSPACE; //等宽字体

 paint.setTypeface(Typeface.DEFAULT_BOLD);  //加粗

如图:

setUnderlineText(boolean underlineText) —设置下划线

paint.setUnderlineText(true);

setMaskFilter(MaskFilter maskfilter)—滤镜的效果

设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等

Android包含了下面几种MaskFilter: BlurMaskFilter 指定了一个模糊的样式和半径来处理Paint的边缘。 EmbossMaskFilter 指定了光源的方向和环境光强度来添加浮雕效果。

        BlurMaskFilter maskFilter = new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID);

        paint.setMaskFilter(maskFilter);

Path类使用详解

Path类可以预先在View上将N个点连成一条”路径”,然后调用Canvas的drawPath(path,paint)即可沿着路径绘制图形。

下面是常用方法:

方法

作用

备注

moveTo

移动起点

移动下一次操作的起点位置

lineTo

连接直线

连接上一个点到当前点之间的直线

setLastPoint

设置终点

重置最后一个点的位置

close

闭合路劲

从最后一个点连接最初的一个点,形成一个闭合区域

addRect

添加矩形

添加矩形到当前Path

addRoundRect

添加圆角矩形

添加圆角矩形到当前Path

addOval

添加椭圆

添加椭圆到当前Path

addCircle

添加圆

添加圆到当前Path

addPah

添加路劲

添加路劲到当前Path

addArc

添加圆弧

添加圆弧到当前Path

arcTo

圆弧

绘制圆弧,注意和addArc的区别

isEmpty

是否为空

判定Path是否为空

isRect

是否为矩形

判定Path是否是一个矩形

set

替换路劲

用新的路劲替换当前路劲的所有内容

offset

偏移路劲

对当前的路劲进行偏移

quadTo

贝塞尔曲线

二次贝塞尔曲线的方法

cubicTo

贝塞尔曲线

三次贝塞尔曲线的方法

rMoveTo,rlineTo,rQuadTo,rCubicTo

rXxx方法

不带r的方法是基于原点坐标系(偏移量),带r的基于当前点坐标系(偏移量)

op

布尔操作

对两个Path进行布尔运算(交集,并集)等操作

setFillType

填充模式

设置Path的填充模式

getFillType

填充模式

获取Path的填充

isInverseFillType

是否逆填充

判断是否是逆填充模式

toggleInverseFillType

相反模式

切换相反的填充模式

getFillType

填充模式

获取Path的填充

incReserve

提示方法

提示Path还有多少个点等待加入

computeBounds

计算边界

计算Path的路劲

reset,rewind

重置路劲

清除Path中的内容(reset相当于new Path , rewind 会保留Path的数据结构)

transform

矩阵操作

矩阵变换

由于属性过多,我们分组进行讲解: 先创建画笔:

public DemoView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        paint.setAntiAlias(true);
        paint.setColor(Color.parseColor("#ff0000"));
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
}

第一组:画线段(绝对坐标) XXXTo方法

moveTo , lineTo , setLastPoint , close 我们来看上面绘制的三角形

        path.moveTo(150, 400); //起点 (150,400)
        path.lineTo(450, 400); //移动到(450,400)
        path.lineTo(300, 150); //移动到(300,150)
        path.close(); //回到起点

我们把path.close() 去掉, 看一下效果

第二组:画线段(相对坐标) rXXXTo方法

rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3) rLineTo(float dx, float dy) rMoveTo(float dx, float dy) rQuadTo(float dx1, float dy1, float dx2, float dy2)

我们看一下代码就知道什么情况了:

    path.moveTo(100, 100);
    path.LineTo(200, 200);

这里的move和lineTo的坐标都是对于画布左上角(0,0)来说的,是一个绝对坐标。

    path.moveTo(100, 100);
    path.rLineTo(200, 200);

这里的(200,200)是相对于开始点(100,100)来说的,是相对坐标。

第三组:画贝赛尔曲线

quadTo,cubicTo 二次贝塞尔曲线以及三次贝塞尔曲线

1.quadTo

//quadTo方法其中 (x1,y1) 为控制点,(x2,y2)为结束点。
public void quadTo(float x1, float y1, float x2, float y2) 

解释:其中quadTo的前两个参数为控制点的坐标,后两个参数为终点坐标,至于起点默认是画布的左上角。这里的p0就是起点,(x1,y1)就是中点P1,(x2,y2)就是末端点P2。

        path = new Path();
        path.moveTo(150, 400);
        path.quadTo(200, 200, 400, 400);

效果如图:

2、cubicTo

//cubicTo方法比quadTo方法多了一个点坐标,那么其中(x1,y1) 为控制点,(x2,y2)为控制点,(x3,y3) 为结束点。
public void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

解释:与quadTo类似,前四个参数表示两个控制点,最后两个参数表示终点。其实,(x1,y1)就是P1,(x2,y2)是P2,(x3,y3)是P3。

path.moveTo(100, 400);
        path.cubicTo(200, 200, 300, 200, 400, 400);

效果如图:

第四组:画弧线

arcTo (RectF oval, float startAngle, float sweepAngle) 上面这个方法就是画圆弧,说白了就是先确定一个矩形区域,这个区域就是圆或者椭圆的区域,然后在上面截取一段。

    path.moveTo(100, 100);
    RectF oval = new RectF(100, 100, 200, 200);
    path.arcTo(oval, 0, 90);

这里你会发现有一条从起点到圆弧的直线,那么使用下面的方法可以重置起点。

arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

    path.moveTo(100, 100);
    RectF oval = new RectF(100, 100, 200, 200);
    path.arcTo(oval, 0, 90,true);

第五组:添加path(addXXX方法)

addXXX方法则可以让我们直接往Path中添加一些曲线。 比如:

addArc(RectF oval, float startAngle, float sweepAngle) addCircle(float x, float y, float radius, Path.Direction dir) addOval(float left, float top, float right, float bottom, Path.Direction dir) addRect(float left, float top, float right, float bottom, Path.Direction dir) addRoundRect(float left, float top, float right, float bottom, float rx, float ry, Path.Direction dir)

我们注意到addArc和其他四个方法的区别在于Path.Direction这个参数,因为addArc添加的是一个开放曲线,而其他的方法添加的是闭合曲线,所以Path.Direction就是闭合曲线的方向。

让我们看一下示例:

    path.moveTo(100, 100);
    path.rLineTo(200, 200);
    RectF oval = new RectF(100, 100, 200, 200);
    path.addArc(oval, 0, 90);

这是我们上面讲的两个图形 add到了一起。

是不是豁然开朗,那再让我们看一下其他的需要使用Path.Direction这个的效果。

    path = new Path();
    path.moveTo(100, 100);
    path.rLineTo(200, 200);
    RectF oval = new RectF(100, 100, 300, 300);
    path.addRect(oval, Path.Direction.CW);

那么上面的例子可能看不出来CW和CCW的太大区别,下面的例子就可以直观的看出来了。

     path = new Path();
     RectF oval = new RectF(100, 100, 300, 300);
     path.addRect(oval, Path.Direction.CW);
     canvas.drawPath(path,paint);
     paint.setTextSize(60);
     canvas.drawTextOnPath("12345",path,0,0,paint);

Path.Direction只有两个常量值CCW和CW分别表示逆时针方向闭合和顺时针方向闭合。 CW

CCW

第六组:path的集合运算(op方法)

先看下代码

    Path path1 = new Path();
    path1.addCircle(120, 120, 100, Path.Direction.CW);
    Path path2 = new Path();
    path2.addCircle(200, 200, 100, Path.Direction.CW);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        path1.op(path2, Path.Op.UNION); //这边使用不同属性效果如下表
    }
    canvas.drawPath(path1, paint);

逻辑名称

类比

说明

示意图

DIFFERENCE

差集

Path1中减去Path2后剩下的部分

REVERSE_DIFFERENCE

差集

Path2中减去Path1后剩下的部分

INTERSECT

交集

Path1与Path2相交的部分

UNION

并集

包含全部Path1和Path2

XOR

异或

包含Path1与Path2但不包括两者相交的部分

第七组:填充效果(setFillType方法)

参数

解释

效果

WINDING

非零环绕数规则填充

INVERSE_WINDING

和WINDING相反

EVEN_ODD

奇偶规则填充

INVERSE_EVEN_ODD

和EVEN_ODD相反


PathEffect类使用详解

PathEffect是用来控制绘制轮廓(线条)的方式。

常用PathEffect如下:

方法

作用

CornerPathEffect

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

DashPathEffect

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

DiscretePathEffect

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

PathDashPathEffect

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

SumPathEffect

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

ComposePathEffect

组合效果,这个类需要两个PathEffect参数来构造一个实例,ComposePathEffect (PathEffect outerpe,PathEffect innerpe),表现时,会首先将innerpe表现出来,然后再在innerpe的基础上去增加outerpe的效果。。

看一个demo就知道这几个效果的区别了。

paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(4);
        //创建,并初始化Path
        path = new Path();
        path.moveTo(100, 100);
        path.rLineTo(90,0);
        path.rLineTo(-80,50);
        path.rLineTo(35,-80);
        path.rLineTo(45,80);
        path.close();

        //初始化七个颜色
        colors = new int[] {
                Color.BLACK,Color.BLUE,Color.CYAN,
                Color.GREEN,Color.MAGENTA,Color.RED,Color.YELLOW,Color.BLACK
        };

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //将背景填充成白色
        canvas.drawColor(Color.WHITE);
        //-------下面开始初始化7中路径的效果
        //使用路径效果
        effects[0] = null;
        //使用CornerPathEffect路径效果
        effects[1] = new CornerPathEffect(10);
        //初始化DiscretePathEffect
        effects[2] = new DiscretePathEffect(3.0f,5.0f);
        //初始化DashPathEffect
        effects[3] = new DashPathEffect(new float[]{20,10,5,10},phase);
        //初始化PathDashPathEffect
        Path p = new Path();
        p.addRect(0, 0, 8, 8, Path.Direction.CCW);
        effects[4] = new PathDashPathEffect(p,12,phase,PathDashPathEffect.Style.ROTATE);
        //初始化PathDashPathEffect
        effects[5] = new ComposePathEffect(effects[2],effects[4]);
        effects[6] = new SumPathEffect(effects[4],effects[3]);
        //将画布移到8,8处开始绘制
        canvas.translate(8, 8);
        //依次使用7中不同路径效果,7种不同的颜色来绘制路径
        for(int i = 0; i < effects.length; i++)
        {
            paint.setPathEffect(effects[i]);
            paint.setColor(colors[i]);
            canvas.drawPath(path, paint);
            canvas.translate(0, 60);
        }
        //改变phase值,形成动画效果
        phase += 1;
        invalidate();
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android知识点总结

Android材料设计之Behavior攻坚战

接触目标view时才会回调:onStartNestedScroll 加了layout_behavior的View是child

24520
来自专栏有困难要上,没有困难创造困难也要上!

Android项目编译报错Failed to resolve: firebase-iid-interop

修改 build.gradle 文件,将其中 jcenter() 和 google() 两个仓库换一下顺序,比如

12520
来自专栏AndroidTv

前端入门7-JavaScript语法之相关术语声明正文-相关术语

作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。

11130
来自专栏编码前线

Android中的序列化:Parcelable和Serializable

13510
来自专栏Android先生

Flutter组件学习(二)—— Image

上一节中,我们讲了 Flutter 中 Text 组件的一些用法以及 API,本节我们继续学习 Flutter 中的 Image 组件,同样先上图:

28130
来自专栏Android知识点总结

Android资源res之矢量图完全指南(加SVG-path命令分析)

16340
来自专栏Android 开发者

用新技术 “派生” 动听旋律 | Android 开发者故事

QQ 音乐 Android 团队平台组的负责人阿宝,在和 QQ 音乐一同寻求突破的过程中,他发现创意、研发和平台的助力是让应用进步的三个重要因素。所以,他和团队...

14220
来自专栏非著名程序员

我今天浏览 Twitter 的时候,发现了一个好东西

今天很累,很累,本来不想写文章来,所以晚上回家之后,打开 Twitter 想浏览一下外国的新闻,顺便看看 NBA 的集锦来。可是,像我这么爱学习的一个人, Tw...

17620
来自专栏Android 开发者

[译] 为用户提供安全可靠的体验

我们不遗余力地关注 Google Play Store 的安全性和隐私,以确保 Android 用户拥有发现和安装他们喜欢的应用程序和游戏的积极体验。我们定期更...

11340
来自专栏androidBlog

聊一聊Android 中巧妙的位操作

如果不采用或运算符来写,采用布尔值来记录每一种状态,那每一次绘制 TextView 的时候,你得判断多少次,才能得出 TextView 的对其方向。

10530

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励