我需要通过在 .NET MAUI 中向上或向下滚动来查看列表中的项目。我的列表中有一些使用 YoutubeApi 和 MediaElement 展示的视频。 (Tiktok、Instagram Reels 等)我想在这里做什么;

无论哪个项目当前可见,MediaElement.Play()该项目的 都应该起作用。无论如何我找不到合适的滚动事件。有人对此有什么想法吗?

我已经尝试了所有附加的方法来做到这一点,但没有成功。

4

  • 您尝试过吗?


    – 

  • 我希望它像卡片一样向上滑动时消失,向下拉时相关卡片又回来。或者像社交媒体应用程序一样。将无限滚动,但每次都会有一个项目视图。


    – 

  • 1
    您已经尝试过什么?您可以使用CollectionView来实现此目的,例如通过指定线性布局并使项目填充可用空间。然后,使用来控制滚动行为。


    – 


  • 非常感谢您的评论。我怎么错过了这个……


    – 


1 个回答
1

布局

CollectionView可以轻松实现这种布局和滚动行为

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
  x:Class="FullPageSnapCollection.MainPage"
  xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:fullPageSnapCollection="clr-namespace:FullPageSnapCollection"
  x:DataType="fullPageSnapCollection:MainViewModel">

  <Grid>

    <CollectionView
      HeightRequest="500"
      HorizontalOptions="Fill"
      VerticalOptions="Center"
      ItemsSource="{Binding Items}">

      <CollectionView.ItemsLayout>
        <LinearItemsLayout
          Orientation="Vertical"
          SnapPointsType="MandatorySingle"
          SnapPointsAlignment="Start" />
      </CollectionView.ItemsLayout>

      <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="fullPageSnapCollection:MyModel">
          <Grid
            HeightRequest="500"
            BackgroundColor="{Binding BackgroundColor}"
            HorizontalOptions="Fill">
            <Label
              Text="{Binding Name}"
              VerticalOptions="Center"
              HorizontalOptions="Center"
              FontSize="Title" />
          </Grid>
        </DataTemplate>
      </CollectionView.ItemTemplate>

    </CollectionView>

  </Grid>

</ContentPage>

您需要HeightRequestCollectionView以及DataTemplate中的布局提供,以便项目与CollectionView具有相同的高度。与捕捉点一起,这应该会产生您正在寻找的内容。我还为此创建了一个小型

如果您需要检测当前在(视图)中心可见的项目,您可以订阅该事件并从事件参数中Scrolled获取,如此所述。CenterItemIndex

自动播放/停止

为了播放MediaElement中托管的视频,我们需要使用一些小技巧。由于MediaElement不公开任何可绑定和可设置的属性,因此我们需要通过使用IsPlaying扩展该类来自行添加

public class ExtendedMediaElement : MediaElement
{
    public bool IsPlaying
    {
        get => (bool)GetValue(IsPlayingProperty);
        set => SetValue(IsPlayingProperty, value);
    }

    public static readonly BindableProperty IsPlayingProperty = BindableProperty.Create(nameof(IsPlaying), typeof(bool), typeof(ExtendedMediaElement), false, propertyChanged: OnIsPlayingPropertyChanged);

    private static void OnIsPlayingPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var mediaElement = (ExtendedMediaElement)bindable;

        if (newValue is true)
        {
            mediaElement.Play();
        }
        else
        {
            mediaElement.Stop();
        }
    }
}

现在,我们可以绑定到模型类的任何属性,通过更新属性来控制播放/停止功能。

接下来,我们需要向模型添加一个可观察属性,用于更新可绑定属性,例如:

public partial class MyModel(string videoUri) : ObservableObject
{
    public string VideoUri { get; } = videoUri;

    [ObservableProperty]
    private bool _isPlaying;
}

我们将不使用MediaElement ,而是使用ExtendedMediaElement并在添加到我们添加的可绑定属性的DataTemplate绑定中:IsPlaying

<DataTemplate x:DataType="fullPageSnapCollection:MyModel">
  <Grid
    HeightRequest="500"
    BackgroundColor="{Binding BackgroundColor}"
    HorizontalOptions="Fill">
    <fullPageSnapCollection:ExtendedMediaElement
      Aspect="Fill"
      Source="{Binding VideoUri}"
      ShouldAutoPlay="False"
      ShouldLoopPlayback="True"
      ShouldShowPlaybackControls="False"
      IsPlaying="{Binding IsPlaying}" />
  </Grid>
</DataTemplate>

最后但并非最不重要的一点是,我们现在可以使用CollectionViewScrolled事件的事件处理程序来更新页面代码隐藏中每个模型实例的属性,这将随后触发播放/停止功能:IsPlaying

public partial class MainPage : ContentPage
{
    private readonly MainViewModel _vm;

    public MainPage()
    {
        InitializeComponent();
        BindingContext = _vm = new MainViewModel();
    }

    private void ItemsView_OnScrolled(object? sender, ItemsViewScrolledEventArgs e)
    {
        var itemIndex = e.CenterItemIndex;

        _vm.Items[itemIndex].IsPlaying = true;

        foreach (var myModel in _vm.Items)
        {
            if (myModel != _vm.Items[itemIndex])
            {
                myModel.IsPlaying = false;
            }
        }
    }
}

3

  • 感谢您的回答。你太棒了。


    – 

  • 我需要通过滚动事件到达相当于 CenterItemIndex 的 MediaElement 并触发 MediaElement.Play()。您对此有什么想法吗?谢谢先生。 🙂


    – 


  • @Bestekarx 我已经更新了答案。


    –