Skip to content

Commit 0133c16

Browse files
added DisallowTouchThresholdProperty
#35
1 parent 8bcc042 commit 0133c16

File tree

7 files changed

+90
-164
lines changed

7 files changed

+90
-164
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ RegularAnimationDuration | `int` | 0 | The duration of animation by applying Reg
140140
RegularAnimationEasing | `Easing` | null | The easing of animation by applying RegularOpacity and/or RegularBackgroundColor and/or RegularScale
141141
RippleCount | `int` | 0 | This property allows to set ripple of animation (Pressed/Regular animation loop). '**0**: disabled'; '**-1**: infinite loop'; '**1, 2, 3 ... n**: Ripple's interations'
142142
IsToggled | `bool?` | null | This property allows to achieve "switch" behavior. **null** means that feature is disabled and view will return to inital state after touch releasing
143+
DisallowTouchThreshold | `int` | 0 | Movement threshold for considering **android** touch as canceled
143144
NativeAnimation | `bool` | false | If native platform touch feedback animations are present (Tilt on UWP, Ripple on Android)
144145
NativeAnimationColor | `Color` | Color.Default | The color used for the native touch feedback animation
145146
NativeAnimationRadius | `int` | -1 | The radius of the native ripple animation on Android

TouchEffect.Droid/PlatformTouchEff.cs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
using System;
1111
using Android.Graphics.Drawables;
1212
using Android.Widget;
13-
using Android.Animation;
14-
using Android.Graphics;
1513
using Color = Android.Graphics.Color;
1614
using Android.Content.Res;
15+
using static System.Math;
1716

1817
[assembly: ResolutionGroupName(nameof(TouchEffect))]
1918
[assembly: ExportEffect(typeof(PlatformTouchEff), nameof(TouchEff))]
@@ -30,6 +29,9 @@ public static void Preserve() { }
3029
private RippleDrawable _ripple;
3130
private FrameLayout _viewOverlay;
3231
private AView View => Control ?? Container;
32+
private float _startX;
33+
private float _startY;
34+
private bool _canceled;
3335

3436
protected override void OnAttached()
3537
{
@@ -73,6 +75,7 @@ protected override void OnDetached()
7375
{
7476
_effect.Control = null;
7577
_effect = null;
78+
Container.LayoutChange -= LayoutChange;
7679
if (Container != null)
7780
{
7881
Container.Touch -= OnTouch;
@@ -81,7 +84,6 @@ protected override void OnDetached()
8184
{
8285
Control.Touch -= OnTouch;
8386
}
84-
Container.LayoutChange -= LayoutChange;
8587
if (_viewOverlay != null)
8688
{
8789
Container.RemoveView(_viewOverlay);
@@ -104,18 +106,37 @@ private void OnTouch(object sender, AView.TouchEventArgs e)
104106
switch (e.Event.ActionMasked)
105107
{
106108
case MotionEventActions.Down:
109+
_canceled = false;
110+
_startX = e.Event.GetX();
111+
_startY = e.Event.GetY();
107112
Element.GetTouchEff().HandleTouch(TouchStatus.Started);
108113
StartRipple(e.Event.GetX(), e.Event.GetY());
114+
if (_effect.DisallowTouchThreshold > 0)
115+
{
116+
Container.Parent?.RequestDisallowInterceptTouchEvent(true);
117+
}
109118
break;
110119
case MotionEventActions.Up:
111-
Element.GetTouchEff().HandleTouch(Element.GetTouchEff().Status == TouchStatus.Started ? TouchStatus.Completed : TouchStatus.Canceled);
112-
EndRipple();
120+
HandleEnd(Element.GetTouchEff().Status == TouchStatus.Started ? TouchStatus.Completed : TouchStatus.Canceled);
113121
break;
114122
case MotionEventActions.Cancel:
115-
Element.GetTouchEff().HandleTouch(TouchStatus.Canceled);
116-
EndRipple();
123+
HandleEnd(TouchStatus.Canceled);
117124
break;
118125
case MotionEventActions.Move:
126+
if(_canceled)
127+
{
128+
return;
129+
}
130+
var diffX = Abs(e.Event.GetX() - _startX) / Container.Context.Resources.DisplayMetrics.Density;
131+
var diffY = Abs(e.Event.GetY() - _startY) / Container.Context.Resources.DisplayMetrics.Density;
132+
var maxDiff = Max(diffX, diffY);
133+
var disallowTouchThreshold = _effect.DisallowTouchThreshold;
134+
if (disallowTouchThreshold > 0 && maxDiff > disallowTouchThreshold)
135+
{
136+
HandleEnd(TouchStatus.Canceled);
137+
_canceled = true;
138+
return;
139+
}
119140
var view = sender as AView;
120141
var screenPointerCoords = new Xamarin.Forms.Point(view.Left + e.Event.GetX(), view.Top + e.Event.GetY());
121142
var viewRect = new Rectangle(view.Left, view.Top, view.Right - view.Left, view.Bottom - view.Top);
@@ -150,7 +171,21 @@ private void OnTouch(object sender, AView.TouchEventArgs e)
150171
}
151172
}
152173

153-
public bool StartRipple(float x, float y)
174+
private void HandleEnd(TouchStatus status)
175+
{
176+
if(_canceled)
177+
{
178+
return;
179+
}
180+
if (_effect.DisallowTouchThreshold > 0)
181+
{
182+
Container.Parent?.RequestDisallowInterceptTouchEvent(false);
183+
}
184+
Element.GetTouchEff().HandleTouch(status);
185+
EndRipple();
186+
}
187+
188+
private bool StartRipple(float x, float y)
154189
{
155190
if (_effect.NativeAnimation && _viewOverlay.Background is RippleDrawable)
156191
{
@@ -162,7 +197,7 @@ public bool StartRipple(float x, float y)
162197
return false;
163198
}
164199

165-
public bool EndRipple()
200+
private bool EndRipple()
166201
{
167202
if (_viewOverlay != null && _viewOverlay.Pressed)
168203
{

TouchEffect/Interfaces/ITouchEff.cs

Lines changed: 0 additions & 131 deletions
This file was deleted.

TouchEffect/TouchEff.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@
88
using System;
99
using System.Threading.Tasks;
1010
using System.Threading;
11-
using TouchEffect.Interfaces;
1211

1312
namespace TouchEffect
1413
{
15-
public class TouchEff : RoutingEffect, ITouchEff
14+
public class TouchEff : RoutingEffect
1615
{
1716
private readonly TouchVisualManager _visualManager;
1817
private VisualElement _control;
@@ -23,7 +22,7 @@ public TouchEff() : base($"{nameof(TouchEffect)}.{nameof(TouchEff)}")
2322
StateChanged += (sender, args) => ForceUpdateState();
2423
}
2524

26-
internal TouchEff(Func<ITouchEff, TouchState, int, CancellationToken, Task> animationTaskGetter) : this()
25+
internal TouchEff(Func<TouchEff, TouchState, int, CancellationToken, Task> animationTaskGetter) : this()
2726
=> _visualManager.SetCustomAnimationTask(animationTaskGetter);
2827

2928
public event TEffectStatusChangedHandler StatusChanged;
@@ -385,6 +384,15 @@ internal TouchEff(Func<ITouchEff, TouchState, int, CancellationToken, Task> anim
385384
bindable.GetTouchEff()?.ForceUpdateState(false);
386385
});
387386

387+
/// <summary>
388+
/// Android only
389+
/// </summary>
390+
public static readonly BindableProperty DisallowTouchThresholdProperty = BindableProperty.CreateAttached(
391+
nameof(DisallowTouchThreshold),
392+
typeof(int),
393+
typeof(TouchEff),
394+
default(int));
395+
388396
public static readonly BindableProperty NativeAnimationProperty = BindableProperty.CreateAttached(
389397
nameof(NativeAnimation),
390398
typeof(bool),
@@ -651,6 +659,18 @@ public static void SetRippleCount(BindableObject bindable, int value)
651659
public static void SetIsToggled(BindableObject bindable, bool? value)
652660
=> bindable.SetValue(IsToggledProperty, value);
653661

662+
/// <summary>
663+
/// Android only
664+
/// </summary>
665+
public static int GetDisallowTouchThreshold(BindableObject bindable)
666+
=> (int)bindable.GetValue(DisallowTouchThresholdProperty);
667+
668+
/// <summary>
669+
/// Android only
670+
/// </summary>
671+
public static void SetDisallowTouchThreshold(BindableObject bindable, int value)
672+
=> bindable.SetValue(DisallowTouchThresholdProperty, value);
673+
654674
public static bool GetNativeAnimation(BindableObject bindable)
655675
=> (bool)bindable.GetValue(NativeAnimationProperty);
656676

@@ -697,6 +717,11 @@ public HoverState HoverState
697717
set => SetHoverState(Control, value);
698718
}
699719

720+
/// <summary>
721+
/// Android only
722+
/// </summary>
723+
public int DisallowTouchThreshold => GetDisallowTouchThreshold(Control);
724+
700725
public bool NativeAnimation => GetNativeAnimation(Control);
701726

702727
public Color NativeAnimationColor => GetNativeAnimationColor(Control);

TouchEffect/TouchEffect.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
<Folder Include="EventArgs\" />
1313
<Folder Include="Enums\" />
1414
<Folder Include="Extensions\" />
15-
<Folder Include="Interfaces\" />
1615
</ItemGroup>
1716
<ItemGroup>
1817
<Compile Remove="Controls\TouchColorView.cs" />

TouchEffect/TouchImage.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using Xamarin.Forms;
44
using System.Threading.Tasks;
55
using System.Threading;
6-
using TouchEffect.Interfaces;
76
using System.ComponentModel;
87

98
namespace TouchEffect
@@ -127,7 +126,7 @@ public bool ShouldSetImageOnAnimationEnd
127126
set => SetValue(ShouldSetImageOnAnimationEndProperty, value);
128127
}
129128

130-
private async Task GetAnimationTask(ITouchEff sender, TouchState state, int duration, CancellationToken token)
129+
private async Task GetAnimationTask(TouchEff sender, TouchState state, int duration, CancellationToken token)
131130
{
132131
var regularBackgroundImageSource = RegularBackgroundImageSource;
133132
var pressedBackgroundImageSource = PressedBackgroundImageSource;

0 commit comments

Comments
 (0)