Android中的非对称RelativeLayout行为

时间:2015-01-08 22:34:56

标签: android layout relativelayout

以下两个布局文件会产生不同的结果:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:gravity="center">
  <RelativeLayout
    android:layout_width="match_parent" 
    android:layout_height="wrap_content"
    android:gravity="center">
    <View
      android:id="@+id/box"
      android:background="#ff0000"
      android:layout_width="0dp"
      android:layout_height="30dp"
      android:layout_alignParentLeft="true"
      android:layout_toLeftOf="@+id/next_box" />
    <View
      android:id="@+id/next_box"
      android:background="#0000ff"
      android:layout_width="60dp"
      android:layout_alignParentRight="true"
      android:layout_height="30dp"
      />
  </RelativeLayout>
</LinearLayout>

结果: enter image description here

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:gravity="center">
  <RelativeLayout
    android:layout_width="match_parent" 
    android:layout_height="wrap_content"
    android:gravity="center">
    <View
      android:id="@+id/box"
      android:background="#ff0000"
      android:layout_width="0dp"
      android:layout_height="30dp"
      android:layout_alignParentLeft="true"
       />
    <View
      android:id="@+id/next_box"
      android:background="#0000ff"
      android:layout_width="60dp"
      android:layout_alignParentRight="true"
      android:layout_height="30dp"
      android:layout_toRightOf="@+id/box"
      />
  </RelativeLayout>
</LinearLayout>

结果: enter image description here

两种布局都试图描述相同的约束。也就是说,红色矩形应该接触父项的左边缘,蓝色矩形应该接触父项的右边缘,它们应该水平地彼此相邻。唯一的区别是您是在红色矩形或蓝色矩形上指定“旁边”约束。我找出了与通过形成约束的依赖图生成的度量分辨率顺序有关的原因,但我只是通过读取RelativeLayout的源代码来解决它,我找不到任何关于这种行为的文档/注释。由于RelativeLayout必须是一个常用的布局组件,是否有更直观的解释这种行为,或者是否有一些我缺少的文档?

2 个答案:

答案 0 :(得分:4)

虽然两者似乎都描述了相同的约束,但它们实际上并没有。不同的是,有人说,红色必须坐在蓝色旁边,而另一个说蓝色必须坐在红色旁边。一个意味着红色必须跟随蓝色,另一个意味着,蓝色必须跟随红色,并且他们都想去不同的地方。

在第一个实例中,红色框取决于蓝色框,因此首先构造蓝色框。蓝色框的宽度为60dp,因此首先构建一个60dp的蓝色框并对齐。然后是红色框,它有一个约束坐在蓝色框旁边。宽度0被忽略,因为它需要位于60dp蓝色旁边并向左对齐。

在第二个实例中,蓝色框取决于红色框,因此首先构造红色框。红色框表示它想要0dp并向左对齐,因此无法看到它。然后是蓝色的盒子,它需要坐在看不见的红色旁边并对齐,从而占据整个空间,忽略它的宽度。

希望这是有道理的:)

答案 1 :(得分:1)

所有这些参数定义于: android.widget.RelativeLayout

private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
    RelativeLayout.LayoutParams anchorParams;

    // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
    // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
    // wants to the right
    // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
    // wants to the left
    // left=10, right=20 means the left and right ends are both fixed
    childParams.mLeft = VALUE_NOT_SET;
    childParams.mRight = VALUE_NOT_SET;

    anchorParams = getRelatedViewParams(rules, LEFT_OF);
    if (anchorParams != null) {
        childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
                childParams.rightMargin);
    } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
        if (myWidth >= 0) {
            childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
        }
    }

    anchorParams = getRelatedViewParams(rules, RIGHT_OF);
    if (anchorParams != null) {
        childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
                childParams.leftMargin);
    } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
        childParams.mLeft = mPaddingLeft + childParams.leftMargin;
    }

    anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
    if (anchorParams != null) {
        childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
    } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
        childParams.mLeft = mPaddingLeft + childParams.leftMargin;
    }

    anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
    if (anchorParams != null) {
        childParams.mRight = anchorParams.mRight - childParams.rightMargin;
    } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
        if (myWidth >= 0) {
            childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
        }
    }

    if (0 != rules[ALIGN_PARENT_LEFT]) {
        childParams.mLeft = mPaddingLeft + childParams.leftMargin;
    }

    if (0 != rules[ALIGN_PARENT_RIGHT]) {
        if (myWidth >= 0) {
            childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
        }
    }
}

基于锚视图参数(anchorParams)的视图左右边缘(childParams.mLeft,childParams.mRight)计算。从此代码中,由LEFT_OF(android:layout_toLeftOf)定义的视图的childParams.mRight边缘可以通过ALIGN_RIGHT(android:layout_alignRight)或ALIGN_PARENT_RIGHT(android:layout_alignParentRight)重新计算。这是为什么0宽红色视图变为大于0的原因。

    <View
        android:id="@+id/box"
        android:background="#ff0000"
        android:layout_width="0dp"
        android:layout_height="30dp"
        android:layout_alignParentLeft="true"
        android:layout_toLeftOf="@+id/next_box"/>

此视图的右边缘由LEFT_OF定义:

childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
                    childParams.rightMargin);

在这种情况下,锚点视图是:

    <View
        android:id="@+id/next_box"
        android:background="#0000ff"
        android:layout_width="60dp"
        android:layout_alignParentRight="true"
        android:layout_height="30dp"
        />

此视图的左边缘距屏幕边缘右侧60dp未定义=&gt; childParams.mRight = screen_width - 60dp

此视图的左边缘由ALIGN_PARENT_LEFT:

定义
childParams.mLeft = mPaddingLeft + childParams.leftMargin;

此视图的左边缘锚点视图的左边缘为0,因为android:layout_alignParentLeft =“true”并且边距未定义=&gt; childParams.mLeft = 0

第二个例子可以进行相同的计算: childParams.mRight = screen_width childParams.mLeft = 0