Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin.Forms: Change RelativeLayout constraints afterwards

Is it possible to change the constraints of a RelativeLayout after they have been set one time?

In the documentation I see methods like GetBoundsConstraint(BindableObject), but I think you can only use them if you have a XAML file. For now I'm trying to do this in code. I get null if I try

RelativeLayout.GetBoundsConstraint(this.label);

The only way I found out is to remove the children from the layout and add it with the new constraints again.

Example:

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;
    }
}
like image 352
testing Avatar asked Mar 22 '16 17:03

testing


1 Answers

It does not officially possible with the current version of Xamarin Forms. The RelativeLayout container only recomputes constraints when adding/removing items from its children collection (it caches the solved constraints - presumable for performance). Even though the various constraints are implemented as Bindable Properties, they still do not get recomputed when changed.

I assume that the intention is to someday respect constraint updates, which would be useful with animations for example, but for now it doesn't appear to work that way.

HOWEVER, I took a look at the decompiled source for RelativeLayout and it is possible to hack together a way around it - but it might not suit your needs, depending on how much functionality you require and how complex your constraint definitions are.

See this example code (the key part is setting the constraint using SetBoundsConstraint, which overrides the internally computed bounds of the added view - and then calling 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;
            });
    }
}
like image 135
Keith Rome Avatar answered Oct 20 '22 00:10

Keith Rome