天天看點

android UI之Path

     Path作為UI繪制的重要的一個類,在官方文檔上對于的介紹如下:

    Path封裝了由直線段,二次曲線和三次曲線組成的複合幾何路徑,它可以用canvas.drawPath()進行繪制,填充,描畫,或者可以用于剪切或者繪制路徑上的文字。

   下面關于它常見的API進行一個簡單的記錄:

方法 講解
lineTo(float x, float y) 繪制直線,連接配接上一點和目前點
moveTo(float x, float y) 移動畫筆,将畫筆的開始位置移動到(x,y)
arcTo(RectF oval, float startAngle, float sweepAngle) 繪制圓弧,oval繪制圓弧的矩形局域,startAngle其實角度,sweepAngle旋轉角度
quadTo(float x1, float y1, float x2, float y2) 繪制二階貝塞爾曲線,目前path位置為起點,(x1,y1)為控制點(x2,y2)終點
cubicTo(float x1, float y1, float x2, float y2,float x3, float y3) 繪制三階貝塞爾曲線   目前path位置為起點,(x1,y1)為控制點1,(x2,y2)控制點2,(x3,y3)終點

add方法

方法 講解
addArc(RectF oval, float startAngle, float sweepAngle) 添加圓弧到目前path,oval添加區域,startAngle開始角度,sweepAngle圓弧旋轉的角度
addCircle(float x, float y, float radius, Direction dir) 添加圓到目前path,(x,y)圓心的坐标,radius半徑,dir線的閉合方向(CW順時針方向 ,CCW逆時針方向)
addOval(RectF oval, Direction dir) 添加橢圓到目前path,oval繪制區域,dir:閉合方向
addRect(RectF rect, Direction dir) 添加矩形到目前path
addRoundRect(RectF rect, float rx, float ry, Direction dir) 添加圓角矩形,rect區域,rx橫軸半徑,ry縱軸半徑,dir閉合方向,該方法的圓角是統一大小的
addRoundRect(RectF rect, float[] radii, Direction dir) 添加圓角矩形(非統一),rect矩形區域,radii四個圓角矩形分别對應的橫軸半徑和縱軸半徑,一個8個,dir閉合反向
addPath(Path src) 添加Path,添加path到目前path
addPath(Path src, float dx, float dy) 添加平移後的path到目前path,src需要平移的path,(dx,dy)平移後的x,y坐标
addPath(Path src, Matrix matrix) 添加經過矩陣變幻後的path

Fill type -Path的填充模式

方法 講解
setFillType(FillType ft)

設定填充模式,ft取值共有:

EVEN_ODD:取Path所在并不相交區域

INVERSE_EVEN_ODD :取Path未占或相交區域

WINDING:取Path所有區域,預設模式

INVERSE_WINDING:取Path所有未占區域

getFillType() 擷取目前path的填充模式
isInverseFillType() 判斷目前填充模式是否是反向規則,即(INVERSE_XX)
toggleInverseFillType() 目前填充模式與其反向規則模式進行反向取

其他零散重要的方法

方法 講解
close() 封閉目前path,連接配接起點和終點
reset() 清空path,相當于new Path
rewind() 清空path上面的直線和曲線,保留資料結構
set(Path src) 用名為src的path替換目前path
op(Path path, Op op)

目前path與名為path的路徑進行布爾運算:

DIFFERENCE:差集

REVERSE_DIFFERENCE:反向差集,

INTERSECT:交集,

UNION:并集

XOR:異或

offset(float dx, float dy) 平移path,dx-x軸上的移動距離,dy-Y軸上的移動距離
offset(float dx, float dy, Path dst) 平移dstPath
transform(Matrix matrix) 對目前path進行矩陣變幻
setLastPoint(float dx, float dy) 設定目前path的終點
isEmpty() 判斷目前path是否是空
isConvex() 判斷目前path是否為凸多邊形
isRect(RectF rect) 如果目前path是矩形,那麼儲存放入rect中

以上的是Path的建立方法,在日常的開發工作中我們可以根據自己需要調用合适API,在上篇文章androidUI之貝塞爾曲線中我們主要簡介了貝塞爾曲線的原理以及算法,在這裡我們主要以Path繪制貝塞爾曲線為例子進行API的簡單實用。

  Path繪制二階貝塞爾曲線:quadTo()

private void init() {
        mPath = new Path();
        xC1 = 300;
        yC1 = 60;

        xEnd = 500;
        yEnd = 300;

        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.RED);


        pPaint = new Paint();
        pPaint.setStyle(Paint.Style.FILL);
        pPaint.setColor(Color.BLACK);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPath.moveTo(100, 100);
        mPath.quadTo(xC1, yC1, xEnd, yEnd);//二階貝塞爾曲線
        canvas.drawPath(mPath, mPaint);

        canvas.drawCircle(100, 100, 10, pPaint);
        canvas.drawCircle(xEnd, yEnd, 10, pPaint);
        //控制點
        pPaint.setColor(Color.GREEN);
        canvas.drawCircle(xC1, yC1, 10, pPaint);

    }
           
android UI之Path

三階貝塞爾曲線:cublicTo

private void init() {
        mPath = new Path();
        xStart = 100;
        yStart = 100;

        xC1 = 300;
        yC1 = 60;

        xC2 = 500;
        yC2 = 500;

        xEnd = 800;
        yEnd = 300;

        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.RED);


        pPaint = new Paint();
        pPaint.setStyle(Paint.Style.FILL);
        pPaint.setColor(Color.BLACK);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPath.moveTo(xStart, yStart);
        mPath.cubicTo(xC1, yC1, xC2, yC2, xEnd, yEnd);//三階貝塞爾曲線
        canvas.drawPath(mPath, mPaint);

        //控制點
        canvas.drawCircle(xC1, yC1, 10, pPaint);
        canvas.drawCircle(xC2, yC2, 10, pPaint);
        //起始點
        pPaint.setColor(Color.GREEN);
        canvas.drawCircle(xStart, yStart, 10, pPaint);
        canvas.drawCircle(xEnd, yEnd, 10, pPaint);

    }
           
android UI之Path

多階:利用Path+德卡斯特利奧算法:

package com.barry.lsn_7_path_bezier.hy;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


/**
 * @author huanghaha
 * @desc 多階貝塞爾曲線
 */
public class OtherBesselView extends View {

    private Path mPath;
    private Paint mPaint;//點線之間的畫筆
    private Paint pPaint;//貝塞爾曲線的畫筆
    private float xStart, yStart;
    private float xEnd, yEnd;
    private List<PointF> cPoints = new ArrayList<>();

    public OtherBesselView(Context context) {
        super(context);
        init();
    }

    public OtherBesselView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

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

    public OtherBesselView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }


    private void init() {
        mPath = new Path();

        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.RED);

        pPaint = new Paint();
        pPaint.setStyle(Paint.Style.STROKE);
        pPaint.setStrokeWidth(5);
        pPaint.setColor(Color.GREEN);


        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            int cX = random.nextInt(600) + 100;
            int cY = random.nextInt(600) + 100;
            PointF p = new PointF(cX, cY);
            cPoints.add(p);
        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //1.畫出控制點以及他們之間的線段-僅僅為了對照
        for (int i = 0; i < cPoints.size(); i++) {
            PointF pointF = cPoints.get(i);
            if (i > 0) {
                canvas.drawLine(cPoints.get(i - 1).x, cPoints.get(i - 1).y, pointF.x, pointF.y, mPaint);
            }
            canvas.drawCircle(pointF.x, pointF.y, 10, mPaint);
        }

        //2.根據德卡斯特利奧算法畫出貝塞爾曲線
        buildBezierPoints(canvas);


    }

    /**
     * 繪制貝塞爾曲線
     */
    private void buildBezierPoints(Canvas canvas) {
        int order = cPoints.size() - 1; //目前貝塞爾曲線的階數
        float delta = 1.0f / 100;// 繪制的密度,(如果密度太小可能不能繪制完所有的曲線)
        for (float t = 0.0f; t <= 1.0f; t += delta) {
            PointF pointF = new PointF(deCusterLeoX(order, 0, t), deCusterLeoY(order, 0, t));//貝塞爾曲線上的點

            //使用path連接配接點
            if (t == 0.0f) {//移動畫筆到第一個點
                mPath.moveTo(pointF.x, pointF.y);

            } else {//連接配接點
                mPath.lineTo(pointF.x, pointF.y);
            }


        }
        canvas.drawPath(mPath, pPaint);

    }

    /**
     * 利用遞歸思想求出在t-比例不斷變動的情況先貝塞爾曲線上的點
     * 由于遞歸的計算有壓棧的特點,是以這兒的j進入的時候傳入的0,為友善了解可以打開注釋
     *
     * @param order 階數
     * @param j     目前控制點
     * @param t     比例
     * @return
     */
    private float deCusterLeoX(int order, int j, float t) {
//        Log.e("msg", "開始計算的條件" + " order=" + order + "  j=" + j + "  t=" + t);
        float reuslt = 0.0f;
        if (order == 1) {//一階段
            reuslt = (1 - t) * cPoints.get(j).x + t * cPoints.get(j + 1).x;
        } else {

            float val1 = (1 - t) * deCusterLeoX(order - 1, j, t);
            float val2 = t * deCusterLeoX(order - 1, j + 1, t);

            reuslt = val1 + val2;

        }
//        Log.e("msg", "開始計算的結果" + reuslt);
        return reuslt;

    }

    private float deCusterLeoY(int order, int j, float t) {
        if (order == 1) {//一階段
            return (1 - t) * cPoints.get(j).y + t * cPoints.get(j + 1).y;
        } else {


            return (1 - t) * deCusterLeoY(order - 1, j, t) + t * deCusterLeoY(order - 1, j + 1, t);

        }

    }
}
           
android UI之Path

以上便是path關于貝塞爾曲線的簡單應用。