LayoutParams

每个 ViewGroup (例如 LinearLayoutRelativeLayoutCoordinatorLayout 等)都需要存储有关其子项属性的信息。关于他们的孩子在这里铺设的方式 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_widthlayout_heightlayout_gravity 将存储在 LinearLayout.LayoutParams 对象中并由 LinearLayout 使用
  • gravitytexttextColor 将由 TextView 本身使用

对于 ImageView

  • layout_widthlayout_heightlayout_weight 将存储在 LinearLayout.LayoutParams 对象中并由 LinearLayout 使用
  • ImageViewscaleTypesrc 将由 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