【问题标题】:Xamarin.Forms: Change RelativeLayout constraints afterwardsXamarin.Forms:之后更改 RelativeLayout 约束
【发布时间】:2016-07-09 17:31:48
【问题描述】:

RelativeLayout 的约束设置一次后是否可以更改?

在文档中,我看到了类似GetBoundsConstraint(BindableObject) 的方法,但我认为只有在拥有 XAML 文件时才能使用它们。现在我正试图在代码中做到这一点。如果我尝试,我会得到null

RelativeLayout.GetBoundsConstraint(this.label);

我发现的唯一方法是从布局中删除子元素并再次使用新约束添加它。

示例:

public class TestPage : ContentPage
{
    private RelativeLayout relativeLayout;
    private BoxView view;
    private bool moreWidth = false;

    public TestPage()
    {
        this.view = new BoxView
        {
            BackgroundColor = Color.Yellow,
        };

        Button button = new Button
        {
            Text = "change",
            TextColor = Color.Black,
        };

        button.Clicked += Button_Clicked;

        this.relativeLayout = new RelativeLayout();

        this.relativeLayout.Children.Add(this.view,
            Constraint.Constant(0),
            Constraint.Constant(0),
            Constraint.Constant(80),
            Constraint.RelativeToParent((parent) =>
            {
                return parent.Height;
            }));

        this.relativeLayout.Children.Add(button,
            Constraint.RelativeToParent((parent) =>
            {
                return parent.Width / 2;
            }),
            Constraint.RelativeToParent((parent) =>
            {
                return parent.Height / 2;
            }));

        this.Content = this.relativeLayout;
    }

    private void Button_Clicked(object sender, EventArgs e)
    {
        double width = 0;
        if(this.moreWidth)
        {
            width = 120;
        }
        else
        {
            width = 80;
        }
        var c= BoundsConstraint.FromExpression((Expression<Func<Rectangle>>)(() => new Rectangle(0, 0, width, this.Content.Height)), new View[0]);
        RelativeLayout.SetBoundsConstraint(this.view, c);
        this.relativeLayout.ForceLayout();
        this.moreWidth = !this.moreWidth;
    }
}

【问题讨论】:

    标签: c# xamarin layout xamarin.forms


    【解决方案1】:

    当前版本的 Xamarin 表单正式不可能。 RelativeLayout 容器仅在从其子集合中添加/删除项目时重新计算约束(它缓存已解决的约束 - 可能是出于性能考虑)。即使各种约束被实现为可绑定属性,它们在更改时仍然不会被重新计算。

    我认为我们的意图是在某一天尊重约束更新,这对于例如动画来说很有用,但目前它似乎不是这样工作的。

    然而,我查看了RelativeLayout 的反编译源代码, 有可能以某种方式解决它 - 但它可能不适合您的需求,具体取决于关于您需要多少功能以及您的约束定义有多复杂。

    查看此示例代码(关键部分是使用 SetBoundsConstraint 设置约束,它会覆盖已添加视图的内部计算边界 - 然后调用 ForceLayout()):

    public partial class App : Application
    {
        public App ()
        {
            var label = new Label {
                Text = "Test",
                HorizontalTextAlignment = TextAlignment.Center,
                VerticalTextAlignment = TextAlignment.Center,
                BackgroundColor = Color.Silver
            };
            var layout = new RelativeLayout ();
            layout.Children.Add (label,
                Constraint.Constant (50),
                Constraint.Constant (100),
                Constraint.Constant (260),
                Constraint.Constant (30));
            MainPage = new ContentPage {
                Content = layout
            };
    
            var fwd = true;
            layout.Animate ("bounce",
                (delta) => {
                    var d = fwd ? delta : 1.0 - delta;
                    var y = 100.0 + (50.0 * d);
                    var c = BoundsConstraint.FromExpression ((Expression<Func<Rectangle>>)(() => new Rectangle (50, y, 260, 30)), new View [0]);
                    RelativeLayout.SetBoundsConstraint(label, c);
                    layout.ForceLayout ();
                }, 16, 800, Easing.SinInOut, (f, b) => {
                    // reset direction
                    fwd = !fwd;
                }, () => {
                    // keep bouncing
                    return true;
                });
        }
    }
    

    【讨论】:

    • 感谢您的出色回答!是否可以将BoundsConstraint.FromExpressionConstraint.RelativeToParent 一起使用?还是只能为Rectangle 设置值?我想要做的是改变占据屏幕一半的元素的宽度。因此,您必须知道父母的身高。目前我正在使用this.Content.Height,这似乎有效。虽然我必须进一步测试它。
    • 只要您正在修改的项目上的所有约束都是 Constant 或 RelativeToParent(BoundsConstraint 是所有四个 x/y/width/height 约束的组合,所以所有其中必须满足此标准)。如果其中任何一个使用RelativeToView,那么您需要将这些视图作为第二个参数传递给数组中的FromExpression()。除非你尝试一下,否则很难确定这个 hack 是否适用于除 Constant 之外的任何东西。当然,这取决于内部结构,因此可能会在下一个 XF 版本中中断。
    • 我尝试使用RelativeToParent,但难以使用正确的符号。我尝试使用 Constraint.RelativeToParent((parent) =&gt; { return parent.Height;}) 而不是常量,但我收到了类似 too many parameters 的消息。你搞定了吗?
    • 你所有的大小计算逻辑都需要进入传递给FromExtression()的lambda。您不能直接使用Constraint.RelativeToParent() - 因为它只适用于边界的一维。为了有机会获得完全正确的代码,我需要您更新原始问题以包含您用于所有约束的代码,包括您稍后更改的约束,这也将有助于也请参阅相关的 XAML。
    • 目前它只是一个测试项目,我想找出最适合我的选项。除此之外,我可以从中学习。所以我用这个例子更新了我的问题。感谢您的努力。
    【解决方案2】:

    是的。这可能。 布局代码:

    <StackLayout RelativeLayout.XConstraint="{Binding XConstaint}" ...>
    

    虚拟机代码:

    public Constraint XConstaint
    {
      get => _xConstaint;
      set { SetFieldValue(ref _xConstaint, value, nameof(XConstaint)); }
    }
    
    public override void OnAppearing()
    {
      base.OnAppearing();
      XConstaint = Constraint.RelativeToParent((parent) => { return parent.Width - 128; });
    }
    

    【讨论】:

      【解决方案3】:

      如果我理解正确,我会尝试以下方法

      为 relativeLayout 实现 SizeChanged 事件

      喜欢:-

      var relativeLayout = new RelativeLayout ();
      
      relativeLayout.SizeChanged += someEventForTesting;
      

      并在您想要调整相对布局或某个特定子级的大小时调用该事件。

      public void someEventForTesting(Object sender , EventArgs)
      {
      var layout = (RelativeLayout)sender ;
      //here you have access to layout and every child , you can add them again with new constraint .
      }
      

      【讨论】:

      • 如果SizeChanged 事件被触发,我可以访问约束吗?如何使用对应的RelativeLayout 触发SizeChanged 事件?
      • SizeChanged 事件不会提供任何优势,除非您在代码中使用Grid 进行布局和手动定位/调整内容项。此外,要触发 SizeChanged 事件,您需要调整容器的大小 - 如果有很多子视图,这可能是一件非常昂贵的事情。
      • @testing- 正如 Keith 正确指出的那样,当你有很多孩子时,它可能是一项广泛的工作,要触发事件只需向特定布局添加任何内容,它将尝试更改容器并且事件将是被解雇了,如果你可能的话,你重写整个 relativeLayout,或者改变那些将受到影响的孩子的位置。想象一种情况(纯属假设):- 你有一个布局,上面有 4 个按钮。你想点击第 4 个并获得一个出现在第 4 个的新 btn。在这种情况下。在 bttn 点击事件上,你可以触发 sizeChange 并在那里完成工作
      猜你喜欢
      • 2019-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-06
      • 2017-02-27
      • 2012-10-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多