diff --git a/AndroidNative/FormsViewGroup/src/main/java/com/xamarin/forms/platform/android/FormsViewGroup.java b/AndroidNative/FormsViewGroup/src/main/java/com/xamarin/forms/platform/android/FormsViewGroup.java index 6f7cd93a808..45c7a048d4f 100644 --- a/AndroidNative/FormsViewGroup/src/main/java/com/xamarin/forms/platform/android/FormsViewGroup.java +++ b/AndroidNative/FormsViewGroup/src/main/java/com/xamarin/forms/platform/android/FormsViewGroup.java @@ -80,7 +80,8 @@ public void sendBatchUpdate ( float rotation, float rotationX, float rotationY, - float scale, + float scaleX, + float scaleY, float translationX, float translationY){ setPivotX (pivotX); @@ -96,8 +97,8 @@ public void sendBatchUpdate ( setRotation (rotation); setRotationX (rotationX); setRotationY (rotationY); - setScaleX (scale); - setScaleY (scale); + setScaleX (scaleX); + setScaleY (scaleY); setTranslationX (translationX); setTranslationY (translationY); } @@ -112,7 +113,8 @@ public static void sendViewBatchUpdate ( float rotation, float rotationX, float rotationY, - float scale, + float scaleX, + float scaleY, float translationX, float translationY){ view.setPivotX (pivotX); @@ -128,8 +130,8 @@ public static void sendViewBatchUpdate ( view.setRotation (rotation); view.setRotationX (rotationX); view.setRotationY (rotationY); - view.setScaleX (scale); - view.setScaleY (scale); + view.setScaleX (scaleX); + view.setScaleY (scaleY); view.setTranslationX (translationX); view.setTranslationY (translationY); } diff --git a/Xamarin.Forms.Controls/GalleryPages/ScaleRotate.cs b/Xamarin.Forms.Controls/GalleryPages/ScaleRotate.cs index e189f839606..18276d814b0 100644 --- a/Xamarin.Forms.Controls/GalleryPages/ScaleRotate.cs +++ b/Xamarin.Forms.Controls/GalleryPages/ScaleRotate.cs @@ -39,19 +39,41 @@ public ScaleRotate() scaleSlider.SetBinding(Slider.ValueProperty, new Binding("Scale", BindingMode.TwoWay)); + // Label and Slider for ScaleX property. + Label scaleXSliderValue = new Label { + VerticalTextAlignment = TextAlignment.Center + }; + Grid.SetRow(scaleXSliderValue, 1); + Grid.SetColumn(scaleXSliderValue, 0); + + Slider scaleXSlider = new Slider { + Maximum = 10 + }; + Grid.SetRow(scaleXSlider, 1); + Grid.SetColumn(scaleXSlider, 1); + + // Set Bindings. + scaleXSliderValue.BindingContext = scaleXSlider; + scaleXSliderValue.SetBinding(Label.TextProperty, + new Binding("Value", BindingMode.OneWay, null, null, "ScaleX = {0:F1}")); + + scaleXSlider.BindingContext = label; + scaleXSlider.SetBinding(Slider.ValueProperty, + new Binding("ScaleX", BindingMode.TwoWay)); + // Label and Slider for Rotation property. Label rotationSliderValue = new Label { VerticalTextAlignment = TextAlignment.Center }; - Grid.SetRow(rotationSliderValue, 1); + Grid.SetRow(rotationSliderValue, 2); Grid.SetColumn(rotationSliderValue, 0); Slider rotationSlider = new Slider { Maximum = 360 }; - Grid.SetRow(rotationSlider, 1); + Grid.SetRow(rotationSlider, 2); Grid.SetColumn(rotationSlider, 1); // Set Bindings. @@ -68,7 +90,7 @@ public ScaleRotate() { VerticalTextAlignment = TextAlignment.Center }; - Grid.SetRow(anchorxStepperValue, 2); + Grid.SetRow(anchorxStepperValue, 3); Grid.SetColumn(anchorxStepperValue, 0); Stepper anchorxStepper = new Stepper @@ -77,7 +99,7 @@ public ScaleRotate() Minimum = -1, Increment = 0.5 }; - Grid.SetRow(anchorxStepper, 2); + Grid.SetRow(anchorxStepper, 3); Grid.SetColumn(anchorxStepper, 1); // Set bindings. @@ -94,7 +116,7 @@ public ScaleRotate() { VerticalTextAlignment = TextAlignment.Center }; - Grid.SetRow(anchoryStepperValue, 3); + Grid.SetRow(anchoryStepperValue, 4); Grid.SetColumn(anchoryStepperValue, 0); Stepper anchoryStepper = new Stepper @@ -103,7 +125,7 @@ public ScaleRotate() Minimum = -1, Increment = 0.5 }; - Grid.SetRow(anchoryStepper, 3); + Grid.SetRow(anchoryStepper, 4); Grid.SetColumn(anchoryStepper, 1); // Set bindings. @@ -130,6 +152,7 @@ public ScaleRotate() new RowDefinition { Height = GridLength.Auto }, new RowDefinition { Height = GridLength.Auto }, new RowDefinition { Height = GridLength.Auto }, + new RowDefinition { Height = GridLength.Auto }, }, ColumnDefinitions = { @@ -140,6 +163,8 @@ public ScaleRotate() { scaleSliderValue, scaleSlider, + scaleXSliderValue, + scaleXSlider, rotationSliderValue, rotationSlider, anchorxStepperValue, diff --git a/Xamarin.Forms.Core/VisualElement.cs b/Xamarin.Forms.Core/VisualElement.cs index 604dc09a456..8ba01c542c7 100644 --- a/Xamarin.Forms.Core/VisualElement.cs +++ b/Xamarin.Forms.Core/VisualElement.cs @@ -48,7 +48,11 @@ public partial class VisualElement : Element, IAnimatable, IVisualElementControl public static readonly BindableProperty RotationYProperty = BindableProperty.Create("RotationY", typeof(double), typeof(VisualElement), default(double)); - public static readonly BindableProperty ScaleProperty = BindableProperty.Create("Scale", typeof(double), typeof(VisualElement), 1d); + public static readonly BindableProperty ScaleProperty = BindableProperty.Create(nameof(Scale), typeof(double), typeof(VisualElement), 1d); + + public static readonly BindableProperty ScaleXProperty = BindableProperty.Create(nameof(ScaleX), typeof(double), typeof(VisualElement), 1d); + + public static readonly BindableProperty ScaleYProperty = BindableProperty.Create(nameof(ScaleY), typeof(double), typeof(VisualElement), 1d); public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create("IsVisible", typeof(bool), typeof(VisualElement), true, propertyChanged: (bindable, oldvalue, newvalue) => ((VisualElement)bindable).OnIsVisibleChanged((bool)oldvalue, (bool)newvalue)); @@ -266,10 +270,19 @@ public double RotationY set { SetValue(RotationYProperty, value); } } - public double Scale - { - get { return (double)GetValue(ScaleProperty); } - set { SetValue(ScaleProperty, value); } + public double Scale { + get => (double)GetValue(ScaleProperty); + set => SetValue(ScaleProperty, value); + } + + public double ScaleX { + get => (double)GetValue(ScaleXProperty); + set => SetValue(ScaleXProperty, value); + } + + public double ScaleY { + get => (double)GetValue(ScaleYProperty); + set => SetValue(ScaleYProperty, value); } public Style Style diff --git a/Xamarin.Forms.Platform.Android.FormsViewGroup/Jars/formsviewgroup.jar b/Xamarin.Forms.Platform.Android.FormsViewGroup/Jars/formsviewgroup.jar index 10719e2d3bc..9e0bb69e97e 100644 Binary files a/Xamarin.Forms.Platform.Android.FormsViewGroup/Jars/formsviewgroup.jar and b/Xamarin.Forms.Platform.Android.FormsViewGroup/Jars/formsviewgroup.jar differ diff --git a/Xamarin.Forms.Platform.Android/VisualElementTracker.cs b/Xamarin.Forms.Platform.Android/VisualElementTracker.cs index 0f66281919d..3b51eef6ed5 100644 --- a/Xamarin.Forms.Platform.Android/VisualElementTracker.cs +++ b/Xamarin.Forms.Platform.Android/VisualElementTracker.cs @@ -128,7 +128,7 @@ void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) if (e.PropertyName == VisualElement.XProperty.PropertyName || e.PropertyName == VisualElement.YProperty.PropertyName || e.PropertyName == VisualElement.WidthProperty.PropertyName || e.PropertyName == VisualElement.HeightProperty.PropertyName) _layoutNeeded = true; - else if (e.PropertyName == VisualElement.AnchorXProperty.PropertyName || e.PropertyName == VisualElement.AnchorYProperty.PropertyName || e.PropertyName == VisualElement.ScaleProperty.PropertyName || + else if (e.PropertyName == VisualElement.AnchorXProperty.PropertyName || e.PropertyName == VisualElement.AnchorYProperty.PropertyName || e.PropertyName == VisualElement.ScaleProperty.PropertyName || e.PropertyName == VisualElement.ScaleXProperty.PropertyName || e.PropertyName == VisualElement.ScaleYProperty.PropertyName || e.PropertyName == VisualElement.RotationProperty.PropertyName || e.PropertyName == VisualElement.RotationXProperty.PropertyName || e.PropertyName == VisualElement.RotationYProperty.PropertyName || e.PropertyName == VisualElement.IsVisibleProperty.PropertyName || e.PropertyName == VisualElement.OpacityProperty.PropertyName || e.PropertyName == VisualElement.TranslationXProperty.PropertyName || e.PropertyName == VisualElement.TranslationYProperty.PropertyName) @@ -146,7 +146,7 @@ void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) UpdateAnchorX(); else if (e.PropertyName == VisualElement.AnchorYProperty.PropertyName) UpdateAnchorY(); - else if (e.PropertyName == VisualElement.ScaleProperty.PropertyName) + else if (e.PropertyName == VisualElement.ScaleProperty.PropertyName || e.PropertyName == VisualElement.ScaleXProperty.PropertyName || e.PropertyName == VisualElement.ScaleYProperty.PropertyName) UpdateScale(); else if (e.PropertyName == VisualElement.RotationProperty.PropertyName) UpdateRotation(); @@ -240,7 +240,7 @@ void SetElement(VisualElement oldElement, VisualElement newElement) UpdateRotationX(); if (oldElement.RotationY != newElement.RotationY) UpdateRotationY(); - if (oldElement.Scale != newElement.Scale) + if (oldElement.Scale != newElement.Scale || oldElement.ScaleX != newElement.ScaleX || oldElement.ScaleY != newElement.ScaleY) UpdateScale(); // ReSharper restore CompareOfFloatsByEqualityOperator @@ -310,17 +310,26 @@ void UpdateNativeView(object sender, EventArgs e) if (aview is FormsViewGroup) { var formsViewGroup = (FormsViewGroup)aview; - formsViewGroup.SendBatchUpdate((float)(view.AnchorX * _context.ToPixels(view.Width)), (float)(view.AnchorY * _context.ToPixels(view.Height)), - (int)(view.IsVisible ? ViewStates.Visible : ViewStates.Invisible), view.IsEnabled, (float)view.Opacity, (float)view.Rotation, (float)view.RotationX, (float)view.RotationY, (float)view.Scale, - _context.ToPixels(view.TranslationX), _context.ToPixels(view.TranslationY)); + formsViewGroup.SendBatchUpdate((float)(view.AnchorX * _context.ToPixels(view.Width)), + (float)(view.AnchorY * _context.ToPixels(view.Height)), + (int)(view.IsVisible ? ViewStates.Visible : ViewStates.Invisible), + view.IsEnabled, + (float)view.Opacity, + (float)view.Rotation, + (float)view.RotationX, + (float)view.RotationY, + (float)view.ScaleX, + (float)view.ScaleY, + _context.ToPixels(view.TranslationX), + _context.ToPixels(view.TranslationY)); } else { FormsViewGroup.SendViewBatchUpdate(aview, (float)(view.AnchorX * _context.ToPixels(view.Width)), (float)(view.AnchorY * _context.ToPixels(view.Height)), (int)(view.IsVisible ? ViewStates.Visible : ViewStates.Invisible), view.IsEnabled, (float)view.Opacity, - (float)view.Rotation, (float)view.RotationX, (float)view.RotationY, (float)view.Scale, - _context.ToPixels(view.TranslationX), _context.ToPixels(view.TranslationY)); + (float)view.Rotation, (float)view.RotationX, (float)view.RotationY, (float)view.ScaleX, + (float)view.ScaleY, _context.ToPixels(view.TranslationX), _context.ToPixels(view.TranslationY)); } Performance.Stop(reference); @@ -368,8 +377,8 @@ void UpdateScale() VisualElement view = _renderer.Element; AView aview = _renderer.View; - aview.ScaleX = (float)view.Scale; - aview.ScaleY = (float)view.Scale; + aview.ScaleX = (float)view.Scale * (float)view.ScaleX; + aview.ScaleY = (float)view.Scale * (float)view.ScaleY; } void UpdateTranslationX() @@ -411,4 +420,4 @@ public void OnViewDetachedFromWindow(AView detachedView) } } } -} \ No newline at end of file +} diff --git a/Xamarin.Forms.Platform.UAP/VisualElementTracker.cs b/Xamarin.Forms.Platform.UAP/VisualElementTracker.cs index ead389e1f1b..7f0430e46ac 100644 --- a/Xamarin.Forms.Platform.UAP/VisualElementTracker.cs +++ b/Xamarin.Forms.Platform.UAP/VisualElementTracker.cs @@ -197,7 +197,7 @@ protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs { UpdateScaleAndRotation(Element, Container); } - else if (e.PropertyName == VisualElement.ScaleProperty.PropertyName) + else if (e.PropertyName == VisualElement.ScaleProperty.PropertyName || e.PropertyName == VisualElement.ScaleXProperty.PropertyName || e.PropertyName == VisualElement.ScaleYProperty.PropertyName) { UpdateScaleAndRotation(Element, Container); } @@ -508,9 +508,8 @@ static void UpdateScaleAndRotation(VisualElement view, FrameworkElement framewor { double anchorX = view.AnchorX; double anchorY = view.AnchorY; - double scale = view.Scale; frameworkElement.RenderTransformOrigin = new Windows.Foundation.Point(anchorX, anchorY); - frameworkElement.RenderTransform = new ScaleTransform { ScaleX = scale, ScaleY = scale }; + frameworkElement.RenderTransform = new ScaleTransform { ScaleX = view.Scale * view.ScaleX, ScaleY = view.Scale * view.ScaleY }; UpdateRotation(view, frameworkElement); } @@ -589,4 +588,4 @@ void HandleDoubleTapped(object sender, DoubleTappedRoutedEventArgs doubleTappedR doubleTappedRoutedEventArgs.Handled = true; } } -} \ No newline at end of file +} diff --git a/Xamarin.Forms.Platform.iOS/VisualElementTracker.cs b/Xamarin.Forms.Platform.iOS/VisualElementTracker.cs index 2b1d751f700..f243c0e6eaf 100644 --- a/Xamarin.Forms.Platform.iOS/VisualElementTracker.cs +++ b/Xamarin.Forms.Platform.iOS/VisualElementTracker.cs @@ -79,7 +79,7 @@ void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == VisualElement.XProperty.PropertyName || e.PropertyName == VisualElement.YProperty.PropertyName || e.PropertyName == VisualElement.WidthProperty.PropertyName || e.PropertyName == VisualElement.HeightProperty.PropertyName || e.PropertyName == VisualElement.AnchorXProperty.PropertyName || e.PropertyName == VisualElement.AnchorYProperty.PropertyName || - e.PropertyName == VisualElement.TranslationXProperty.PropertyName || e.PropertyName == VisualElement.TranslationYProperty.PropertyName || e.PropertyName == VisualElement.ScaleProperty.PropertyName || + e.PropertyName == VisualElement.TranslationXProperty.PropertyName || e.PropertyName == VisualElement.TranslationYProperty.PropertyName || e.PropertyName == VisualElement.ScaleProperty.PropertyName || e.PropertyName == VisualElement.ScaleXProperty.PropertyName || e.PropertyName == VisualElement.ScaleYProperty.PropertyName || e.PropertyName == VisualElement.RotationProperty.PropertyName || e.PropertyName == VisualElement.RotationXProperty.PropertyName || e.PropertyName == VisualElement.RotationYProperty.PropertyName || e.PropertyName == VisualElement.IsVisibleProperty.PropertyName || e.PropertyName == VisualElement.IsEnabledProperty.PropertyName || e.PropertyName == VisualElement.InputTransparentProperty.PropertyName || e.PropertyName == VisualElement.OpacityProperty.PropertyName || @@ -154,6 +154,8 @@ void OnUpdateNativeControl(CALayer caLayer) var rotationY = (float)view.RotationY; var rotation = (float)view.Rotation; var scale = (float)view.Scale; + var scaleX = (float)view.ScaleX * scale; + var scaleY = (float)view.ScaleY * scale; var width = (float)view.Width; var height = (float)view.Height; var x = (float)view.X + (float)CompressedLayout.GetHeadlessOffset(view).X; @@ -246,8 +248,8 @@ void OnUpdateNativeControl(CALayer caLayer) if (Math.Abs(translationX) > epsilon || Math.Abs(translationY) > epsilon) transform = transform.Translate(translationX, translationY, 0); - if (Math.Abs(scale - 1) > epsilon) - transform = transform.Scale(scale); + if (Math.Abs(scaleX - 1) > epsilon || Math.Abs(scaleY - 1) > epsilon) + transform = transform.Scale(scaleX, scaleY, scale); // not just an optimization, iOS will not "pixel align" a view which has m34 set if (Math.Abs(rotationY % 180) > epsilon || Math.Abs(rotationX % 180) > epsilon)