Hello,
Welcome to our Microsoft Q&A platform!
I set set a height for StackLayout
that goes beyond the screen, the Gesture
works on iOS but not on Android. If you put the stacklayout inside a ScrollView
, the swipe action will conflict with the scroll action. There is a workaround, you have to use Custom Renderers, refer to the following code:
Customize a scrollview in your Xamarin project
using System;
using Xamarin.Forms;
namespace ScrollViewDemo
{
public class GestureScrollView : ScrollView
{
public event EventHandler SwipeLeft;
public event EventHandler SwipeRight;
public void OnSwipeLeft() =>
SwipeLeft?.Invoke(this, null);
public void OnSwipeRight() =>
SwipeRight?.Invoke(this, null);
}
}
Custom Renderer in Android Project
[assembly: ExportRenderer(typeof(GestureScrollView), typeof(GestureScrollViewRenderer))]
namespace ScrollViewDemo.Droid
{
public class GestureScrollViewRenderer : ScrollViewRenderer
{
readonly CustomGestureListener _listener;
readonly GestureDetector _detector;
public GestureScrollViewRenderer(Context context) : base(context)
{
_listener = new CustomGestureListener();
_detector = new GestureDetector(context, _listener);
}
public override bool DispatchTouchEvent(MotionEvent e)
{
if (_detector != null)
{
_detector.OnTouchEvent(e);
base.DispatchTouchEvent(e);
return true;
}
return base.DispatchTouchEvent(e);
}
public override bool OnTouchEvent(MotionEvent ev)
{
base.OnTouchEvent(ev);
if (_detector != null)
return _detector.OnTouchEvent(ev);
return false;
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement == null)
{
_listener.OnSwipeLeft -= HandleOnSwipeLeft;
_listener.OnSwipeRight -= HandleOnSwipeRight;
}
if (e.OldElement == null)
{
_listener.OnSwipeLeft += HandleOnSwipeLeft;
_listener.OnSwipeRight += HandleOnSwipeRight;
}
}
void HandleOnSwipeLeft(object sender, EventArgs e) =>
((GestureScrollView)Element).OnSwipeLeft();
void HandleOnSwipeRight(object sender, EventArgs e) =>
((GestureScrollView)Element).OnSwipeRight();
}
public class CustomGestureListener : GestureDetector.SimpleOnGestureListener
{
static readonly int SWIPE_THRESHOLD = 100;
static readonly int SWIPE_VELOCITY_THRESHOLD = 100;
MotionEvent mLastOnDownEvent;
public event EventHandler OnSwipeLeft;
public event EventHandler OnSwipeRight;
public override bool OnDown(MotionEvent e)
{
mLastOnDownEvent = e;
return true;
}
public override bool OnFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
if (e1 == null)
e1 = mLastOnDownEvent;
float diffY = e2.GetY() - e1.GetY();
float diffX = e2.GetX() - e1.GetX();
if (Math.Abs(diffX) > Math.Abs(diffY))
{
if (Math.Abs(diffX) > SWIPE_THRESHOLD && Math.Abs(velocityX) > SWIPE_VELOCITY_THRESHOLD)
{
if (diffX > 0)
OnSwipeRight?.Invoke(this, null);
else
OnSwipeLeft?.Invoke(this, null);
}
}
return base.OnFling(e1, e2, velocityX, velocityY);
}
}
}
Use this scrollview in XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ScrollViewDemo"
x:Class="ScrollViewDemo.MainPage">
<local:GestureScrollView x:Name="MyGestureScrollView">
<ScrollView.GestureRecognizers>
<SwipeGestureRecognizer x:Name="SwipeLeft" Direction="Left" Swiped="SwipeLeft_Swiped"></SwipeGestureRecognizer>
<SwipeGestureRecognizer x:Name="SwipeRight" Direction="Right" Swiped ="SwipeRight_Swiped"></SwipeGestureRecognizer>
<PinchGestureRecognizer x:Name="Pinch" PinchUpdated="Pinch_PinchUpdated"></PinchGestureRecognizer>
</ScrollView.GestureRecognizers>
<Frame >
<StackLayout BackgroundColor="Yellow"
HeightRequest="1000"
>
</StackLayout>
</Frame>
</local:GestureScrollView>
</ContentPage>
CodeBehind
namespace ScrollViewDemo
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
MyGestureScrollView.SwipeLeft += (s, e) =>
DisplayAlert("Gesture Info", "Swipe Left Detected", "OK");
MyGestureScrollView.SwipeRight += (s, e) =>
DisplayAlert("Gesture Info", "Swipe Right Detected", "OK");
}
// It works on iOS
private void SwipeLeft_Swiped(object sender, SwipedEventArgs e)
{
Console.WriteLine("{SwipeLeft_Swiped---------------}");
}
private void SwipeRight_Swiped(object sender, SwipedEventArgs e)
{
Console.WriteLine("{SwipeRight_Swiped---------------}");
}
private void Pinch_PinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
Console.WriteLine("{Pinch_PinchUpdated---------------}");
}
}
}
About the pinch and zoom action, you could refer to
https://stackoverflow.com/questions/37289517/xamarin-forms-label-in-a-scroll-view-pinch-and-zoom-android
https://social.msdn.microsoft.com/Forums/en-US/2474e9eb-ee00-45cf-86ff-af9554bc2011/is-it-possible-to-achieve-pinch-zoom-on-scrollview-control?forum=xamarincrossplatform
Best Regards,
Wenyan Zhang
If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.