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