二、Android绘制知识总结(路径篇)

Path即路径,可以将其想象成一条或多条线段,它们可能是直的,也可能是弯曲的。一般来说只有一条,即连贯的,除非以下两种情况:

1、调用addXXX方法,直接添加固定形状的路径。
2、调用moveTo函数改变绘制起始位置。

1、Path的方向

Path.Direction,用于将封闭形状(例如矩形,椭圆形)添加到Path时,计算其内部像素点是被填充,还是被镂空
Path.Direction.CW表示顺时针。
Path.Direction.CCW表示逆时针。

注意:计算规则根据FillType不同而不同


2、Path填充模式

setFillType(FillType fillType)

FileType有以下取值:WINDING(默认值)EVEN_ODDINVERSE_WINDINGINVERSE_EVEN_ODD

1、WINDING(默认值)

这里画了6个图形,1和2是关于y=300对称,方向相反,其余的都是以(300,300)为中心,他们的边界已用黑线在图中标记。

image.png

WINDING判断一个像素点是被填充,还是被镂空的规则如下:

1、从该点引出一条假想线到外层区域,如图中红线。
2、计算从该点出发所穿过的边界,如果遇到顺时针,则+1,逆时针,则-1。
3、计算的结果,如果为0,则表示该点被镂空,否则该点被填充颜色。

2、EVEN_ODD
在原先代码中,设置FileType为EVEN_ODD

image.png

EVEN_ODD判断一个像素点是被填充,还是被镂空的规则如下:

1、从该点引出一条假想线到外层区域。
2、计算从该点出发所穿过的边界数量。
3、如果穿过数量为奇数,表示被填充,偶数则被镂空。

3、INVERSE_WINDING
在原先代码中,设置FileType为INVERSE_WINDING

image.png

INVERSE_WINDING判断一个像素点是被填充,还是被镂空的规则如下:

1、从该点引出一条假想线到外层区域。
2、计算从该点出发所穿过的边界,如果遇到顺时针,则+1,逆时针,则-1。
3、计算的结果,如果为0,则表示该点被填充颜色,否则该点被镂空。

4、INVERSE_EVEN_ODD
在原先代码中,设置FileType为INVERSE_EVEN_ODD

image.png

INVERSE_EVEN_ODD判断一个像素点是被填充,还是被镂空的规则如下:

1、从该点引出一条假想线到外层区域。
2、计算从该点出发所穿过的边界数量。
3、如果穿过数量为偶数,表示被填充,奇数则被镂空。


3、重置路径

rewind()
清除FillType及所有的直线、曲线、点的数据,但会保留数据结构,这样可以实现快速重用,提高一定的性能。
reset()
新建一个路径对象,它的所有数据空间都会被回收并重新分配,但不会清除FillType。
对比:rewind()函数不会清除内存,但会清除FillType;而reset()函数会清除内存,但不会清除FillType


4、PathMeasure

PathMeasure是Path的一个辅助类,虽然它跟绘制无关,但它有以下2个主要作用:
1、获取Path中某个子线段。
2、获取Path中某个位置的坐标和切线角度。
这意味着它配合属性动画,能够实现各种Path特效。

PathMeasure需要关联一个创建好的path,以及指定forceClose,表示Path是否需要闭合,它会影响path的测量结果。

1、获取路径的长度
float getLength()

2、分割路径
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
startWithMoveTo为true时,表示将dst中的终点,设置为本次分割结果的起始点。

  • 截取的结果跟Path的方向有关。
  • 会将截取的Path片段添加到dst中,而不是替换dst中的内容。

3、获取路径上某点的坐标和切线
3.1、方式1:
boolean getPosTan(float distance, float pos[], float tan[])
pos[0]、pos[1]分别表示当前distance位置的x,y坐标
tan表示该点的切线与单位圆相交的坐标点,可通过以下公式求出角度。
double degress = Math.atan2(tan[0], tan[1]) * 180.0 / Math.PI;
3.2、方式2:
boolean getMatrix(float distance, Matrix matrix, int flags)
这个函数用于得到路径上某一长度的位置以及该位置的正切值的矩阵。
flags可指定以下参数,并且可通过|来同时传入:
POSITION_MATRIX_FLAG:表示获取位置信息
TANGENT_MATRIX_FLAG:表示获取切线信息

4、跳转到下一条线段
boolean nextContour()
由于Path可以包含多条线段,但getLength()、getSegment()还是其他函数,都只针对第一条线段进行计算,而nextContour()就是用于跳转到下一条曲线的函数。


5、贝塞尔曲线

二阶贝塞尔曲线
B(t) = (1 - t)^2 * P_0 + 2t * (1 - t) * P_1 + t^2 * P_2, t ∈ [0,1]
void quadTo(float x1, float y1, float x2, float y2)
void rQuadTo(float dx1, float dy1, float dx2, float dy2)

三阶贝塞尔曲线
B(t) = P_0 * (1-t)^3 + 3 * P_1 * t * (1-t)^2 + 3 * P_2 * t^2 * (1-t) + P_3 * t^3, t ∈ [0,1]
void cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
void rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

注意:函数前带r的贝塞尔函数,表示坐标位置都是相对位置,参照于Path中前一个线段的终点。

下面解释几种贝塞尔曲线在真实场景下的实际应用。

5.1、平滑线条

利用贝塞尔曲线,可以改善书写时的边缘锯齿。

public class DrawBezierView extends View {
    private Path mPath;
    private Paint mPaint;
    private float mPreX;
    private float mPreY;

    public DrawBezierView(Context context) {
        super(context);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPath = new Path();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPreX = event.getX();
                mPreY = event.getY();
                mPath.moveTo(mPreX, mPreY);
                break;
            case MotionEvent.ACTION_MOVE:
                float endX = (event.getX() + mPreX) / 2;
                float endY = (event.getY() + mPreY) / 2;
                mPath.quadTo(mPreX, mPreY, endX, endY);
                mPreX = event.getX();
                mPreY = event.getY();
                invalidate();
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(mPath, mPaint);
    }
}

5.2、波浪

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容