自定义控件——绘制基础(一)

什么是自定义控件

自定义控件有三个点,布局绘制和触摸反馈。接下来的章节我们先说绘制,绘制就是在控件上显示需要我们用代码控制绘制细节,显示系统自带控件显示不出的内容,不管是多复杂的显示我们能够通过自定义控件绘制出来。

纸和笔.jpg

android的绘制跟我们平常的画画一样,我们在画画的时候需要笔和纸。android开发中笔就是Paint类,Canvas就是纸。凡是一些设置画笔的粗细、画笔颜色、透明度都是在Paint类中设置的,凡是能画出某种物体比如绘制圆形、矩形、文字都是在canvas里进行完成。
自定义绘制非常容易只需要重写onDraw方法,将创建好Paint对象传入canvas中,这里需要注意的是onDraw方法需要super.onDraw方法,而且android中的坐标系是左上角为(0,0)原点。

 private Paint paint = new Paint();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawRect(10, 10, 100, 100, paint);
    }

Paint相关方法

1.setColor
public void setColor(@ColorInt int color)

设置画笔的颜色,向下边这样。

paint.setColor(Color.YELLOW);
canvas.drawRect(10, 10, 100, 100, paint);
黄色的矩形.jpg
2.setAntiAlias
public void setAntiAlias(boolean aa)

一些不规则的图形例如文字,圆形需要打开抗锯齿功能让边缘更平滑。我们可以通过setAntiAlias方法可以设置是否使用抗锯齿,也可以在Paint构造函数中传入ANTI_ALIAS_FLAG

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 
paint.setAntiAlias(true);

下边绘制一个圆形放大一下可以看到效果

未开锯齿.jpg

开锯齿.jpg

抗锯齿依赖算法实现的,通过修改边缘处的颜色,让图形有了平滑的感觉。

3.setStyle
public void setStyle(Style style)

setStyle方法设置填充样式,对文字以及图形都有效。样式一共有三种,Paint.Style.FILL填充模式,Paint.Style.STROKE描边模式,以及Paint.Style.FILL_AND_STROKE两个模式一起使用,默认情况下是FILL填充模式。

Style样式.jpg

4.setStrokeWidth

设置描边的宽度,在画笔在STROKE和FILL_AND_STROKE时使用。

paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(20);
canvas.drawCircle(200, 200, 50, paint);
setStrokeWidth.jpg

Canvas基础

1.画布颜色设置

canvas可以实现画布的颜色设置,通过以下方法实现。

public void drawColor(@ColorInt int color)
public void drawARGB(int a, int r, int g, int b)
public void drawRGB(int r, int g, int b)

根据drawColor方法可以知道,我们需要传入8位的颜色值,drawARGB传入A、R、G、B的颜色取值0~255范围。drawRGB函数只传入R、G、B,Alpha默认是255。

canvas.drawColor(Color.parseColor("#FF0000"));
canvas.drawRGB(255,0,0);
canvas.drawARGB(255,255,0,0);
画布设置.png
2.绘制点
public void drawPoint(float x, float y, @NonNull Paint paint)

drawPoint方法x,y分别是设置点的坐标,点的大小可以用setStrokeWidth方法设置大小。

paint.setStrokeWidth(80);
canvas.drawPoint(120, 120, paint);

点.jpg

多点绘制可以通过drawPoints方法来实现。

public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
            @NonNull Paint paint)
            
public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint)

这里pts是设置点的坐标,设置为{x1,y1,x2,y2...},offset是跳过数值的个数(一个点有两个数)。count代表绘制数字的个数。

paint.setColor(Color.RED);
paint.setStrokeWidth(80);
float[] pts = {50, 50, 100, 100, 200, 200, 300, 300};
canvas.drawPoints(pts, 2/* 跳过两个数,即前两个 0 */, 6/* 一共绘制 6 个数(3 个点) */, paint);
drawPoints.jpg
3.绘制直线
public void drawLine(float startX, float startY, float stopX, float stopY,
            @NonNull Paint paint)

startX、startY、stopX、stopY分别代表坐标的起始位置以及终止位置。
绘制多条直线

public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
            @NonNull Paint paint)
            
public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint)
直线.png

与多点绘制相似,可以利用drawLines方法绘制多条线段。

4.绘制矩形
public void drawRect(@NonNull RectF rect, @NonNull Paint paint)

public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)

利用drawRect方法可以绘制矩形我们可以通过矩形的四个点传值也可以通过RectF、Rect传值。

paint.setStyle(Paint.Style.STROKE);
canvas.drawRect(100, 100, 200, 200, paint);
矩形.jpg
5.绘制圆角矩形
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)

利用drawRoundRect方法可以绘制出带有圆角的矩形参数RectF设置绘制的矩形,rx、ry分别代表x轴和y轴的半径。

圆角矩形.png

6.绘制圆形
public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

cx和cy分别设置圆心点的坐标,radius设置圆形半径。

canvas.drawCircle(100, 100, 30, paint);
圆形.png
7.绘制椭圆
public void drawOval(@NonNull RectF oval, @NonNull Paint paint)

drawOval是绘制椭圆的方法,根据矩形oval区域绘制出椭圆。

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        paint2.setStyle(Paint.Style.STROKE);
        paint2.setColor(Color.RED);
        paint2.setStrokeWidth(5);
        RectF rect = new RectF(100, 100, 400, 300);
        canvas.drawOval(rect, paint);
        canvas.drawRect(rect, paint2);

椭圆.png

另外绘制椭圆还有其他重载方法,drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)根据边界点绘制椭圆。

8.绘制弧形
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
            @NonNull Paint paint)

弧是椭圆的一部分,椭圆又是根据矩形生成的,所以弧形也是根据矩形而来。
这里oval是矩形区域,startAngle是弧形的起始弧度,sweepAngle是弧形划过的角度;useCenter 表示是否连接到圆心,如果不连接到圆心,就是弧形,如果连接到圆心,就是扇形。

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        paint2.setStyle(Paint.Style.STROKE);
        paint2.setColor(Color.RED);
        paint2.setStrokeWidth(5);
        RectF rect = new RectF(100, 100, 400, 300);
        canvas.drawArc(rect, 0, 90, true, paint);
        canvas.drawArc(rect, 90, 180, false, paint2);
弧线.png

Path路径

在android开发中通过canvas绘制路径

void drawPath(Path path, Paint paint)

接下来看看path下有哪些方法。

1.直线路径

画直线一般需要三个函数

public void moveTo(float x, float y) 

x,y设置起始点的位置。由当前位置 (0, 0) 移动致 (x, y)

public void lineTo(float x, float y)

lineTo方法是根据起始点设置终点。

public void close()

如果前边的划线没有形成闭环那么调用close方法可以首尾连接起来。当需要填充图形时(即 Paint.Style 为 FILL 或 FILL_AND_STROKE),Path 会自动封闭子图形。后边会有介绍。

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        Path path = new Path();
        path.moveTo(150, 150);//移动到(150,150)初始化为原点
        path.lineTo(50, 250);//第一条线
        path.lineTo(250, 250);//第二条线
        path.close();
        canvas.drawPath(path, paint);

close方法调用后形成一个闭环形成一个三角形。

路径.png

2.弧线路径
public void arcTo(@NonNull RectF oval, float startAngle, float sweepAngle,
                      boolean forceMoveTo)

这个方法和 Canvas.drawArc() 比起来,少了一个参数 useCenter,而多了一个参数 forceMoveTo,forceMoveTo是否连线到弧形起点。

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);

        RectF rect = new RectF(200, 200, 300, 300);
        Path path = new Path();

        path.moveTo(100, 100);
        path.lineTo(150, 150);
        path.arcTo(rect, -90, 90, true);
        canvas.drawPath(path, paint);
forceMoveTo为true.png
path.arcTo(rect, -90, 90, false);
forceMoveTo为false.png.png
3.addXXX系列方法

添加圆形

public void addCircle(float x, float y, float radius, @NonNull Direction dir)

Direction是指路径方向CW顺时针,CCW逆时针。无论是顺时针逆时针,仅仅是方向不同,这里后边会介绍,其他addxxx方法也类似。
添加椭圆

public void addOval(@NonNull RectF oval, @NonNull Direction dir)

添加矩形

public void addRect(@NonNull RectF rect, @NonNull Direction dir)

添加圆角矩形

public void addRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Direction dir)
4.填充方式
public void setFillType(@NonNull FillType ft)
  • WINDING (默认值)显示方向相交的内容
  • EVEN_ODD 相交的地方填充
  • INVERSE_WINDING
  • INVERSE_EVEN_ODD
        path.setFillType(Path.FillType.WINDING);
        path.addCircle(200, 200, 100, Path.Direction.CW);
        path.addCircle(290, 200, 100, Path.Direction.CW);
        canvas.drawPath(path, paint);
WINDING相同方向.jpg
        path.setFillType(Path.FillType.WINDING);
        path.addCircle(200, 200, 100, Path.Direction.CW);
        path.addCircle(290, 200, 100, Path.Direction.CCW);
        canvas.drawPath(path, paint);

WINDING不同同方向.png

WINDING方式如果两个图形的path方向相同,即方向不想交,则可以全部显示,若方向不同,就产生了相交区域。
EVEN_ODD与WINDING不同同方向效果一致。
INVERSE_WINDING与INVERSE_EVEN_ODD与刚刚介绍的两个相反。

5.重置路径

当我们需要绘制一条全新的路径的时候,可以重置路径的对象,这样我们就可以不需要重新定义路径的对象了。

void reset()
void rewind()

reset会保留当前的数据结构,会清除FillType,rewind会清除数据空间,但不会清除FillType。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容

  • HenCoder 原文 关键点 自定义绘制方法的重写,其中最常用的是onDraw 绘制的关键是Canvas的使用C...
    李小神不偷懒阅读 510评论 4 1
  • 系列文章之 Android中自定义View(一)系列文章之 Android中自定义View(二)系列文章之 And...
    YoungerDev阅读 4,396评论 3 11
  • 记录下自己学习自定义View的过程。共勉 首先,在我们创建的自定义View中 重写onDraw()方法。如下 @O...
    万有引力丶阅读 948评论 0 1
  • 自定义绘制概述 方法:重写绘制方法(最常用:onDraw()) 绘制的关键:CanvasCanvas的绘制类方法:...
    NewSalton阅读 451评论 0 0
  • 最近在跟扔物线大婶学自定义控件,其实许多东西之前都用过的,不过时间长就忘了,然后这次就系统的复习一下,顺便记录下来...
    jeff_sun阅读 476评论 1 3