summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Smith <jason.smith@xamarin.com>2016-04-08 09:16:02 -0700
committerRui Marinho <me@ruimarinho.net>2016-04-08 17:16:02 +0100
commite9f655688586bc0eb4183507def7ed3f774b7c71 (patch)
tree24aaba13b7a44468bb0989055813b11fdb5baf6f
parent870f9c98ffccce86d80c6ff7a05de7238fc1b0e8 (diff)
downloadxamarin-forms-e9f655688586bc0eb4183507def7ed3f774b7c71.tar.gz
xamarin-forms-e9f655688586bc0eb4183507def7ed3f774b7c71.tar.bz2
xamarin-forms-e9f655688586bc0eb4183507def7ed3f774b7c71.zip
[A] Fix Bugzilla 40173 (#62)
* [A]Fix issue where Frame would block all tap gestures under it even if it didn't have one * [A]Make BoxView non-blocking inside a ListView only * Add UITest for bz40173 * Fix code review issues
-rw-r--r--Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla40173.cs110
-rw-r--r--Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems1
-rw-r--r--Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs63
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs22
4 files changed, 190 insertions, 6 deletions
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
new file mode 100644
index 00000000..422e363d
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla40173.cs
@@ -0,0 +1,110 @@
+using Xamarin.Forms.CustomAttributes;
+#if UITEST
+using NUnit.Framework;
+#endif
+
+namespace Xamarin.Forms.Controls
+{
+ [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";
+
+#if UITEST
+ [Test]
+ public void ButtonBlocked()
+ {
+ RunningApp.Tap(q => q.Marked(CantTouchButtonId));
+ RunningApp.WaitForNoElement(q => q.Text(CantTouchFailText));
+
+ RunningApp.Tap(q => q.Marked(CanTouchButtonId));
+ RunningApp.WaitForElement(q => q.Text(CanTouchSuccessText));
+
+ RunningApp.Tap(q => q.Marked(ListTapTarget));
+ RunningApp.WaitForElement(q => q.Text(ListTapSuccessText));
+ }
+#endif
+
+ 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;
+
+ var testGrid = new Grid
+ {
+ Children =
+ {
+ testButton,
+ new BoxView
+ {
+ Color = Color.Pink.MultiplyAlpha(0.5)
+ }
+ }
+ };
+
+ // BoxView over Button prevents Button click
+ var testButtonOk = new Button
+ {
+ Text = "Can Touch This",
+ AutomationId = CanTouchButtonId
+ };
+
+ testButtonOk.Clicked += (sender, args) => outputLabel.Text = CanTouchSuccessText;
+
+ var testGridOk = new Grid
+ {
+ Children =
+ {
+ testButtonOk,
+ new BoxView
+ {
+ 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)
+ }
+ }
+ }
+ };
+
+ return result;
+ });
+
+ testListView.ItemSelected += (sender, args) => outputLabel.Text = ListTapSuccessText;
+
+ Content = new StackLayout
+ {
+ Children = { outputLabel, testGrid, testGridOk, testListView }
+ };
+ }
+ }
+} \ 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 e13847bf..e7afb089 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
@@ -95,6 +95,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla39702.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Bugzilla40173.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CarouselAsync.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla34561.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla34727.cs" />
diff --git a/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs b/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs
index 7b544b25..232b6a6f 100644
--- a/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs
+++ b/Xamarin.Forms.Platform.Android/AppCompat/FrameRenderer.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
using System.ComponentModel;
using Android.Content;
using Android.Support.V4.View;
@@ -19,11 +21,13 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
float _defaultElevation = -1f;
+ bool _clickable;
bool _disposed;
Frame _element;
InnerGestureListener _gestureListener;
VisualElementPackager _visualElementPackager;
VisualElementTracker _visualElementTracker;
+ NotifyCollectionChangedEventHandler _collectionChangeHandler;
public FrameRenderer() : base(Forms.Context)
{
@@ -147,17 +151,20 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));
if (e.OldElement != null)
- e.OldElement.PropertyChanged -= OnElementPropertyChanged;
- else
{
- SetOnClickListener(this);
- SetOnTouchListener(this);
+ e.OldElement.PropertyChanged -= OnElementPropertyChanged;
+ UnsubscribeGestureRecognizers(e.OldElement);
}
if (e.NewElement != null)
{
if (_visualElementTracker == null)
{
+ SetOnClickListener(this);
+ SetOnTouchListener(this);
+
+ UpdateGestureRecognizers(true);
+
_visualElementTracker = new VisualElementTracker(this);
_visualElementPackager = new VisualElementPackager(this);
_visualElementPackager.Load();
@@ -166,6 +173,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
e.NewElement.PropertyChanged += OnElementPropertyChanged;
UpdateShadow();
UpdateBackgroundColor();
+ SubscribeGestureRecognizers(e.NewElement);
}
}
@@ -185,6 +193,11 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
}
}
+ void HandleGestureRecognizerCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
+ {
+ UpdateGestureRecognizers();
+ }
+
void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == Frame.HasShadowProperty.PropertyName)
@@ -193,12 +206,54 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
UpdateBackgroundColor();
}
+ void SubscribeGestureRecognizers(VisualElement element)
+ {
+ var view = element as View;
+ if (view == null)
+ return;
+
+ if (_collectionChangeHandler == null)
+ _collectionChangeHandler = HandleGestureRecognizerCollectionChanged;
+
+ var observableCollection = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
+ observableCollection.CollectionChanged += _collectionChangeHandler;
+ }
+
+ void UnsubscribeGestureRecognizers(VisualElement element)
+ {
+ var view = element as View;
+ if (view == null || _collectionChangeHandler == null)
+ return;
+
+ var observableCollection = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
+ observableCollection.CollectionChanged -= _collectionChangeHandler;
+ }
+
void UpdateBackgroundColor()
{
Color bgColor = Element.BackgroundColor;
SetCardBackgroundColor(bgColor.IsDefault ? AColor.White : bgColor.ToAndroid());
}
+ void UpdateClickable(bool force = false)
+ {
+ var view = Element as View;
+ if (view == null)
+ return;
+
+ bool newValue = view.ShouldBeMadeClickable();
+ if (force || _clickable != newValue)
+ Clickable = newValue;
+ }
+
+ void UpdateGestureRecognizers(bool forceClick = false)
+ {
+ if (Element == null)
+ return;
+
+ UpdateClickable(forceClick);
+ }
+
void UpdateShadow()
{
float elevation = _defaultElevation;
diff --git a/Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs
index 725829f4..b29dd760 100644
--- a/Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs
+++ b/Xamarin.Forms.Platform.Android/Renderers/BoxRenderer.cs
@@ -5,6 +5,8 @@ namespace Xamarin.Forms.Platform.Android
{
public class BoxRenderer : VisualElementRenderer<BoxView>
{
+ bool _isInViewCell;
+
public BoxRenderer()
{
AutoPackage = false;
@@ -12,13 +14,29 @@ namespace Xamarin.Forms.Platform.Android
public override bool OnTouchEvent(MotionEvent e)
{
- base.OnTouchEvent(e);
- return !Element.InputTransparent;
+ if (base.OnTouchEvent(e))
+ return true;
+ return !Element.InputTransparent && !_isInViewCell;
}
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
+
+ if (e.NewElement != null)
+ {
+ var parent = e.NewElement.Parent;
+ while (parent != null)
+ {
+ if (parent is ViewCell)
+ {
+ _isInViewCell = true;
+ break;
+ }
+ parent = parent.Parent;
+ }
+ }
+
UpdateBackgroundColor();
}