StackLayout宽度计算中的RelativeLayout

时间:2016-02-08 09:53:48

标签: android xaml xamarin xamarin.forms

我在StackLayout中嵌套了RelativeLayouts有一个奇怪的问题,看来StackLayout计算其元素的宽度应该使用RelativeLayout宽度,然后RelativeLayout再次重新计算它。这导致子控件是相对宽度的平方,但是下面的控件被放置为相对宽度。

这是一个错误还是我做错了什么?

例如

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="MyClass"
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             >

  <StackLayout Orientation="Horizontal" BackgroundColor="Blue">
    <RelativeLayout>
      <ContentView BackgroundColor="Red"

          RelativeLayout.XConstraint=
                "{ConstraintExpression Type=RelativeToParent,
                                      Property=Width,
                                      Factor=0}"
          RelativeLayout.WidthConstraint=
                "{ConstraintExpression Type=RelativeToParent,
                                      Property=Width,
                                      Factor=0.707106}"
          RelativeLayout.YConstraint=
                  "{ConstraintExpression Type=RelativeToParent,
                                        Property=Height,
                                        Factor=0}"
          RelativeLayout.HeightConstraint=
                "{ConstraintExpression Type=RelativeToParent,
                                      Property=Height,
                                      Factor=1}">

      </ContentView>

    </RelativeLayout>
    <RelativeLayout>
      <ContentView BackgroundColor="Green"

          RelativeLayout.XConstraint=
                "{ConstraintExpression Type=RelativeToParent,
                                      Property=Width,
                                      Factor=0}"
          RelativeLayout.WidthConstraint=
                "{ConstraintExpression Type=RelativeToParent,
                                      Property=Width,
                                      Factor=0.5}"
          RelativeLayout.YConstraint=
                  "{ConstraintExpression Type=RelativeToParent,
                                        Property=Height,
                                        Factor=0}"
          RelativeLayout.HeightConstraint=
                "{ConstraintExpression Type=RelativeToParent,
                                      Property=Height,
                                      Factor=1}">

      </ContentView>

    </RelativeLayout>

  </StackLayout>
</ContentPage>

Screen shot

2 个答案:

答案 0 :(得分:6)

两个RelativeLayout元素可能是一个令人困惑的因素,并且每个元素都被指定为相对于父StackLayout位于X位置0,这似乎是一个冲突。

从评论中可以看出,如果预期每个RelativeLayout都是父StackLayout宽度的一半,则可以通过消除其中一个RelativeLayout元素来简化布局。 / p>

然后可以将子条带的指定宽度除以2,使得每个子条带的宽度相对于父StackLayout的预期宽度,并且相对于父级设置的X位置也将它们水平放置

以下是我提出的建议:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="App1.HomePage"
         xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
>

<StackLayout Orientation="Horizontal" BackgroundColor="Blue">
  <RelativeLayout>
    <ContentView BackgroundColor="Red"
        RelativeLayout.XConstraint=
          "{ConstraintExpression Type=RelativeToParent,
                                 Property=Width,
                                 Factor=0}"
        RelativeLayout.WidthConstraint=
          "{ConstraintExpression Type=RelativeToParent,
                                 Property=Width,
                                 Factor=0.35}"
        RelativeLayout.YConstraint=
          "{ConstraintExpression Type=RelativeToParent,
                                 Property=Height,
                                 Factor=0}"
        RelativeLayout.HeightConstraint=
          "{ConstraintExpression Type=RelativeToParent,
                                 Property=Height,
                                 Factor=1}">
    </ContentView>
<!--
</RelativeLayout>
<RelativeLayout>
-->
    <ContentView BackgroundColor="Green"
        RelativeLayout.XConstraint=
          "{ConstraintExpression Type=RelativeToParent,
                                 Property=Width,
                                 Factor=0.5}"
        RelativeLayout.WidthConstraint=
          "{ConstraintExpression Type=RelativeToParent,
                                 Property=Width,
                                 Factor=0.25}"
        RelativeLayout.YConstraint=
          "{ConstraintExpression Type=RelativeToParent,
                                 Property=Height,
                                 Factor=0}"
        RelativeLayout.HeightConstraint=
          "{ConstraintExpression Type=RelativeToParent,
                                 Property=Height,
                                 Factor=1}">
      </ContentView>
    </RelativeLayout>
  </StackLayout>
</ContentPage>

这会产生更接近预期的东西吗?

Screenshot

答案 1 :(得分:2)

  

没有任何内容定位或覆盖任何指定的值。

这不太正确。只要看一眼,红色乐队看起来与第一个蓝色乐队的宽度大约是7:3。换句话说,这是第一个RelativeLayout。第二个RelativeLayout部分被推离屏幕,但它看起来像第二个ContentView完全在屏幕上,并且与前两个波段成比例地进行了褶皱 - 即绿色波段与红色波段的比例大约为5:7

换句话说,两个RelativeViews确实获得了相等的空间分配,两个ContentView完全在屏幕上。但是,两个RelativeLayouts的总宽度大于屏幕的宽度!

您可以在此处查看StackLayout的源代码: https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Core/StackLayout.cs

和RelativeLayout在这里: https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Core/RelativeLayout.cs

我没有详细介绍过所有内容,但StackLayout有两个布局,首先尝试做一个“天真”的布局,希望所有的孩子都适合。如果没有,它将通过压缩传递。由于RelativeLayout子节点没有指定任何宽度,因此它们最初尝试抓取父节点的整个宽度,并从那里进行压缩。

基本上它归结为这是一个来自Xamarin.Forms视角的欠约束系统。像这样使用StackLayout,没有任何东西给父母或孩子一个特定的宽度,可能会导致令人惊讶的结果。对于这样的情况,您不需要动态布局,其他布局(如Grid)可以提供更可预测的结果。

修改

经过进一步调查,如果你有一个包含多个子节点的StackLayout,包括一个(或多个)没有宽度规范的RelativeLayout,你可能做错了。其中一部分是有道理的,但您也看到了可疑(IMO)设计决策的症状。

默认情况下,RelativeLayout假定它的宽度与其父级相同。 StackLayout有一个多阶段的过程,先做一个Measure传递,然后是一个Layout传递,最后是一个压缩传递(在这种情况下实际上不会发挥作用)。在测量期间,RelativeLayout计算出实际布置其子项所需的空间。对于第一个RelativeLayout,这意味着它需要其父节点总宽度的大约70%(即总屏幕宽度的70% - 我忽略了小数量)。下一个RelativeLayout表示它需要总宽度的50%。

然后是布局传递。第一个RelativeLayout被给予它所要求的70%,因此它将其子项列出,这将是其中的70%(同意您的观察结果)。第二个RelativeLayout给出了它要求的50%,并将其50%分配给它的孩子。是的,这意味着StackLayout分配的屏幕宽度超过100%。当然它只显示前100%。

所以Xamarin.Forms的布局方式似乎有点奇怪,但我也不确定你对所需解决方案的限制。

假设您需要在StackLayout下拥有多个RelativeLayout兄弟,请按照这些说明获取您想要的内容:

  1. 填充每个RelativeLayout的整个宽度,即使它只是一个空的ContentView。如果不这样做,上面描述的测量/布局通过将导致严重的麻烦。

  2. 为每个RelativeLayout分配一个小的MinimumWidthRequest和一个包括Expand的Horizo​​ntalOptions。这将允许压缩传递(如上所述)启动,并为每个RelativeLayout提供相同的宽度分配。在测试中,我使用了MinimumWidthRequest =“20”和Horizo​​ntalOptions =“StartAndExpand”,并且工作正常。

  3. 这样做会为Xamarin.Forms布局引擎提供足够的约束,使其按照您期望的方式运行。