diff options
18 files changed, 466 insertions, 126 deletions
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla25943.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla25943.cs new file mode 100644 index 00000000..09c6dfc8 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla25943.cs @@ -0,0 +1,103 @@ +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +#if UITEST +using Xamarin.Forms.Core.UITests; +using Xamarin.UITest; +using NUnit.Framework; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ +#if UITEST + [Category(UITestCategories.InputTransparent)] +#endif + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Bugzilla, 25943, "[Android] TapGestureRecognizer does not work with a nested StackLayout", PlatformAffected.Android)] + public class Bugzilla25943 : TestContentPage + { + Label _result; + int _taps; + const string InnerLayout = "innerlayout"; + const string OuterLayout = "outerlayout"; + const string Success = "Success"; + + protected override void Init() + { + StackLayout layout = GetNestedStackLayout(); + + var tapGestureRecognizer = new TapGestureRecognizer(); + tapGestureRecognizer.Tapped += (sender, e) => + { + _taps = _taps + 1; + if (_taps == 2) + { + _result.Text = Success; + } + }; + layout.GestureRecognizers.Add(tapGestureRecognizer); + + Content = layout; + } + + public StackLayout GetNestedStackLayout() + { + _result = new Label(); + + var innerLayout = new StackLayout + { + AutomationId = InnerLayout, + HeightRequest = 100, + Orientation = StackOrientation.Horizontal, + HorizontalOptions = LayoutOptions.Fill, + BackgroundColor = Color.AntiqueWhite, + Children = + { + new Label + { + Text = "inner label", + FontSize = 20, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.CenterAndExpand + } + } + }; + + var outerLayout = new StackLayout + { + AutomationId = OuterLayout, + Orientation = StackOrientation.Vertical, + BackgroundColor = Color.Brown, + Children = + { + _result, + innerLayout, + new Label + { + Text = "outer label", + FontSize = 20, + HorizontalOptions = LayoutOptions.Center, + } + } + }; + + return outerLayout; + } + + +#if UITEST + [Test] + public void VerifyNestedStacklayoutTapsBubble(TransparentOverlayTests.TestPoint test) + { + RunningApp.WaitForElement(q => q.Marked(InnerLayout)); + RunningApp.Tap(InnerLayout); + + RunningApp.WaitForElement(q => q.Marked(OuterLayout)); + RunningApp.Tap(OuterLayout); + + RunningApp.WaitForElement(Success); + } +#endif + + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla39331.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla39331.cs index 4a51cf5b..8ec4131d 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla39331.cs +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla39331.cs @@ -12,6 +12,7 @@ namespace Xamarin.Forms.Controls.Issues { #if UITEST [Category(UITestCategories.BoxView)] + [Category(UITestCategories.InputTransparent)] #endif [Preserve (AllMembers = true)] diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla40173.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla40173.cs index 61d2b1b6..7a584e1d 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla40173.cs +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla40173.cs @@ -10,109 +10,118 @@ namespace Xamarin.Forms.Controls.Issues { #if UITEST [Category(UITestCategories.BoxView)] + [Category(UITestCategories.InputTransparent)] #endif - [Preserve(AllMembers = true)] - [Issue(IssueTracker.Bugzilla, 40173, "Android BoxView/Frame not clickthrough in ListView")] - public class Bugzilla40173 : TestContentPage // or TestMasterDetailPage, etc ... - { - const string CantTouchButtonId = "CantTouchButtonId"; - const string CanTouchButtonId = "CanTouchButtonId"; - const string ListTapTarget = "ListTapTarget"; - const string CantTouchFailText = "Failed"; - const string CanTouchSuccessText = "ButtonTapped"; - const string ListTapSuccessText = "ItemTapped"; + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Bugzilla, 40173, "Android BoxView/Frame not clickthrough in ListView")] + public class Bugzilla40173 : TestContentPage + { + const string CantTouchButtonId = "CantTouchButtonId"; + const string CanTouchButtonId = "CanTouchButtonId"; + const string ListTapTarget = "ListTapTarget"; + const string CantTouchFailText = "Failed"; + const string CanTouchSuccessText = "ButtonTapped"; + const string ListTapSuccessText = "ItemTapped"; #if UITEST - [Test] - public void ButtonBlocked() - { - RunningApp.Tap(q => q.All().Marked(CantTouchButtonId)); - RunningApp.WaitForNoElement(q => q.All().Text(CantTouchFailText)); + [Test] + public void ButtonBlocked() + { + RunningApp.Tap(q => q.All().Marked(CantTouchButtonId)); + RunningApp.WaitForNoElement(q => q.All().Text(CantTouchFailText)); - RunningApp.Tap(q => q.All().Marked(CanTouchButtonId)); - RunningApp.WaitForElement(q => q.All().Text(CanTouchSuccessText)); + RunningApp.Tap(q => q.All().Marked(CanTouchButtonId)); + RunningApp.WaitForElement(q => q.All().Text(CanTouchSuccessText)); #if !__MACOS__ - RunningApp.Tap(q => q.All().Marked(ListTapTarget)); - RunningApp.WaitForElement(q => q.All().Text(ListTapSuccessText)); + RunningApp.Tap(q => q.All().Marked(ListTapTarget)); + RunningApp.WaitForElement(q => q.All().Text(ListTapSuccessText)); #endif - } + } #endif - protected override void Init() - { - var outputLabel = new Label(); - var testButton = new Button - { - Text = "Can't Touch This", - AutomationId = CantTouchButtonId - }; + protected override void Init() + { + var outputLabel = new Label(); + var testButton = new Button + { + Text = "Can't Touch This", + AutomationId = CantTouchButtonId + }; - testButton.Clicked += (sender, args) => outputLabel.Text = CantTouchFailText; + testButton.Clicked += (sender, args) => outputLabel.Text = CantTouchFailText; - var testGrid = new Grid - { - Children = - { - testButton, - new BoxView - { - Color = Color.Pink.MultiplyAlpha(0.5) - } - } - }; + var testGrid = new Grid + { + AutomationId = "testgrid", + Children = + { + testButton, + new BoxView + { + AutomationId = "nontransparentBoxView", + Color = Color.Pink.MultiplyAlpha(0.5) + } + } + }; - // BoxView over Button prevents Button click - var testButtonOk = new Button - { - Text = "Can Touch This", - AutomationId = CanTouchButtonId - }; + // BoxView over Button prevents Button click + var testButtonOk = new Button + { + Text = "Can Touch This", + AutomationId = CanTouchButtonId + }; - testButtonOk.Clicked += (sender, args) => outputLabel.Text = CanTouchSuccessText; + testButtonOk.Clicked += (sender, args) => + { + outputLabel.Text = CanTouchSuccessText; + }; - var testGridOk = new Grid - { - Children = - { - testButtonOk, - new BoxView - { - Color = Color.Pink.MultiplyAlpha(0.5), - InputTransparent = true - } - } - }; + var testGridOk = new Grid + { + AutomationId = "testgridOK", + Children = + { + testButtonOk, + new BoxView + { + AutomationId = "transparentBoxView", + Color = Color.Pink.MultiplyAlpha(0.5), + InputTransparent = true + } + } + }; - var testListView = new ListView(); - var items = new[] { "Foo" }; - testListView.ItemsSource = items; - testListView.ItemTemplate = new DataTemplate(() => - { - var result = new ViewCell - { - View = new Grid - { - Children = - { - new BoxView - { - AutomationId = ListTapTarget, - Color = Color.Pink.MultiplyAlpha(0.5) - } - } - } - }; + var testListView = new ListView(); + var items = new[] { "Foo" }; + testListView.ItemsSource = items; + testListView.ItemTemplate = new DataTemplate(() => + { + var result = new ViewCell + { + View = new Grid + { + Children = + { + new BoxView + { + AutomationId = ListTapTarget, + Color = Color.Pink.MultiplyAlpha(0.5) + } + } + } + }; - return result; - }); + return result; + }); - testListView.ItemSelected += (sender, args) => outputLabel.Text = ListTapSuccessText; + testListView.ItemSelected += (sender, args) => outputLabel.Text = ListTapSuccessText; - Content = new StackLayout - { - Children = { outputLabel, testGrid, testGridOk, testListView } - }; - } - } + Content = new StackLayout + { + AutomationId = "Container Stack Layout", + Children = { outputLabel, testGrid, testGridOk, testListView } + }; + } + } }
\ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla55912.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla55912.cs index 834244bb..b5a65d88 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla55912.cs +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla55912.cs @@ -10,7 +10,7 @@ using NUnit.Framework; namespace Xamarin.Forms.Controls.Issues { [Preserve(AllMembers = true)] - [Issue(IssueTracker.Bugzilla, 955912, "Tap event not always propagated to containing Grid/StackLayout", + [Issue(IssueTracker.Bugzilla, 55912, "Tap event not always propagated to containing Grid/StackLayout", PlatformAffected.Android)] public class Bugzilla55912 : TestContentPage { @@ -26,7 +26,7 @@ namespace Xamarin.Forms.Controls.Issues layout.RowDefinitions.Add(new RowDefinition { Height = GridLength.Star }); layout.RowDefinitions.Add(new RowDefinition { Height = GridLength.Star }); - var testGrid = new Grid { BackgroundColor = Color.Red }; + var testGrid = new Grid { BackgroundColor = Color.Red, AutomationId = "testgrid"}; var gridLabel = new Label { AutomationId = GridLabelId, @@ -37,7 +37,7 @@ namespace Xamarin.Forms.Controls.Issues Grid.SetRow(testGrid, 1); testGrid.Children.Add(gridLabel); - var testStack = new StackLayout { BackgroundColor = Color.Default }; + var testStack = new StackLayout { BackgroundColor = Color.Default, AutomationId = "teststack"}; var stackLabel = new Label { AutomationId = StackLabelId, diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2775.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2775.cs index a32417f8..3fd30cbc 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2775.cs +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2775.cs @@ -5,15 +5,19 @@ using System.Collections.Generic; using Xamarin.Forms.Internals; #if UITEST +using Xamarin.Forms.Core.UITests; using Xamarin.UITest; using NUnit.Framework; #endif namespace Xamarin.Forms.Controls.Issues { +#if UITEST + [Category(UITestCategories.InputTransparent)] +#endif [Preserve (AllMembers = true)] [Issue (IssueTracker.Github, 2775, "ViewCell background conflicts with ListView Semi-Transparent and Transparent backgrounds")] - public class Issue2775 : TestContentPage // or TestMasterDetailPage, etc ... + public class Issue2775 : TestContentPage { protected override void Init () { diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TransparentOverlayTests.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TransparentOverlayTests.cs new file mode 100644 index 00000000..6fd5772d --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TransparentOverlayTests.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; +using System.Linq; + +#if UITEST +using Xamarin.Forms.Core.UITests; +using Xamarin.UITest; +using NUnit.Framework; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ +#if UITEST + [Category(UITestCategories.InputTransparent)] +#endif + [Preserve(AllMembers = true)] + [Issue(IssueTracker.None, 618, "Transparent Overlays", PlatformAffected.All)] + public class TransparentOverlayTests : TestNavigationPage + { + readonly Color _transparentColor = Color.Transparent; + readonly Color _nontransparentColor = Color.Blue; + + double _transparentOpacity = 0; + double _nonTransparentOpacity = 0.2; + + const string Running = "Running..."; + const string Success = "Success"; + const string Failure = "Failure"; + const string DefaultButtonText = "Button"; + const string Overlay = "overlay"; + const string AddOverlay = "Add Overlay"; + + protected override void Init() + { + PushAsync(Menu()); + } + + ContentPage Menu() + { + var layout = new StackLayout(); + + layout.Children.Add(new Label {Text = "Select a test below"}); + + foreach (var test in GenerateTests) + { + layout.Children.Add(MenuButton(test)); + } + + return new ContentPage { Content = layout }; + } + + Button MenuButton(TestPoint test) + { + var button = new Button { Text = test.ToString(), AutomationId = test.AutomationId }; + + button.Clicked += (sender, args) => PushAsync(CreateTestPage(test)); + + return button; + } + + [Preserve(AllMembers = true)] + public struct TestPoint + { + public TestPoint(int i) : this() + { + AutomationId = $"transparenttest{i}"; + + Opacity = (i & (1 << 0)) == 0; + InputTransparent = (i & (1 << 1)) == 0; + BackgroundColor = (i & (1 << 2)) == 0; + + // Layouts should be input transparent _only_ if they were explicitly told to be + ShouldBeTransparent = InputTransparent; + } + + internal string AutomationId { get; set; } + internal bool ShouldBeTransparent { get; set; } + + internal bool Opacity { get; set; } + internal bool InputTransparent { get; set; } + internal bool BackgroundColor { get; set; } + + public override string ToString() + { + return $"O{(Opacity ? "1" : "0")}, B{(BackgroundColor ? "1" : "0")}, I{(InputTransparent ? "1" : "0")}"; + } + } + + static IEnumerable<TestPoint> GenerateTests + { + get { return Enumerable.Range(0, 8).Select(i => new TestPoint(i)); } + } + + ContentPage CreateTestPage(TestPoint test) + { + Color backgroundColor = test.BackgroundColor? _transparentColor : _nontransparentColor; + double opacity = test.Opacity ? _transparentOpacity : _nonTransparentOpacity; + bool inputTransparent = test.InputTransparent; + + var grid = new Grid + { + AutomationId = "testgrid", + HorizontalOptions = LayoutOptions.Fill, + VerticalOptions = LayoutOptions.Fill + }; + + grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); + grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); + grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto }); + grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Star }); + + var instructions = new Label + { + HorizontalOptions = LayoutOptions.Fill, + HorizontalTextAlignment = TextAlignment.Center, + Text = $"Tap the 'Add Overlay' button, then tap the button labeled 'Button'." + + (test.ShouldBeTransparent + ? $" If the label below's text changes to {Success} the test has passed." + : " If the label below's text remains unchanged, the test has passed.") + }; + + grid.Children.Add(instructions); + + var results = new Label + { + HorizontalOptions = LayoutOptions.Fill, + HorizontalTextAlignment = TextAlignment.Center, + Text = Running + }; + + grid.Children.Add(results); + Grid.SetRow(results, 1); + + var button = new Button + { + Text = DefaultButtonText, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center + }; + + button.Clicked += (sender, args) => + { + results.Text = test.ShouldBeTransparent ? Success : Failure; + }; + + var layout = new StackLayout + { + AutomationId = Overlay, + HorizontalOptions = LayoutOptions.Fill, + VerticalOptions = LayoutOptions.Fill, + BackgroundColor = backgroundColor, + InputTransparent = inputTransparent, + Opacity = opacity + }; + + grid.Children.Add(button); + Grid.SetRow(button, 3); + + var addOverlayButton = new Button() { Text = AddOverlay }; + addOverlayButton.Clicked += (sender, args) => + { + grid.Children.Add(layout); + Grid.SetRow(layout, 3); + }; + + grid.Children.Add(addOverlayButton); + Grid.SetRow(addOverlayButton, 2); + + return new ContentPage { Content = grid, Title = test.ToString()}; + } + +#if UITEST + [Test, TestCaseSource(nameof(GenerateTests))] + public void VerifyInputTransparent(TestPoint test) + { + RunningApp.WaitForElement(q => q.Marked(test.AutomationId)); + RunningApp.Tap(test.AutomationId); + + // Determine the location of the button; we have to do this before adding the overlay because + // otherwise the iOS UI tests won't be able to find it consistently + var button = RunningApp.WaitForElement(DefaultButtonText); + var coords = new Tuple<float, float>(button[0].Rect.CenterX, button[0].Rect.CenterY); + + // Add the overlay + RunningApp.WaitForElement(AddOverlay); + RunningApp.Tap(AddOverlay); + + // Now tap the screen at the Button's location + // We can't do RunningApp.Tap(DefaultButtonText) because the UI tests on iOS can't see it + RunningApp.TapCoordinates(coords.Item1, coords.Item2); + + // Check the results + RunningApp.WaitForElement(test.ShouldBeTransparent ? Success : Running); + } +#endif + + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index 62b97166..c6f34ade 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -25,6 +25,7 @@ <Compile Include="$(MSBuildThisFileDirectory)Bugzilla24769.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla25234.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla25662.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)Bugzilla25943.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla26501.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla26868.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla27378.cs" /> @@ -287,6 +288,7 @@ <Compile Include="$(MSBuildThisFileDirectory)Bugzilla52533.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla53362.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla45874.cs" /> + <Compile Include="$(MSBuildThisFileDirectory)TransparentOverlayTests.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Unreported1.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Bugzilla53909.cs" /> <Compile Include="$(MSBuildThisFileDirectory)ListViewNRE.cs" /> diff --git a/Xamarin.Forms.Platform.Android/FastRenderers/GestureManager.cs b/Xamarin.Forms.Platform.Android/FastRenderers/GestureManager.cs index 8335b9d7..396b12fb 100644 --- a/Xamarin.Forms.Platform.Android/FastRenderers/GestureManager.cs +++ b/Xamarin.Forms.Platform.Android/FastRenderers/GestureManager.cs @@ -70,7 +70,7 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers if (View.GestureRecognizers.Count == 0) { handled = true; - return _motionEventHelper.HandleMotionEvent(parent); + return _motionEventHelper.HandleMotionEvent(parent, e); } handled = false; diff --git a/Xamarin.Forms.Platform.Android/Platform.cs b/Xamarin.Forms.Platform.Android/Platform.cs index add84eb6..4f6b33e9 100644 --- a/Xamarin.Forms.Platform.Android/Platform.cs +++ b/Xamarin.Forms.Platform.Android/Platform.cs @@ -1034,14 +1034,31 @@ namespace Xamarin.Forms.Platform.Android internal class DefaultRenderer : VisualElementRenderer<View> { bool _notReallyHandled; + readonly MotionEventHelper _motionEventHelper = new MotionEventHelper(); + internal void NotifyFakeHandling() { _notReallyHandled = true; } + public override bool OnTouchEvent(MotionEvent e) + { + if (base.OnTouchEvent(e)) + return true; + + return _motionEventHelper.HandleMotionEvent(Parent, e); + } + + protected override void OnElementChanged(ElementChangedEventArgs<View> e) + { + base.OnElementChanged(e); + + _motionEventHelper.UpdateElement(e.NewElement); + } + public override bool DispatchTouchEvent(MotionEvent e) { - #region + #region Excessive explanation // Normally dispatchTouchEvent feeds the touch events to its children one at a time, top child first, // (and only to the children in the hit-test area of the event) stopping as soon as one of them has handled // the event. @@ -1074,7 +1091,6 @@ namespace Xamarin.Forms.Platform.Android // don't consider the event truly "handled" yet. // Since a child control short-circuited the normal dispatchTouchEvent stuff, this layout never got the chance for // IOnTouchListener.OnTouch and the OnTouchEvent override to try handling the touches; we'll do that now - return OnTouchEvent(e); } diff --git a/Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs index aa19d81c..0737b26f 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs @@ -17,7 +17,7 @@ namespace Xamarin.Forms.Platform.Android if (base.OnTouchEvent(e)) return true; - return _motionEventHelper.HandleMotionEvent(Parent); + return _motionEventHelper.HandleMotionEvent(Parent, e); } protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e) diff --git a/Xamarin.Forms.Platform.Android/Renderers/ImageRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/ImageRenderer.cs index 43dac1fe..077cf159 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/ImageRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/ImageRenderer.cs @@ -111,7 +111,7 @@ namespace Xamarin.Forms.Platform.Android if (base.OnTouchEvent(e)) return true; - return _motionEventHelper.HandleMotionEvent(Parent); + return _motionEventHelper.HandleMotionEvent(Parent, e); } } }
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/Renderers/LabelRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/LabelRenderer.cs index 0736d17c..f3de6297 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/LabelRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/LabelRenderer.cs @@ -200,7 +200,7 @@ namespace Xamarin.Forms.Platform.Android if (base.OnTouchEvent(e)) return true; - return _motionEventHelper.HandleMotionEvent(Parent); + return _motionEventHelper.HandleMotionEvent(Parent, e); } } }
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/Renderers/MotionEventHelper.cs b/Xamarin.Forms.Platform.Android/Renderers/MotionEventHelper.cs index 4ed06d26..2653ee6e 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/MotionEventHelper.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/MotionEventHelper.cs @@ -7,9 +7,9 @@ namespace Xamarin.Forms.Platform.Android VisualElement _element; bool _isInViewCell; - public bool HandleMotionEvent(IViewParent parent) + public bool HandleMotionEvent(IViewParent parent, MotionEvent motionEvent) { - if (_isInViewCell || _element.InputTransparent) + if (_isInViewCell || _element.InputTransparent || motionEvent.Action == MotionEventActions.Cancel) { return false; } diff --git a/Xamarin.Forms.Platform.Android/VisualElementExtensions.cs b/Xamarin.Forms.Platform.Android/VisualElementExtensions.cs index 800a967e..268b3af2 100644 --- a/Xamarin.Forms.Platform.Android/VisualElementExtensions.cs +++ b/Xamarin.Forms.Platform.Android/VisualElementExtensions.cs @@ -16,39 +16,16 @@ namespace Xamarin.Forms.Platform.Android public static bool ShouldBeMadeClickable(this View view) { - var shouldBeClickable = false; for (var i = 0; i < view.GestureRecognizers.Count; i++) { IGestureRecognizer gesture = view.GestureRecognizers[i]; if (gesture is TapGestureRecognizer || gesture is PinchGestureRecognizer || gesture is PanGestureRecognizer) { - shouldBeClickable = true; - break; + return true; } } - // do some evil - // This is required so that a layout only absorbs click events if it is not fully transparent - // However this is not desirable behavior in a ViewCell because it prevents the ViewCell from activating - if (view is Layout && view.BackgroundColor != Color.Transparent && view.BackgroundColor != Color.Default) - { - Element parent = view.RealParent; - var skip = false; - while (parent != null) - { - if (parent is ViewCell) - { - skip = true; - break; - } - parent = parent.RealParent; - } - - if (!skip) - shouldBeClickable = true; - } - - return shouldBeClickable; + return false; } } }
\ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs b/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs index 747956c3..80d70ff7 100644 --- a/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs +++ b/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs @@ -93,7 +93,9 @@ namespace Xamarin.Forms.Platform.Android public override bool OnInterceptTouchEvent(MotionEvent ev) { if (!Element.IsEnabled || (Element.InputTransparent && Element.IsEnabled)) + { return true; + } return base.OnInterceptTouchEvent(ev); } diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs b/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs index 9b711b81..4843fd74 100644 --- a/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs +++ b/Xamarin.Forms.Platform.WinRT/VisualElementRenderer.cs @@ -548,7 +548,7 @@ namespace Xamarin.Forms.Platform.WinRT if (control != null) control.IsEnabled = Element.IsEnabled; else - IsHitTestVisible = Element.IsEnabled; + IsHitTestVisible = Element.IsEnabled && !Element.InputTransparent; } void UpdateTracker() diff --git a/Xamarin.Forms.Platform.WinRT/VisualElementTracker.cs b/Xamarin.Forms.Platform.WinRT/VisualElementTracker.cs index d477028f..7e9572bc 100644 --- a/Xamarin.Forms.Platform.WinRT/VisualElementTracker.cs +++ b/Xamarin.Forms.Platform.WinRT/VisualElementTracker.cs @@ -449,7 +449,7 @@ namespace Xamarin.Forms.Platform.WinRT static void UpdateInputTransparent(VisualElement view, FrameworkElement frameworkElement) { - frameworkElement.IsHitTestVisible = !view.InputTransparent; + frameworkElement.IsHitTestVisible = view.IsEnabled && !view.InputTransparent; } static void UpdateOpacity(VisualElement view, FrameworkElement frameworkElement) diff --git a/Xamarin.Forms.Platform.iOS/Platform.cs b/Xamarin.Forms.Platform.iOS/Platform.cs index b3542d69..5a442bab 100644 --- a/Xamarin.Forms.Platform.iOS/Platform.cs +++ b/Xamarin.Forms.Platform.iOS/Platform.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using CoreGraphics; using Foundation; using UIKit; using RectangleF = CoreGraphics.CGRect; @@ -478,6 +479,30 @@ namespace Xamarin.Forms.Platform.iOS internal class DefaultRenderer : VisualElementRenderer<VisualElement> { + public override UIView HitTest(CGPoint point, UIEvent uievent) + { + // UIview hit testing ignores objects which have an alpha of less than 0.01 + // (see https://developer.apple.com/reference/uikit/uiview/1622469-hittest) + // To prevent layouts with low opacity from being implicitly input transparent, + // we need to temporarily bump their alpha value during the actual hit testing, + // then restore it. If the opacity is high enough or user interaction is disabled, + // we don't have to worry about it. + + nfloat old = Alpha; + if (UserInteractionEnabled && old <= 0.01) + { + Alpha = (nfloat)0.011; + } + + var result = base.HitTest(point, uievent); + + if (UserInteractionEnabled && old <= 0.01) + { + Alpha = old; + } + + return result; + } } } }
\ No newline at end of file |