From 01a56f9836a1cc5ea7da860032d8ca71859b1e17 Mon Sep 17 00:00:00 2001 From: Philippe Leybaert Date: Thu, 2 Feb 2017 12:55:55 -0600 Subject: [PATCH] Update RelativeLayout to make it respond to constraint changes (#425) * Update RelativeLayout to make it respond to constraint changes Constraints of a RelativeLayout are bindable properties but the layout does not update when the constraints are updated. This change will invalidate the layout whenever XConstraint, YConstraint, WidthConstraint or HeightConstraint is changed (either in code or through a change in the bound property) * Specified changed handler as named property * Adding attached property accessors for layout properties Since the constraint attached properties can now be updated at runtime, setters are required for those properties. Also, when adding a child view at runtime using the Add() method with x/y/w/h constraints, generating the bounds constraints is deferred to the layout phase. * Unit tests for runtime constraints updates in RelativeLayout * Rename unit test method Rename LayoutChangesAtRuntim() to LayoutIsUpdatedWhenConstraintsChange() * Wrap RelativeLayout update setters in BatchBegin/Commit * Update documentation of RelativeLayout Added SetXConstraint(), SetYConstraint(), SetWidthConstraint() and SetHeightConstraint() --- .../RelativeLayoutTests.cs | 41 +++++++++ Xamarin.Forms.Core/RelativeLayout.cs | 62 ++++++++++++-- .../Xamarin.Forms/RelativeLayout.xml | 84 +++++++++++++++++++ 3 files changed, 179 insertions(+), 8 deletions(-) diff --git a/Xamarin.Forms.Core.UnitTests/RelativeLayoutTests.cs b/Xamarin.Forms.Core.UnitTests/RelativeLayoutTests.cs index 96b3dfc8f88..fdbcdfc0075 100644 --- a/Xamarin.Forms.Core.UnitTests/RelativeLayoutTests.cs +++ b/Xamarin.Forms.Core.UnitTests/RelativeLayoutTests.cs @@ -74,6 +74,47 @@ public void SimpleLayout () Assert.AreEqual (new Rectangle (30, 20, 50, 25), child.Bounds); } + [Test] + public void LayoutIsUpdatedWhenConstraintsChange() + { + var relativeLayout = new RelativeLayout + { + Platform = new UnitPlatform(), + IsPlatformEnabled = true + }; + + var child = new View + { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add(child, + Constraint.Constant(30), + Constraint.Constant(20), + Constraint.RelativeToParent(parent => parent.Height / 2), + Constraint.RelativeToParent(parent => parent.Height / 4)); + + relativeLayout.Layout(new Rectangle(0, 0, 100, 100)); + + Assert.AreEqual(new Rectangle(30, 20, 50, 25), child.Bounds); + + RelativeLayout.SetXConstraint(child, Constraint.Constant(40)); + + Assert.AreEqual(new Rectangle(40, 20, 50, 25), child.Bounds); + + RelativeLayout.SetYConstraint(child, Constraint.Constant(10)); + + Assert.AreEqual(new Rectangle(40, 10, 50, 25), child.Bounds); + + RelativeLayout.SetWidthConstraint(child, Constraint.RelativeToParent(parent => parent.Height / 4)); + + Assert.AreEqual(new Rectangle(40, 10, 25, 25), child.Bounds); + + RelativeLayout.SetHeightConstraint(child, Constraint.RelativeToParent(parent => parent.Height / 2)); + + Assert.AreEqual(new Rectangle(40, 10, 25, 50), child.Bounds); + } + [Test] public void SimpleExpressionLayout () { diff --git a/Xamarin.Forms.Core/RelativeLayout.cs b/Xamarin.Forms.Core/RelativeLayout.cs index b3a1b61570c..2b835013f86 100644 --- a/Xamarin.Forms.Core/RelativeLayout.cs +++ b/Xamarin.Forms.Core/RelativeLayout.cs @@ -8,13 +8,13 @@ namespace Xamarin.Forms { public class RelativeLayout : Layout { - public static readonly BindableProperty XConstraintProperty = BindableProperty.CreateAttached("XConstraint", typeof(Constraint), typeof(RelativeLayout), null); + public static readonly BindableProperty XConstraintProperty = BindableProperty.CreateAttached("XConstraint", typeof(Constraint), typeof(RelativeLayout), null, propertyChanged: ConstraintChanged); - public static readonly BindableProperty YConstraintProperty = BindableProperty.CreateAttached("YConstraint", typeof(Constraint), typeof(RelativeLayout), null); + public static readonly BindableProperty YConstraintProperty = BindableProperty.CreateAttached("YConstraint", typeof(Constraint), typeof(RelativeLayout), null, propertyChanged: ConstraintChanged); - public static readonly BindableProperty WidthConstraintProperty = BindableProperty.CreateAttached("WidthConstraint", typeof(Constraint), typeof(RelativeLayout), null); + public static readonly BindableProperty WidthConstraintProperty = BindableProperty.CreateAttached("WidthConstraint", typeof(Constraint), typeof(RelativeLayout), null, propertyChanged: ConstraintChanged); - public static readonly BindableProperty HeightConstraintProperty = BindableProperty.CreateAttached("HeightConstraint", typeof(Constraint), typeof(RelativeLayout), null); + public static readonly BindableProperty HeightConstraintProperty = BindableProperty.CreateAttached("HeightConstraint", typeof(Constraint), typeof(RelativeLayout), null, propertyChanged: ConstraintChanged); public static readonly BindableProperty BoundsConstraintProperty = BindableProperty.CreateAttached("BoundsConstraint", typeof(BoundsConstraint), typeof(RelativeLayout), null); @@ -72,6 +72,25 @@ IEnumerable ChildrenInSolveOrder } } + static void ConstraintChanged(BindableObject bindable, object oldValue, object newValue) + { + View view = bindable as View; + + (view?.Parent as RelativeLayout)?.UpdateBoundsConstraint(view); + } + + void UpdateBoundsConstraint(View view) + { + if (GetBoundsConstraint(view) == null) + return; // Bounds constraint hasn't been calculated yet, no need to update just yet + + CreateBoundsFromConstraints(view, GetXConstraint(view), GetYConstraint(view), GetWidthConstraint(view), GetHeightConstraint(view)); + + _childrenInSolveOrder = null; // New constraints may have impact on solve order + + InvalidateLayout(); + } + public static BoundsConstraint GetBoundsConstraint(BindableObject bindable) { return (BoundsConstraint)bindable.GetValue(BoundsConstraintProperty); @@ -102,6 +121,26 @@ public static void SetBoundsConstraint(BindableObject bindable, BoundsConstraint bindable.SetValue(BoundsConstraintProperty, value); } + public static void SetHeightConstraint(BindableObject bindable, Constraint value) + { + bindable.SetValue(HeightConstraintProperty, value); + } + + public static void SetWidthConstraint(BindableObject bindable, Constraint value) + { + bindable.SetValue(WidthConstraintProperty, value); + } + + public static void SetXConstraint(BindableObject bindable, Constraint value) + { + bindable.SetValue(XConstraintProperty, value); + } + + public static void SetYConstraint(BindableObject bindable, Constraint value) + { + bindable.SetValue(YConstraintProperty, value); + } + protected override void LayoutChildren(double x, double y, double width, double height) { foreach (View child in ChildrenInSolveOrder) @@ -248,13 +287,13 @@ void CreateBoundsFromConstraints(View view, Constraint xConstraint, Constraint y static Rectangle SolveView(View view) { BoundsConstraint boundsConstraint = GetBoundsConstraint(view); - var result = new Rectangle(); if (boundsConstraint == null) { throw new Exception("BoundsConstraint should not be null at this point"); } - result = boundsConstraint.Compute(); + + var result = boundsConstraint.Compute(); return result; } @@ -280,7 +319,7 @@ public RelativeElementCollection(ObservableCollection inner, RelativeLa public void Add(View view, Expression> bounds) { if (bounds == null) - throw new ArgumentNullException("bounds"); + throw new ArgumentNullException(nameof(bounds)); SetBoundsConstraint(view, BoundsConstraint.FromExpression(bounds)); base.Add(view); @@ -308,7 +347,14 @@ public void Add(View view, Expression> x = null, ExpressionTo be added. + + + + Method + + 2.0.0.0 + + + System.Void + + + + + + + The to which the constraint will be applied. + The on the height of the . + Sets as a constraint on the height of the . + To be added. + + + + + + Method + + 2.0.0.0 + + + System.Void + + + + + + + The to which the constraint will be applied. + The on the width of the . + Sets as a constraint on the width of the . + To be added. + + + + + + Method + + 2.0.0.0 + + + System.Void + + + + + + + The to which the constraint will be applied. + The on the X position of the . + Sets as a constraint on the X position of the . + To be added. + + + + + + Method + + 2.0.0.0 + + + System.Void + + + + + + + The to which the constraint will be applied. + The on the Y position of the . + Sets as a constraint on the Y position of the . + To be added. + +