Graphics2D API:Paint类、Canvas类

现实生活中作画,有两样东西必不可少:笔、纸,Android中图形绘制类似于此.
在Android中:
Paint类就是我们的“画笔”,为绘图定义各种参数:颜色、文本样式、图形样式等等.
Canvas类就是我们的“画布”,Canvas类中提供了若干方法用于绘制各种图案:点、线、矩形、圆等等.
掌握Graphics绘图是自定义组件的基础,Android给了我们一支笔(Paint)和一张纸(Canvas),画出什么样的图形取决于我们的想象力以及对Graphics绘图的掌握程度.

一、Paint类

Paint类就是画笔,用来设置绘图时的参数,比如:笔的颜色、笔的粗细等等.
使用方法很简单,首先定义一个画笔对象:

Paint mPaint = new Paint();

然后就可以调用Paint以set开头的相关方法为画笔设置各种参数了:

mPaint.set......

最后,在画布Canvas调用相关绘图方法的时候把画笔对象Paint传进去就可以了:

canvas.draw......(其它参数.....,mPaint);

这里先简单介绍画笔的几个方法,后面会详细介绍画笔Paint.

1、画笔颜色设置

画笔肯定有颜色了,Paint设置颜色的方法有下面3个,我们一般使用的是第1个

public void setColor(int color)  设置颜色值
public void setAlpha(int a)  设置透明度
public void setARGB(int a, int r, int g, int b)  指定透明度、红、绿、蓝定义一种颜色
2、画笔填充样式设置
public void setStyle(Style style)
设置画笔填充样式,可选值:
    Paint.Style.FILL  填充内部(默认值)
    Paint.Style.STROKE  仅描边
    Paint.Style.FILL_AND_STROKE 填充内部和描边

上图是调用Canvas画了一个正方形在Paint三种填充样式下的效果,FILL就是内部填充满画笔的颜色,STROKE就是图形是空心的,在外层描边,所以STROKE看着比FILL大一点,FILL_AND_STROKE就是两者的综合.

3、画笔宽度、抗锯齿功能
public void setStrokeWidth(float width)  设置画笔宽度

当画笔填充样式为STROKE 、FILL_AND_STROKE 的时候,我们可以设置画笔的宽度,画笔宽度越大,描的边就越“厚”.

public void setAntiAlias(boolean aa)
aa为true时开启抗锯齿功能

我们画一条直线,细看是直的,但是仔细看会发现直线上有很多锯齿,就像电锯一样,这个方法可以让绘图过程中的线条更加平滑.

4、文本样式设置

Canvas是可以绘制文字的,文字的颜色就是Paint设置的颜色,Paint还可以为文字设置下面这些样式:

public void setTextSize(float textSize)
设置文本字体大小,单位px

public void setTextAlign(Align align)
设置文本对齐方式,可选值:Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT
绘制文本时会有个起始点坐标,三个值分别代表:在起始坐标的左边绘制文本、以起始坐标为中心绘制文本、在起始坐标的右边绘制文本

public void setTextScaleX(float scaleX)
将文本沿X轴水平缩放,默认值为1,大于1时会沿X轴水平放大文本,小于1会沿X轴水平缩放文本

public void setTextSkewX(float skewX)
设置文本倾斜程度,范围:-1~1,正负表示倾斜的方向,默认0不倾斜

public void setUnderlineText(boolean underlineText)
给文本添加下划线,true添加下划线

public void setFakeBoldText(boolean fakeBoldText)
文本粗体设置,true表示设置为粗体

public void setStrikeThruText(boolean strikeThruText)
文本添加删除线,true表示添加删除线
5、copy画笔、重置画笔
public void set(Paint src)
为当前画笔设置一个画笔
在绘图时,有时我们会用到不止一种画笔,如果有个画笔的属性都设置好了,另外一个画笔也需要这些属性,就可以直接copy过来

public void reset() 
重置画笔,恢复初始状态

二、Canvas类

Canvas类提供了大量以draw...开头的方法用于绘图.
一般在绘图前,先创建Paint对象,定义绘制的颜色、样式等参数. 因为Paint对象可以重置(reset),所以除非有必要,Paint对象创建一个就可以了,然后再调用Canvas的绘图方法.

Canvas绘图方法主要有下面几种类型:
颜色

线
矩形

路径
文字
位图
Canvas操作

1、前置

在讲绘图方法前,我们先自定义一个简单的XView继承自View,不然光介绍API不是很好理解.

public class XView extends View {

    private Paint mPaint;

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

    public XView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();//初始化画笔
    }

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

        gogogo(canvas);
    }

    /**
     * 后面所有测试代码都在gogogo中
     */
    private void gogogo(Canvas canvas){
        ...
    }
}

然后在布局文件中使用:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.fgq.demo.XView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</FrameLayout>

自定义的View在布局文件中写法:完整包名+类名
2、绘制颜色
public void drawRGB(int r, int g, int b)
public void drawARGB(int a, int r, int g, int b)
public void drawColor(int color)

这里绘制的是整个画布的颜色

测试:

    private void gogogo(Canvas canvas){
        canvas.drawColor(Color.GRAY);
    }
3、绘制点

虽然是点,但并不表面它很小,点的大小取决于画笔宽度,也就是Paint.setStrokeWidth(...)参数的大小.
关于点,请看Point类、PointF类.

public void drawPoint(float x, float y, Paint paint)
在(x,y)处绘制一个点

public void drawPoints(float[] pts, Paint paint)
连续绘制多个点:pts是一个数组,从下标0开始,每2个数组元素确定一个点,数组长度必须是2的倍数

public void drawPoints(float[] pts, int offset, int count,Paint paint)
连续绘制多个点:pts是一个数组,从下标offset开始取数组元素,每2个数组元素确定一个点,一共取count个元素

测试:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(20f);
        mPaint.setColor(Color.BLUE);
        canvas.drawPoint(50, 50, mPaint);//点(50,50)

        mPaint.setStrokeWidth(30f);
        mPaint.setColor(Color.RED);
        canvas.drawPoints(new float[]{50, 100, 110, 100, 188, 100}, mPaint);//点(50,100)、(110,100)、(188,100)

        mPaint.setColor(Color.BLACK);
        canvas.drawPoints(new float[]{50, 150, 110, 150, 188, 150}, 2, 4, mPaint);//点(110,150)、(188,150)
4、绘制线段

两个点确定一条线段,所以,绘制一条线段时需要两个点的坐标,线段的粗细由画笔宽度决定

public void drawLine(float startX,float startY,float stopX,float stopY,Paint paint)
在(startX,startY)和(stopX,stopY)   两个点之间绘制一条线段   

public void drawLines(float[] pts, Paint paint)
绘制多条线段:pts是一个数组,从下标0开始,每4个数组元素确定一条线段,数组长度必须是4的倍数

public void drawLines(float[] pts, int offset, int count,Paint paint)
绘制多条线段:pts是一个数组,从下标offset开始取数组元素,每4个数组元素确定一条线段,一共取count个元素,数组长度必须是4的倍数

测试:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);//圆形笔触
        mPaint.setStrokeWidth(20f);
        mPaint.setColor(Color.BLUE);
        canvas.drawLine(50, 50, 500, 50, mPaint);//点(50,50)到(500,50)的线段

        mPaint.setColor(Color.RED);
        canvas.drawLines(new float[]{50, 50, 50, 500, 500, 50, 500, 500}, mPaint);//点(50,50)到(50,500)的线段、点(500,50)到(500,500)的线段

        mPaint.setColor(Color.BLACK);
        canvas.drawLines(new float[]{50, 50, 50, 500, 500, 500, 0, 0}, 2, 4, mPaint);//点(50,50)到(500,500)的线段
    }
5、绘制矩形

矩形分为:直角矩形、圆角矩形.(正方形也是矩形的一种)
关于矩形,请看Rect类、RectF类.
直角矩形:

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

public void drawRect(Rect r, Paint paint) 

public void drawRect(RectF rect, Paint paint)

这三个重载方法绘制矩形的本质是完全一样的,使用哪一个随意

测试:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(20f);
        mPaint.setColor(Color.BLUE);

        canvas.drawRect(50, 50, 200, 200, mPaint);

        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.RED);
        canvas.drawRect(new Rect(50,250,200,400),mPaint);

        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(Color.BLACK);
        canvas.drawRect(new RectF(50f,450f,200f,600f),mPaint);
    }

圆角矩形:

public void drawRoundRect(RectF rect, float rx, float ry, Paint paint)

public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint)

相对于直角矩形,圆角矩形多了两个参数:rx、ry
这两个参数用来指定4个角的弧度,分别是圆角处的水平半径、竖直半径
若rx=ry,则4个拐角是半径为rx的圆的1/4圆弧,
若rx≠ry,则4个拐角是长半轴、短半轴为rx、ry的椭圆的1/4圆弧.

这二个重载方法绘制矩形的本质是完全一样的,不同的是第二个需要在>=5.0(API 21)的版本使用

测试:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLUE);
        canvas.drawRoundRect(50, 50, 600, 600, 20, 40, mPaint);

        mPaint.setColor(Color.RED);
        canvas.drawRoundRect(new RectF(100, 100, 400, 400), 30, 30, mPaint);
    }
6、绘制圆

我把椭圆、圆、弧、扇形统一归类到“圆”中.
(1)椭圆
椭圆的大小由它的外切矩形决定.

public void drawOval(RectF oval, Paint paint)

public void drawOval(float left, float top, float right, float bottom, Paint paint)

这两个重载方法本质一样,都是通过定义椭圆的外切矩形来绘制椭圆
(第二个重载方法需要在>=5.0(API 21)的版本使用)

(2)圆
圆的大小由圆心、半径决定.

public void drawCircle(float cx, float cy, float radius, Paint paint)
圆心:(cx,cy)      半径:radius

(3)弧、扇形
弧和扇形相似,都是椭圆上一部分,而椭圆又是由它的外切矩形决定

public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)

public void drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter,Paint paint)

startAngle:开始角度
sweepAngle:弧线、扇形所占角度,正数顺时针,负数逆时针
useCenter:是否使用中心点,true表示扇形、false表示弧线
(第二个重载方法需要在>=5.0(API 21)的版本使用)

当矩形长、宽一样时,就是正方形,椭圆就变成圆,弧线、扇形就是圆上一部分了

测试:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLUE);
        RectF rectF = new RectF(50, 50, 600, 400);
        canvas.drawOval(rectF, mPaint);//椭圆

        mPaint.setColor(Color.RED);
        canvas.drawCircle(325, 225, 175, mPaint);//圆

        mPaint.setColor(Color.YELLOW);
        canvas.drawArc(rectF, 90, 90, false, mPaint);//弧

        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawArc(rectF, 180, 90, false, mPaint);//弧

        canvas.drawArc(rectF, 30, 30, true, mPaint);//扇形
        canvas.drawArc(rectF, -30, 30, true, mPaint);//扇形
    }
7、绘制文字

(1)从指定位置开始绘制文字:

public void drawText(String text, float x, float y, Paint paint)

public void drawText(char[] text, int index, int count, float x, float y,Paint paint)
            
public void drawText(String text, int start, int end, float x, float y,Paint paint)
            
public void drawText(CharSequence text, int start, int end, float x, float y,
            Paint paint)

文字内容:text
部分内容:index、count------从下标index开始取,一共取count个数组元素;start、end------从字符串索引start到end处的部分字符串

测试:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLUE);
        mPaint.setTextSize(40);//设置字体大小

        String text = "随风飘扬的Smile";

        canvas.drawText(text, 50, 50, mPaint);//绘制文字

        mPaint.setTextSkewX(-0.5f);//向左倾斜
        canvas.drawText(text, 50, 100, mPaint);
        mPaint.setTextSkewX(0.5f);//向右倾斜
        canvas.drawText(text, 50, 150, mPaint);

        mPaint.setUnderlineText(true);//下划线
        canvas.drawText(text.toCharArray(), 1, 2, 50, 200, mPaint);

        mPaint.setFakeBoldText(true);//粗体
        mPaint.setStrikeThruText(true);//删除线
        canvas.drawText(text, 2, text.length() - 1, 50, 250, mPaint);

        mPaint.setTextScaleX(1.5f);//水平放大文本
        canvas.drawText(text, 50, 300, mPaint);
    }

(2)沿着Path路径绘制文字:
关于Path,请看Path类基本操作.

public void drawTextOnPath(String text, Path path, float hOffset,
            float vOffset, Paint paint)

public void drawTextOnPath(char[] text, int index, int count,Path path,
            float hOffset, float vOffset, Paint paint)

hOffset:文字与路径起始点的水平偏移距离
vOffset:文字与路径竖直方向偏移量,>0往Path下方偏移,<0往Path上方偏移
同样支持截取部分内容:从index处取元素,一共取count个元素

测试:

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLUE);
        mPaint.setTextSize(30);

        String text = "随风飘扬的Smile";

        Path path = new Path();
        path.moveTo(50, 200);
        path.lineTo(666, 666);//线段
        //沿着Path绘制文字
        canvas.drawTextOnPath(text+"1", path, 0, 0, mPaint);
        canvas.drawTextOnPath(text+"2", path, 0, -30, mPaint);
        canvas.drawTextOnPath(text+"3", path, 300, 0, mPaint);
        canvas.drawTextOnPath(text+"4", path, 0, 30, mPaint);
        canvas.drawTextOnPath(text+"5", path, 300, 30, mPaint);

        Path path2 = new Path();
        path2.addCircle(300,300,200, Path.Direction.CCW);//逆向圆,文字沿着逆向
        canvas.drawTextOnPath(text, path2, 0, 0, mPaint);//沿着Path绘制文字

        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path, mPaint);//绘制Path---线段
        canvas.drawPath(path2, mPaint);//绘制Path---圆
    }

(3)指定文字位置:

public void drawPosText(String text, float[] pos,Paint paint)

public void drawPosText(char[] text, int index, int count,float[] pos,Paint paint)

text:文子内容
pos:每个文字的坐标位置,一个坐标需要2个参数,所以数组pos长度必须是2的倍数
index:第一个文字索引
count:一共绘制count个文字

测试

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLUE);
        mPaint.setAntiAlias(true); //使用抗锯齿功能
        mPaint.setTextSize(50);

        float[] textPos = new float[]{10, 50,
                20, 100,
                30, 150,
                40, 200,
                50, 250,
                60, 300,
        };
        canvas.drawPosText("随风飘扬的笑", textPos, mPaint);
    }
8、绘制位图
public void drawBitmap(Bitmap bitmap, float left, float top,Paint paint)
将bitmap绘制在画布上,同时指定位图相左上角位置(left,top),bitmap大小与原图一样,不进行缩放

public void drawBitmap(Bitmap bitmap, Rect src, Rect dst,Paint paint)
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst,Paint paint)
从bitmap中抠出大小为src的矩形区域绘制到画布dst矩形处,bitmap会缩放适应src区域,所以src与dst的大小与比例关系影响最终绘制效果
若src为null,就将原bitmap绘制到dst处,bitmap会缩放适应src区域

测试

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

推荐阅读更多精彩内容