[Android] Prevent bubbled gestures from falling through transparent containers (#3091) fixes #2858

* [Android] Prevent bubbled gestures from falling through to controls behind transparent containers;
fixes #2858

* Remove XAMLC attribute
This commit is contained in:
E.Z. Hart 2018-06-28 05:35:55 -06:00 коммит произвёл Rui Marinho
Родитель c79c77dbdf
Коммит b4da374c73
6 изменённых файлов: 176 добавлений и 10 удалений

Просмотреть файл

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8" ?>
<controls:TestContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Xamarin.Forms.Controls;assembly=Xamarin.Forms.Controls"
x:Class="Xamarin.Forms.Controls.Issues.Issue2858">
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" ></RowDefinition>
<RowDefinition Height="Auto" ></RowDefinition>
<RowDefinition Height="*" ></RowDefinition>
</Grid.RowDefinitions>
<Label Text="Tap the red box below. Then tap the green area outside the box. If 'Success' is visible below, this test has passed."></Label>
<Label Grid.Row="1" Text="Running..." x:Name="Result"></Label>
<Grid Grid.Row="2" BackgroundColor="Blue" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
AutomationId="TapGrid">
<Grid.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_OnTapped" />
</Grid.GestureRecognizers>
</Grid>
<Grid Grid.Row="2" RowSpacing="0" ColumnSpacing="0" InputTransparent="True" CascadeInputTransparent="False"
Padding="10" BackgroundColor="Green" AutomationId="OuterGrid">
<Grid BackgroundColor="Red" HorizontalOptions="Center" WidthRequest="300" VerticalOptions="Center"
HeightRequest="300" AutomationId="InnerGrid">
</Grid>
</Grid>
</Grid>
</ContentPage.Content>
</controls:TestContentPage>

Просмотреть файл

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml;
#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
#endif
namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.InputTransparent)]
[Category(UITestCategories.Gestures)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 2858, "Transparency Cascading", PlatformAffected.Android)]
public partial class Issue2858 : TestContentPage
{
const string Success = "Success";
const string Failure = "Fail";
const string InnerGrid = "InnerGrid";
const string OuterGrid = "OuterGrid";
#pragma warning disable CS0414
int _tapCount = 0;
#pragma warning restore CS0414
public Issue2858 ()
{
#if APP
InitializeComponent ();
#endif
}
#if APP
void TapGestureRecognizer_OnTapped(object sender, EventArgs e)
{
_tapCount += 1;
if (_tapCount == 1)
{
Result.Text = Success;
}
else
{
Result.Text = Failure;
}
}
#endif
protected override void Init()
{
}
#if UITEST
[Test]
public void CascadeInputTransparentGrids()
{
RunningApp.WaitForElement(InnerGrid);
RunningApp.Tap(InnerGrid);
var green = RunningApp.WaitForElement(OuterGrid);
RunningApp.TapCoordinates(green[0].Rect.CenterX, green[0].Rect.Y + 20);
RunningApp.WaitForElement(Success);
}
#endif
}
}

Просмотреть файл

@ -328,6 +328,10 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue2681.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2858.xaml.cs">
<DependentUpon>Issue2858.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue2929.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2983.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2963.cs" />
@ -929,4 +933,10 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue2858.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

Просмотреть файл

@ -1156,7 +1156,7 @@ namespace Xamarin.Forms.Platform.Android
internal class DefaultRenderer : VisualElementRenderer<View>
{
bool _notReallyHandled;
public bool NotReallyHandled { get; private set; }
IOnTouchListener _touchListener;
[Obsolete("This constructor is obsolete as of version 2.5. Please use DefaultRenderer(Context) instead.")]
@ -1173,7 +1173,7 @@ namespace Xamarin.Forms.Platform.Android
internal void NotifyFakeHandling()
{
_notReallyHandled = true;
NotReallyHandled = true;
}
public override bool OnTouchEvent(MotionEvent e)
@ -1216,11 +1216,11 @@ namespace Xamarin.Forms.Platform.Android
// it then knows to ignore that result and return false/unhandled. This allows the event to propagate up the tree.
#endregion
_notReallyHandled = false;
NotReallyHandled = false;
var result = base.DispatchTouchEvent(e);
if (result && _notReallyHandled)
if (result && NotReallyHandled)
{
// If the child control returned true from its touch event handler but signalled that it was a fake "true", then we
// don't consider the event truly "handled" yet.

Просмотреть файл

@ -9,13 +9,13 @@ namespace Xamarin.Forms.Platform.Android
public bool HandleMotionEvent(IViewParent parent, MotionEvent motionEvent)
{
if (_isInViewCell || _element.InputTransparent || motionEvent.Action == MotionEventActions.Cancel)
if (_isInViewCell || motionEvent.Action == MotionEventActions.Cancel)
{
return false;
}
var renderer = parent as Platform.DefaultRenderer;
if (renderer == null)
if (renderer == null || ShouldPassThroughElement())
{
return false;
}
@ -40,5 +40,45 @@ namespace Xamarin.Forms.Platform.Android
// we don't fake handle the events because ListView needs them for row selection
_isInViewCell = element.IsInViewCell();
}
bool ShouldPassThroughElement()
{
if (_element is Layout layout)
{
if (!layout.InputTransparent)
{
// If the layout is not input transparent, then the event should not pass through it
return false;
}
if (layout.CascadeInputTransparent)
{
// This is a layout, and it's transparent, and all its children are transparent, then the event
// can just pass through
return true;
}
if (Platform.GetRenderer(_element) is Platform.DefaultRenderer renderer)
{
// If the event is being bubbled up from a child which is not inputtransparent, we do not want
// it to be passed through (just up the tree)
if (renderer.NotReallyHandled)
{
return false;
}
}
// This event isn't being bubbled up by a non-InputTransparent child layout
return true;
}
if (_element.InputTransparent)
{
// This is not a layout and it's transparent; the event can just pass through
return true;
}
return false;
}
}
}

Просмотреть файл

@ -21,7 +21,7 @@ namespace Xamarin.Forms.Platform.Android
string _defaultContentDescription;
bool? _defaultFocusable;
string _defaultHint;
bool _inputTransparentInherited = true;
bool _cascadeInputTransparent = true;
VisualElementPackager _packager;
PropertyChangedEventHandler _propertyChangeHandler;
@ -52,7 +52,7 @@ namespace Xamarin.Forms.Platform.Android
public override bool DispatchTouchEvent(MotionEvent e)
{
if (InputTransparent && _inputTransparentInherited)
if (InputTransparent && _cascadeInputTransparent)
{
// If the Element is InputTransparent, this ViewGroup will be marked InputTransparent
// If we're InputTransparent and our transparency should be applied to our child controls,
@ -339,7 +339,7 @@ namespace Xamarin.Forms.Platform.Android
return;
}
_inputTransparentInherited = layout.CascadeInputTransparent;
_cascadeInputTransparent = layout.CascadeInputTransparent;
}
protected void SetPackager(VisualElementPackager packager)