LayoutParams

每個 ViewGroup (例如 LinearLayout , RelativeLayout , CoordinatorLayout 等)都需要儲存有關其子項屬性的資訊。關於他們的孩子在這裡鋪設的方式 5。此資訊儲存在包裝類 ViewGroup.LayoutParams 的物件中。

要包含特定佈局型別的引數,ViewGroups 使用 ViewGroup.LayoutParams 類的子類。

例如

大多數 ViewGroups 都重新使用了為孩子設定 margins 的能力,因此他們不直接將 ViewGroup.LayoutParams 子類化,但是他們將 ViewGroup.MarginLayoutParams 子類化 (它本身是 ViewGroup.LayoutParams 的子類)。

LayoutParams in xml

LayoutParams 物件是基於膨脹的佈局 xml 檔案建立的。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:layout_gravity="right"
        android:gravity="bottom"
        android:text="Example text"
        android:textColor="@android:color/holo_green_dark"/>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@android:color/holo_green_dark"
        android:scaleType="centerInside"
        android:src="@drawable/example"/>

</LinearLayout>

所有以 layout_ 開頭的引數都指定了封閉佈局應該如何工作。當佈局膨脹時,這些引數被包裹在一個合適的 LayoutParams 物件中,Layout 稍後將使用該物件將特定的 View 正確定位在 ViewGroup 內。View 的其他屬性直接與 View 相關,並由 View 本身處理。

對於 TextView:

  • layout_width,layout_height 和 layout_gravity 將儲存在 LinearLayout.LayoutParams 物件中並由 LinearLayout 使用
  • gravity,text 和 textColor 將由 TextView 本身使用

對於 ImageView:

  • layout_width,layout_height 和 layout_weight 將儲存在 LinearLayout.LayoutParams 物件中並由 LinearLayout 使用
  • ImageView,scaleType 和 src 將由 ImageView 本身使用

獲取 LayoutParams 物件

getLayoutParams 是一個 View's 方法,允許檢索當前的 LayoutParams 物件。

因為 LayoutParams 物件與封閉的 ViewGroup 直接相關,所以只有當 View 附加到 ViewGroup 時,此方法才會返回非空值。你需要記住,此物件可能始終不存在。特別是你不應該依賴於 View's 建構函式。

public class ExampleView extends View {
    
    public ExampleView(Context context) {
        super(context);
        setupView(context);
    }

    public ExampleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupView(context);
    }

    public ExampleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setupView(context);
    }

    private void setupView(Context context) {
        if (getLayoutParams().height == 50){  // DO NOT DO THIS!
                                              // This might produce NullPointerException
            doSomething();
        }
    }
    
    //...
}

如果你想依賴 LayoutParams 物件,你應該使用 onAttachedToWindow 方法代替。

public class ExampleView extends View {

    public ExampleView(Context context) {
        super(context);
    }

    public ExampleView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ExampleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (getLayoutParams().height == 50) { // getLayoutParams() will NOT return null here
            doSomething();
        }
    }

    //...
}

轉換 LayoutParams 物件

你可能需要使用特定於 ViewGroup 的功能(例如,你可能希望以程式設計方式更改 RelativeLayout 的規則)。為此,你需要知道如何正確地投射 ViewGroup.LayoutParams 物件。

當為一個孩子獲得一個 LayoutParams 物件時,這可能有點令人困惑,這實際上是另一個 ViewGroup。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/outer_layout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <FrameLayout
        android:id="@+id/inner_layout"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="right"/>

</LinearLayout>

重要提示: LayoutParams 物件的型別與 ENCLOSING ViewGroup 的型別直接相關。

***** 轉換錯誤* :

FrameLayout innerLayout = (FrameLayout)findViewById(R.id.inner_layout);
FrameLayout.LayoutParams par = (FrameLayout.LayoutParams) innerLayout.getLayoutParams();
                                          // INCORRECT! This will produce ClassCastException

正確轉換 :

FrameLayout innerLayout = (FrameLayout)findViewById(R.id.inner_layout);
LinearLayout.LayoutParams par = (LinearLayout.LayoutParams) innerLayout.getLayoutParams();
                                         // CORRECT! the enclosing layout is a LinearLayout