Reputation: 57
I want to be able to swipe over the entire listview (not individual items in the listview) to perform a certain action. I have swipe gestures attached to a listview inside a Gridview. It works as expected in iOS, but on android the gestures are never triggered.
I've tried placing the gesture recognizers on the parent gridview for android only. This re-enables the feature, but only if the swipe includes the very edges of the screen (I assume due to the small padding I have that isn't taken up by the listview.) What can I do to make the behavior similar to ios?
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test.ViewModels"
mc:Ignorable="d"
x:Class="Test.Pages.ViewerPage"
x:Name="View">
<ContentView.BindingContext>
<local:ViewerViewModel />
</ContentView.BindingContext>
<Grid Padding="10, 0, 10, 0">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Padding="0, 5, 0, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Text="Back" IsVisible="{Binding ShowBackButton}" Clicked="OnBackTap" VerticalOptions="Center" Padding="10, 0, 0, 0">
</Button>
<Button Grid.Column="1" x:Name="Header" HorizontalOptions="Center" BorderColor="Black" TextColor="Black" VerticalOptions="Center" BorderWidth="1" Clicked="OnTitleTap">
</Button>
</Grid>
<ListView Grid.Row="1" x:Name="Viewer" HasUnevenRows="True" SeparatorVisibility="None" BackgroundColor="Green">
<ListView.GestureRecognizers>
<SwipeGestureRecognizer Direction="Left" Swiped="OnSwiped" />
<SwipeGestureRecognizer Direction="Right" Swiped="OnSwiped"/>
</ListView.GestureRecognizers>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="False">
<StackLayout>
<Label>
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding verse}" FontSize="18" TextColor="Gray"></Span>
<Span Text=" "></Span>
<Span Text="{Binding text}" FontSize="18" TextColor="Black"></Span>
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentView>
Upvotes: 1
Views: 2123
Reputation: 11
You can use SwipeView. Wrap your ListView inside SwipeView.
<SwipeView SwipeStarted="SwipeView_SwipeStarted" SwipeEnded="SwipeView_SwipeEnded" SwipeChanging="SwipeView_SwipeChanging">
<SwipeView.RightItems>
<SwipeItems Mode="Reveal | Execute">
<SwipeItem Text="Your Action" Command="{Binding SwipeRightToLeftCommand}" />
</SwipeItems>
</SwipeView.RightItems>
<SwipeView.LeftItems>
<SwipeItems Mode="Reveal | Execute">
<SwipeItem Text="Your Action" Command="{Binding SwipeLeftToRightCommand}" />
</SwipeItems>
</SwipeView.LeftItems>
<ListView>
.
. Your Complete ListView
.
</ListView>
</SwipeView>
To enable swiping Right to Left, use SwipeView.RightItems
and for Left to
Right, use SwipeView.LeftItems
. If you want swiping enabled in both
directions use both.
SwipeItems.Mode="Execute"
will execute the command
directly on swiping.
SwipeItems.Mode="Reveal"
will just reveal the
action button and you will have to click on it to execute the
Command.
SwipeView
is currently experimental, so
remember to add the following line in your MainActivity
class,
before calling Forms.Init
.
Forms.SetFlags("SwipeView_Experimental");
Upvotes: 0
Reputation: 9274
You can create an custom renderer listview.
public class MyListview:ListView
{
public event EventHandler SwipeLeft;
public event EventHandler SwipeRight;
public void OnSwipeLeft() =>
SwipeLeft?.Invoke(this, null);
public void OnSwipeRight() =>
SwipeRight?.Invoke(this, null);
}
In the android platform.
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using App3;
using App3.Droid;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(MyListview), typeof(MyListviewRenderer))]
namespace App3.Droid
{
class MyListviewRenderer : ListViewRenderer
{
readonly CustomGestureListener _listener;
readonly GestureDetector _detector;
public MyListviewRenderer(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(ElementChangedEventArgs<Xamarin.Forms.ListView> 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) =>
((MyListview)Element).OnSwipeLeft();
void HandleOnSwipeRight(object sender, EventArgs e) =>
((MyListview)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);
}
}
}
Then you can use it in xaml.
<app3:MyListview x:Name="gi">
<app3:MyListview.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>mono</x:String>
<x:String>monodroid</x:String>
<x:String>monotouch</x:String>
<x:String>monorail</x:String>
<x:String>monodevelop</x:String>
<x:String>monotone</x:String>
<x:String>monopoly</x:String>
<x:String>monomodal</x:String>
<x:String>mononucleosis</x:String>
</x:Array>
</app3:MyListview.ItemsSource>
</app3:MyListview>
Here is layot backgroundcode,
public MainPage()
{
InitializeComponent();
gi.SwipeLeft += (s, e) =>
DisplayAlert("Gesture Info", "Swipe Left Detected", "OK");
gi.SwipeRight += (s, e) =>
DisplayAlert("Gesture Info", "Swipe Right Detected", "OK");
}
Here is running gif.
Upvotes: 2