1. 程式人生 > >自定義View-Rect和RectF

自定義View-Rect和RectF

Rect 類定義了一個矩形結構,同樣實現了 Parcelable 序列化介面。Rect 類定義了 left、top、right、bottom 四個成員變數,我們需要正確理解這 4 個成員變數的作用:
left:矩形左邊線條離 y 軸的距離
top:矩形上面線條離 x 軸的距離
right:矩形右邊線條離 y 軸的距離
bottom:矩形底部線條離 x 軸的距離

矩形是一種非常常見的圖形結構,並且能衍生出更多的圖形,如橢圓、扇形、弧線等等;矩形還能進行各種圖形運算,如交集、並集等等,所以,與之對應的 Rect 類功能也更加複雜。有人會疑惑為什麼不直接指定左上角的座標、寬度和高度來確定一個矩形,因為指定 top、left、right 和 bottom 更符合座標系的數學邏輯,也能更好的支援矩形的計算。

Rect 的主要功能有:
1)初始化:主要有兩種初始化的方法:一是直接指定 left、top、right、bottom 等 4 個成員變數的值,二是從另一個 Rect 物件中複製。下面是 Rect 的三個構造方法:
  Rect()
  Rect(int left,int top,int right,int bottom)
  Rect(Rect r)

2)增值計算:根據 left、top、right、bottom 等 4 個成員變數計算矩形的寬度、高度或中心點的座標,主要的方法定義如下:
 public final boolean isEmpty(){
       return left>=right ||top>= bottom;
 }

判斷 Rect 是否為空,也就是矩形區域面積是否為 0 或者為無效矩形。


 public final int width(){
      return right - left;
 }

返回矩形的寬度。


 public final int height(){
     return bottom - top;
 }

返回矩形的高度。

public final int centerX(){
     return (left + right) >> 1;
}
計算矩形中心點的 x 座標,右移一位相當於除以 2,移位運算比普通的除法運算效率

更高。


public final int centerY(){
    return (top + bottom) >> 1;
}

計算矩形中心點的 y 座標。


public final float exactCenterX(){
   return (left + right) * 0.5f;
}

計算矩形中心點的 x 座標,返回 float 型別,結果更精確。


 public final float exactCenterY(){
   return (top + bottom) * 0.5f;
 }

計算矩形中心點的 y 座標,返回 float 型別,結果更精確。


3)改變矩形的位置或大小,通過修改 left、top、right 和 bottom 等 4 個成員變數的值,獲取矩形位置平移、放大、縮小等結果。
public void setEmpty(){
  left = right = top = bottom = 0;
}

將矩形的 left、top、right 和 bottom 置 0。


 public void set(int left,int top,int right,int bottom){
  this.left = left;
  this.top = top;
  this.right = right;
  this.bottom = bottom;
}

給 left、top、right 和 bottom 重新賦值。


 public void set(Rect src){
  this.left = src.left;
  this.top = src.top;
  this.right = src.right;
  this.bottom = src.bottom;
}


矩形的 left、top、right 和 bottom 來自於另一個矩形 src。
public void offset(int dx,int dy){
  left += dx;
  top += dy;
  right += dx;
  bottom += dy;
}
矩形的 left 和 right 同時移動相同的距離 dx,矩形的 top 和 bottom 同時移動相同的距

離 dy,實際上就是將矩形移動(dx、dy)距離,正負決定移動的方向。


public void offsetTo(int newLeft,int newTop){
  right += newLeft - left;
  bottom += newTop - top;
  left = newLeft;
  top = newTop;
}

offsetTo()方法也是移位,和 offset()不同的是前者是絕對定位,後者是相對定位。


public void inset(int dx,int dy){
  left += dx;
  top += dy;
  right -= dx;
  bottom -= dy;
}
實現了矩形的縮放功能,縮放中心點就是矩形的中心點,要注意的是 dx、dy 為正數時
表示縮小,負數表示放大。
4)包含測試:支援一個點是否位於矩形內和一個矩形是否位於另一個矩形內。
public boolean contains(int x,int y){
  return left < right && top < bottom
              && x >= left && x < right && y >= top && y < bottom;
}

判斷點(x,y)是否位於矩形內。


public boolean contains(int left,int top,int right,int bottom){
  return this.left < this.right && this.top < this.bottom
             && this.left <= left && this.top <= top
             && this.right >= right && this.bottom >= bottom;
}

判斷傳遞過來的矩形是否位於矩形內。


public boolean contains(Rect r){
 return this.left < this.right && this.top < this.bottom
            && left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom;
}
判斷傳遞過來的矩形是否位於矩形內。


矩形的交集與並集運算:交集是指兩個矩形相交的公共部分,並集是指兩個矩形所佔
有最大面積區域。
主要的方法如下:
 public boolean intersect(int left,int top,int right,int bottom){
            if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) {
                if (this.left < left) this.left = left;
                if (this.top < top) this.top = top;
                if (this.right > right) this.right = right;
                if (this.bottom > bottom) this.bottom = bottom;
                return true;
            }
            return false;
 }
傳入 Rect 的 left、top、right、bottom,並將構建的 Rect 物件與當前 Rect 物件做交集運算,結果儲存在當前 Rect 物件中。

 public boolean intersect(Rect r){
            return intersect(r.left, r.top, r.right, r.bottom);
 }

傳入新的 Rect 物件,並將該物件與當前 Rect 物件做交集運算,結果儲存在當前 Rect物件中。比如有下面的程式碼段:

 Rect rect1 = new Rect(0, 0, 400, 400);
 Rect rect2 = new Rect(200, 200, 600, 600);
 rect1.intersect(rect2);
此時,rect1 的 left、top、right、bottom 屬性被改變了,分別為 200、200、400、400,
public void union(int left,int top,int right,int bottom){
            if ((left < right) && (top < bottom)) {
                if ((this.left < this.right) && (this.top < this.bottom)) {
                    if (this.left > left) this.left = left;
                    if (this.top > top) this.top = top;
                    if (this.right < right) this.right = right;
                    if (this.bottom < bottom) this.bottom = bottom;
                } else {
                    this.left = left;
                    this.top = top;
                    this.right = right;
                    this.bottom = bottom;
                }
            }
}
public void union(Rect r){
            union(r.left, r.top, r.right, r.bottom);
}
union()方法是計算兩個矩形的並集,傳入一個新的 Rect,與當前 Rect 進行並集運算,並將結果儲存在當前 Rect 物件中。比如有下面的程式碼段:
 Rect rect1 = new Rect(0, 0, 400, 400);
 Rect rect2 = new Rect(200, 200, 600, 600);
 rect1.union(rect2);
執行後與交集一樣,最終的結果儲存在 rect1 物件中, rect1 的 left、top、right、bottom屬性值分別為:0,0,600,600,也就是說,並集取的是四個方向的最大值。與 Rect 類類似的還有 RectF 類,RectF 類的程式碼實現與 Rect 如出一轍,主要的不同是 Rect的 left、top、right、bottom 四個成員變數為 int 型別,而 RectF 為 float 型別。在開發中,常常會出現 Rect 與 RectF 相互轉換的情況,Rect 類中沒有定義與 RectF 相關的任何資訊,但在 RectF 類中,則定義了二者相互轉換的方法。RectF 轉換成 Rect。RectF 定義了兩個名為 round 和 roundOut 的方法,round()方法將 RectF類的型別為 float 的 left、top、right、bottom 屬性以四捨五入的方式轉換成 int 再通過 Rect 型別的引數傳回,roundOut()方法雖然和 round()差不多,但在某些情況下返回的矩形區域要大些。


如果還有疑問,扒開原始碼探個究竟,我們發現,roundOut()方法中獲取 left 和 top 時呼叫了 FloatMath.floor()方法,該方法返回小於引數的最大值,如 FloatMath.floor(3.5)返回 3;而獲取 right 和 bottom 呼叫了 FloatMath.ceil()方法,該方法返回大於引數的最小值,如
FloatMath.ceil(5.2)返回 6。
 public void round(Rect dst){
            dst.set(FastMath.round(left), FastMath.round(top),
                    FastMath.round(right), FastMath.round(bottom));
 }

public void roundOut(Rect dst){
        dst.set((int) FloatMath.floor(left), (int) FloatMath.floor(top),
                (int) FloatMath.ceil(right), (int) FloatMath.ceil(bottom));
}

Rect 轉換成 RectF 就相對簡單了,例項化 RectF 時,構造方法支援傳遞 Rect 物件作為引數:
public RectF(Rect r) {
       if (r == null) {
            left = top = right = bottom = 0.0f;
       } else {
            left = r.left;
            top = r.top;
            right = r.right;
            bottom = r.bottom;
       }
}