自定義View之Path類

1. 簡介

  • 定義: 路徑,即無數個點連接起來的線
  • 作用:設置繪製的順序&區域

Path 只用於描述順序&區域,單使用Path無法產生效果

  • 應用場景:繪製複雜圖形

Path類封裝了由直線和曲線(2,3次貝塞爾曲線)構成的幾何路徑

2. 基礎

2.1 開放路徑與閉合路徑的區別

在這裏插入圖片描述

3.具體使用

3.1 對象創建

// 使用Path首先要new一個Path對象
// Path的起點默認爲座標爲(0,0)
Path path = new Path();
// 特別注意:建全局Path對象,在onDraw()按需修改;儘量不要在onDraw()方法裏new對象
// 原因:若View頻繁刷新,就會頻繁創建對象,拖慢刷新速度。

3.2 具體方法使用

因爲path類的方法都是聯合使用,所以下面將一組組方法進行介紹。

第一組 :設置路徑

採用moveTo()setLastPoint()lineTo()close()組合

// 設置當前點位置
// 後面的路徑會從該點開始畫
moveTo(float x, float y)// 當前點(上次操作結束的點)會連接該點
// 如果沒有進行過操作則默認點爲座標原點。
lineTo(float x, float y)// 閉合路徑,即將當前點和起點連在一起
// 注:如果連接了最後一個點和第一個點仍然無法形成封閉圖形,則close什麼也不做
close()
  • 可使用setLastPoint()設置當前位置(代替moveTo()
  • 二者區別:
類型 是否影響起點 是否影響之前操作
moveTo()
setLastPoint()

實例介紹:


        // 使用moveTo()
        // 起點默認是(0,0)
        //連接點(400,500)
        path.lineTo(400, 500);

        // 將當前點移動到(300, 300)
        path.moveTo(300, 300) ;

        //連接點(900, 800)
        path.lineTo(900, 800);
        
        //連接點(200,700)
        path.lineTo(200, 700);
        
        // 閉合路徑,即連接當前點和起點
        // 即連接(200,700)與起點2(300, 300)
        // 注:此時起點已經進行變換
        path.close();

        // 畫出路徑
        canvas.drawPath(path, mPaint1);

        // 使用setLastPoint()
        // 起點默認是(0,0)
        //連接點(400,500)
        path.lineTo(400, 500);

        // 將當前點移動到(300, 300)
        // 會影響之前的操作
        // 但不將此設置爲新起點
        path.setLastPoint(300, 300) ;

        //連接點(900,800)
        path.lineTo(900, 800);

        //連接點(200,700)
        path.lineTo(200, 700);

        // 閉合路徑,即連接當前點和起點
        // 即連接(200,700)與起點(0,0)
        // 注:起點一直沒變化
        path.close();

        // 畫出路徑
        canvas.drawPath(path, mPaint1);

在這裏插入圖片描述

關於重置路徑
  • 重置Path有兩個方法:reset()rewind()
  • 兩者區別在於:
類型 是否保留FillType設置 是否保留原有數據結構
Path.reset()
Path.rewind()
  1. FillType影響顯示效果;數據結構影響重建速度
  2. 一般選擇Path.reset()

第二組:添加路徑

採用addxxx()arcTo()組合

  • 作用: 在Path路徑中添加基本圖形
  • 具體使用
 
// 添加圓弧
// 方法1
public void addArc (RectF oval, float startAngle, float sweepAngle)

//  startAngle:確定角度的起始位置
//  sweepAngle : 確定掃過的角度
 
    // 方法2
    // 與上面方法唯一不同的是:如果圓弧的起點和上次最後一個座標點不相同,就連接兩個點
    public void arcTo (RectF oval, float startAngle, float sweepAngle)
   
   // 方法3
   // 參數forceMoveTo:是否將之前路徑的結束點設置爲圓弧起點
   // true:在新的起點畫圓弧,不連接最後一個點與圓弧起點,即與之前路徑沒有交集(同addArc())
  // false:在新的起點畫圓弧,但會連接之前路徑的結束點與圓弧起點,即與之前路徑有交集(同arcTo(3參數))
    public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
// 下面會詳細說明


  // 加入圓形路徑
  // 起點:x軸正方向的0度
  // 其中參數dir:指定繪製時是順時針還是逆時針:CW爲順時針,  CCW爲逆時針
  // 路徑起點變爲圓在X軸正方向最大的點
  addCircle(float x, float y, float radius, Path.Direction dir)   

   // 加入橢圓形路徑
  // 其中,參數oval作爲橢圓的外切矩形區域
  addOval(RectF oval, Path.Direction dir)    

  // 加入矩形路徑
  // 路徑起點變爲矩形的左上角頂點
  addRect(RectF rect, Path.Direction dir)     

  //加入圓角矩形路徑
  
  addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)      
 
//  注:添加圖形路徑後會改變路徑的起點

第三組:判斷路徑屬性

  • 採用isEmpty()isRect()isConvex()set()offset()組合
  • 具體使用
// 判斷path中是否包含內容
 public boolean isEmpty ()
// 例子:
Path path = new Path();
path.isEmpty();  //返回false

 path.lineTo(100,100); // 返回true


// 判斷path是否是一個矩形
// 如果是一個矩形的話,會將矩形的信息存放進參數rect中。
public boolean isRect (RectF rect)

// 實例
path.lineTo(0,400);
        path.lineTo(400,400);
        path.lineTo(400,0);
        path.lineTo(0,0);

        RectF rect = new RectF();
        boolean b = path.isRect(rect);  // b返回ture,
        // rect存放矩形參數,具體如下:
        // rect.left = 0
        // rect.top = 0
        // rect.right = 400
        // rect.bottom = 400



// 將新的路徑替代現有路徑
 public void set (Path src)

        // 實例
        // 設置一矩形路徑
        Path path = new Path();                     
        path.addRect(-200,-200,200,200, Path.Direction.CW);
      
        // 設置一圓形路徑
        Path src = new Path();                     
        src.addCircle(0,0,100, Path.Direction.CW);

        // 將圓形路徑代替矩形路徑
        path.set(src);      

        // 繪製圖形
        canvas.drawPath(path,mPaint);


// 平移路徑
// 與Canvas.translate ()平移畫布類似
                     

// 方法1
// 參數x,y:平移位置
public void offset (float dx, float dy)

// 方法2
// 參數dst:存儲平移後的路徑狀態,但不影響當前path
// 可通過dst參數繪製存儲的路徑
        public void offset (float dx, float dy, Path dst)



 // 爲了方便觀察,平移座標系
        canvas.translate(350, 500);

        // path中添加一個圓形(圓心在座標原點)
        path = new Path();
        path.addCircle(0, 0, 100, Path.Direction.CW);

        // 平移路徑並存儲平移後的狀態
        Path dst = new Path();
        path.offset(400, 0, dst);                     // 平移

        canvas.drawPath(path, mPaint1);               // 繪製path

        
        // 通過dst繪製平移後的圖形(紅色)
        mPaint1.setColor(Color.RED);      
        canvas.drawPath(dst,mPaint1);

第四組:設置路徑填充顏色

  • 在Android中,有四種填充模式,具體如下(均封裝在Path類中)
填充模式 介紹
EVEN_ODD 奇偶規則
INVERSE_EVEN_ODD 反奇偶規則
WINDING 非零環繞數規則
INVERSE_WINDING 反非零環繞數規則

圖形是存在方向的(畫圖=連接點的線=有連接順序)

  • 具體使用
// 設置填充規則
path.setFillType()
// 可填規則
// 1. EVEN_ODD:奇偶規則
// 2. INVERSE_EVEN_ODD:反奇偶規則
// 3. WINDING :非零環繞數規則
// 4. INVERSE_WINDING:反非零環繞數規則

// 理解奇偶規則和反奇偶規則:填充效果相反
// 舉例:對於一個矩形而言,使用奇偶規則會填充矩形內部,而使用反奇偶規則會填充矩形外部(下面會舉例說明)

// 獲取當前填充規則
path.getFillType()

// 判斷是否是反向(INVERSE)規則
path.isInverseFillType()

// 切換填充規則(即原有規則與反向規則之間相互切換)
path.toggleInverseFillType()

實例1:(奇偶規則)


 // 爲了方便觀察,平移座標系
        canvas.translate(350, 500);

        // 在Path中添加一個矩形
        path.addRect(-200, -200, 200, 200, Path.Direction.CW);

        // 設置Path填充模式爲 奇偶規則
        path.setFillType(Path.FillType.EVEN_ODD);

        // 反奇偶規則
        // path.setFillType(Path.FillType.INVERSE_EVEN_ODD);

        // 畫出路徑
        canvas.drawPath(path, mPaint1);

在這裏插入圖片描述
實例2:(非零環繞規則)

    // 爲了方便觀察,平移座標系
        canvas.translate(550, 550);
        // 在路徑中添加大正方形
        // 逆時針
        path.addRect(-400, -400, 400, 400, Path.Direction.CCW);

        // 在路徑中添加小正方形
        // 順時針
//        path.addRect(-200, -200, 200, 200, Path.Direction.CW);
//          設置爲逆時針
          path.addRect(-200, -200, 200, 200, Path.Direction.CCW);


        // 設置Path填充模式爲非零環繞規則
        path.setFillType(Path.FillType.WINDING);
        // 設置反非零環繞數規則
        // path.setFillType(Path.FillType.INVERSE_WINDING);

        // 繪製Path
        canvas.drawPath(path, mPaint1);               

在這裏插入圖片描述

第五組:布爾操作

  • 作用:兩個路徑Path之間的運算
  • 應用場景:用簡單的圖形通過特定規則合成相對複雜的圖形
  • 具體使用:
// 方法1
    boolean op (Path path, Path.Op op)
// 舉例
// 對 path1 和 path2 執行布爾運算,運算方式由第二個參數指定
// 運算結果存入到path1中。
    path1.op(path2, Path.Op.DIFFERENCE);


// 方法2
    boolean op (Path path1, Path path2, Path.Op op)
  // 舉例
    // 對 path1 和 path2 執行布爾運算,運算方式由第三個參數指定
    // 運算結果存入到path3中。
    path3.op(path1, path2, Path.Op.DIFFERENCE)

之間的運算方式(即Path.op參數)如下:

在這裏插入圖片描述
舉例:

   // 爲了方便觀察,平移座標系
        canvas.translate(550, 550);

        // 畫兩個圓
        // 圓1:圓心 = (0,0),半徑 = 100
        // 圓2:圓心 = (50,0),半徑 = 100
        path1.addCircle(0, 0, 100, Path.Direction.CW);
        path2.addCircle(50, 0,100, Path.Direction.CW);

        // 取兩個路徑的異或集
        path1.op(path2, Path.Op.XOR);
        // 畫出路徑
        canvas.drawPath(path1, mPaint1);

在這裏插入圖片描述

4.貝塞爾曲線

  • 定義:計算曲線的數學公式
  • 作用:計算並表示曲線

任何一條曲線都可以用貝賽爾曲線表示

  • 具體使用:貝塞爾曲線可通過1數據點和若干個控制點描述
  1. 數據點: 指路徑的起始點和終止點;
  2. 控制點:決定路徑的彎曲軌跡;
  3. n+1階貝塞爾=有n個控制點;
  4. 1階=一條直線,高階可以拆解爲多條低階曲線;

Canvas提供了畫二階 & 三階貝塞爾曲線的方法,下面是具體方法:


// 繪製二階貝塞爾曲線
//  (x1,y1)爲控制點,(x2,y2)爲終點
quadTo(float x1, float y1, float x2, float y2)
//  (x1,y1)爲控制點距離起點的偏移量,(x2,y2)爲終點距離起點的偏移量
rQuadTo(float x1, float y1, float x2, float y2)

// 繪製三階貝塞爾曲線
// (x1,y1),(x2,y2)爲控制點,(x3,y3)爲終點
cubicTo(float x1, float y1, float x2, float y2, float x3, float y3)
// (x1,y1),(x2,y2)爲控制點距離起點的偏移量,(x3,y3)爲終點距離起點的偏移量
rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

發佈了71 篇原創文章 · 獲贊 36 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章