Xamarin Forms - 使用XAML实现Listview视图单元格左右滑动事件

3

我需要编写代码(或XAML)来处理listview视图单元格的左右滑动手势。

向左滑动时,从listview(或observable collection)列表中删除记录,并更新剩余的列表项。向右滑动时,保存记录。

2个回答

1

我已经用C#代码完成了它,你可以在XAML上尝试。

首先,你应该使用手势构建滑动组件。

SwipeGestureGrid.cs

public class SwipeGestureGrid : Grid
    {
        #region Private Member
        private double _gestureX { get; set; }
        private double _gestureY { get; set; }
        private bool IsSwipe { get; set; }
        #endregion
        #region Public Member
        #region Events
        #region Tapped
        public event EventHandler Tapped;
        protected void OnTapped(EventArgs e)
        {
            if (Tapped != null)
                Tapped(this, e);
        }
        #endregion
        #region SwipeUP
        public event EventHandler SwipeUP;
        protected void OnSwipeUP(EventArgs e)
        {
            if (SwipeUP != null)
                SwipeUP(this, e);
        }
        #endregion
        #region SwipeDown
        public event EventHandler SwipeDown;
        protected void OnSwipeDown(EventArgs e)
        {
            if (SwipeDown != null)
                SwipeDown(this, e);
        }
        #endregion
        #region SwipeRight
        public event EventHandler SwipeRight;
        protected void OnSwipeRight(EventArgs e)
        {
            if (SwipeRight != null)
                SwipeRight(this, e);
        }
        #endregion
        #region SwipeLeft
        public event EventHandler SwipeLeft;
        protected void OnSwipeLeft(EventArgs e)
        {
            if (SwipeLeft != null)
                SwipeLeft(this, e);
        }
        #endregion
        #endregion
        public double Height
        {
            get
            {
                return HeightRequest;
            }
            set
            {
                HeightRequest = value;
            }
        }
        public double Width
        {
            get
            {
                return WidthRequest;
            }
            set
            {
                WidthRequest = value;
            }
        }
        #endregion
        public SwipeGestureGrid()
        {
            PanGestureRecognizer panGesture = new PanGestureRecognizer();
            panGesture.PanUpdated += PanGesture_PanUpdated;

            TapGestureRecognizer tapGesture = new TapGestureRecognizer();
            tapGesture.Tapped += TapGesture_Tapped;

            GestureRecognizers.Add(panGesture);
            GestureRecognizers.Add(tapGesture);
        }

        private void TapGesture_Tapped(object sender, EventArgs e)
        {
            try
            {
                if (!IsSwipe)
                    OnTapped(null);

                IsSwipe = false;
            }
            catch (Exception ex)
            {

            }
        }

        private void PanGesture_PanUpdated(object sender, PanUpdatedEventArgs e)
        {
            try
            {
                switch (e.StatusType)
                {
                    case GestureStatus.Running:
                        {
                            _gestureX = e.TotalX;
                            _gestureY = e.TotalY;
                        }
                        break;
                    case GestureStatus.Completed:
                        {
                            IsSwipe = true;
                            //Debug.WriteLine("{0}  {1}", _gestureX, _gestureY);
                            if (Math.Abs(_gestureX) > Math.Abs(_gestureY))
                            {
                                if (_gestureX > 0)
                                {
                                    OnSwipeRight(null);
                                }
                                else
                                {
                                    OnSwipeLeft(null);
                                }
                            }
                            else
                            {
                                if (_gestureY > 0)
                                {
                                    OnSwipeDown(null);
                                }
                                else
                                {
                                    OnSwipeUP(null);
                                }
                            }
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
            }
        }
    }

下一步在ListView中使用DataTemplate并为GestureComponent附加事件。

Page.cs

ListView lsvData = new ListView()
        {
            VerticalOptions = LayoutOptions.Fill,
            HorizontalOptions = LayoutOptions.Fill,
            BackgroundColor = Color.White,
            HasUnevenRows = true,
        };
        List<string> lstData = new List<string>();
        public Pages()
        {
            #region DataTemplate
            DataTemplate ListDataTemplate = new DataTemplate(() =>
            {
                #region DataArea of Template
                SwipeGestureGrid gridData = new SwipeGestureGrid()
                {
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand,
                    HeightRequest = 60,
                    RowDefinitions =
                        {
                            new RowDefinition { },
                        },
                    ColumnDefinitions =
                        {
                            new ColumnDefinition { },
                        }
                };
                #endregion
                #region Base of Template
                Grid gridBase = new Grid()
                {
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand,
                    HeightRequest = 60,
                    RowDefinitions =
                    {
                        new RowDefinition { },
                    },
                    ColumnDefinitions =
                    {
                        new ColumnDefinition { },                                                   //Put Cells Data here
                        new ColumnDefinition { Width = new GridLength(0, GridUnitType.Absolute)},   //Button for Cells here
                    },
                };
                #endregion
                Label lblText = new Label
                {
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand,
                    FontAttributes = FontAttributes.Bold,
                    VerticalTextAlignment = TextAlignment.End,
                    TextColor = Color.Black,
                    BackgroundColor = Color.Silver,
                    LineBreakMode = LineBreakMode.TailTruncation,
                    FontSize = 18,
                };
                lblText.SetBinding(Label.TextProperty, ".");

                ImageButton btnCellDelete = new ImageButton() { Source = "Delete" };

                gridData.Children.Add(lblText, 0, 0);

                gridBase.Children.Add(gridData, 0, 0);
                gridBase.Children.Add(btnCellDelete, 1, 0);

                gridData.SwipeLeft += GridTemplate_SwipeLeft;
                gridData.SwipeRight += GridTemplate_SwipeRight; ;
                gridData.Tapped += GridTemplate_Tapped; ;
                btnCellDelete.Clicked += BtnCellDelete_Clicked; ;

                return new ViewCell
                {
                    View = gridBase,
                    Height = 60,
                };
            });

            #endregion
            for (int i = 1; i <= 100; i++)
            {
                lstData.Add(i.ToString());
            }
            lsvData.ItemTemplate = ListDataTemplate;
            lsvData.ItemsSource = lstData;
            Content = lsvData;
        }

事件

向左滑动以显示删除按钮

private void GridTemplate_SwipeLeft(object sender, EventArgs e)
    {
        try
        {
            if (sender is SwipeGestureGrid)
            {
                var templateGrid = ((SwipeGestureGrid)sender).Parent;
                if (templateGrid != null && templateGrid is Grid)
                {
                    var CellTemplateGrid = (Grid)templateGrid;
                    CellTemplateGrid.ColumnDefinitions[1].Width = new GridLength(60, GridUnitType.Absolute);
                }
            }

        }
        catch (Exception ex)
        {

        }
    }

向右滑动以隐藏删除按钮。
private void GridTemplate_SwipeRight(object sender, EventArgs e)
    {
        try
        {
            if (sender is SwipeGestureGrid)
            {
                var templateGrid = ((SwipeGestureGrid)sender).Parent;
                if (templateGrid != null && templateGrid is Grid)
                {
                    var CellTemplateGrid = (Grid)templateGrid;
                    CellTemplateGrid.ColumnDefinitions[1].Width = new GridLength(0, GridUnitType.Absolute);
                }
            }
        }
        catch (Exception ex)
        {

        }
    }

删除按钮点击事件

private void BtnCellDelete_Clicked(object sender, EventArgs e)
    {
        try
        {
            if (sender is ImageButton)
            {
                var templateGrid = ((ImageButton)sender);
                //templateGrid.Parent = gridBase
                //templateGrid.Parent.Parent = cell
                if (templateGrid.Parent != null && templateGrid.Parent.Parent != null && templateGrid.Parent.Parent.BindingContext != null && templateGrid.Parent.Parent.BindingContext is string)
                {
                    var deletedate = templateGrid.Parent.Parent.BindingContext as string;
                    lstData.RemoveAll(f => f == deletedate);
                    lsvData.ItemsSource = null;
                    lsvData.ItemsSource = lstData;
                }
            }
        }
        catch (Exception ex)
        {

        }
    }

源代码可以在我的Github中找到https://github.com/act70255/ListViewSwipeGesture


1
@Vince 看起来他已经认真听取了你的建议。评论不再需要了。 - ruffin

1
我已经修改了James Lin的类,使其基于命令,以便更符合MVVM的要求。
public class SwipeGestureGrid : Grid
{
    public static readonly BindableProperty SwipeLeftCommandProperty = 
        BindableProperty.Create("SwipeLeftCommand", typeof(ICommand), typeof(ICommand), null);
    public static readonly BindableProperty SwipeRightCommandProperty =
        BindableProperty.Create("SwipeRightCommand", typeof(ICommand), typeof(ICommand), null);
    public static readonly BindableProperty SwipeUpCommandProperty =
        BindableProperty.Create("SwipeUpCommand", typeof(ICommand), typeof(ICommand), null);
    public static readonly BindableProperty SwipeDownCommandProperty =
        BindableProperty.Create("SwipeDownCommand", typeof(ICommand), typeof(ICommand), null);
    public static readonly BindableProperty TappedCommandProperty =
        BindableProperty.Create("TappedCommand", typeof(ICommand), typeof(ICommand), null);

    private double _gestureStartX;
    private double _gestureStartY;
    private double _gestureDistanceX;
    private double _gestureDistanceY;

    public double GestureStartX
    {
        get => _gestureStartX;
        private set
        {
            _gestureStartX = value;
            OnPropertyChanged();
        }
    }

    public double GestureStartY
    {
        get => _gestureStartY;
        private set
        {
            _gestureStartY = value;
            OnPropertyChanged();
        }
    }

    private bool IsSwipe { get; set; }

    public ICommand TappedCommand
    {
        get => (ICommand) GetValue(TappedCommandProperty);
        set => SetValue(TappedCommandProperty, value);
    }
    public ICommand SwipeLeftCommand
    {
        get => (ICommand)GetValue(SwipeLeftCommandProperty);
        set => SetValue(SwipeLeftCommandProperty, value);
    }
    public ICommand SwipeRightCommand
    {
        get => (ICommand)GetValue(SwipeRightCommandProperty);
        set => SetValue(SwipeRightCommandProperty, value);
    }
    public ICommand SwipeUpCommand
    {
        get => (ICommand)GetValue(SwipeUpCommandProperty);
        set => SetValue(SwipeUpCommandProperty, value);
    }
    public ICommand SwipeDownCommand
    {
        get => (ICommand)GetValue(SwipeDownCommandProperty);
        set => SetValue(SwipeDownCommandProperty, value);
    }

    public SwipeGestureGrid()
    {
        var panGesture = new PanGestureRecognizer();
        panGesture.PanUpdated += PanGesture_PanUpdated;

        var tapGesture = new TapGestureRecognizer();
        tapGesture.Tapped += TapGesture_Tapped;

        GestureRecognizers.Add(panGesture);
        GestureRecognizers.Add(tapGesture);
    }

    private void TapGesture_Tapped(object sender, EventArgs e)
    {
        try
        {
            if (!IsSwipe)
                TappedCommand?.Execute(this);

            IsSwipe = false;
        }
        catch (Exception ex)
        {

        }
    }

    private void PanGesture_PanUpdated(object sender, PanUpdatedEventArgs e)
    {
            switch (e.StatusType)
            {
                case GestureStatus.Started:
                    {
                        GestureStartX = e.TotalX;
                        GestureStartY = e.TotalY;
                    }
                    break;
                case GestureStatus.Running:
                    {
                        _gestureDistanceX = e.TotalX;
                        _gestureDistanceY = e.TotalY;
                    }
                    break;
                case GestureStatus.Completed:
                    {
                        IsSwipe = true;

                        if (Math.Abs(_gestureDistanceX) > Math.Abs(_gestureDistanceY))
                        {
                            if (_gestureDistanceX > 0)
                            {
                                SwipeRightCommand?.Execute(this);
                            }
                            else
                            {
                                SwipeLeftCommand?.Execute(null);
                            }
                        }
                        else
                        {
                            if (_gestureDistanceY > 0)
                            {
                                SwipeDownCommand?.Execute(null);
                            }
                            else
                            {
                                SwipeUpCommand?.Execute(null);
                            }
                        }
                    }
                    break;
            }
    }
}

我们将其用于可滚动列表视图的数据模板中:
<DataTemplate x:Key="ThreeRowTemplateTemplate" x:DataType="{x:Type tracking:TrackingItem}">
            <ViewCell>
                <controls:SwipeGestureGrid ColumnSpacing="0" RowSpacing="0" SwipeLeftCommand="{Binding SwipeLeftCommand}">

在这种情况下,bound指令必须放在TrackingItem上。它似乎运行得很好,但有时检测会有些不稳定 - 可能与ScrollableListView的手势处理有冲突。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接