如何防止处理多个 GestureRecognizers.Tapped 事件

How to prevent multiple GestureRecognizers.Tapped events from being handled

提问人:Zerachiel 提问时间:11/7/2023 更新时间:11/8/2023 访问量:64

问:

所以我有这个 MAUI 应用程序,它允许用户在边框内点击以加载一些数据并打开另一个 ContentPage。为了处理用户的点击,我在边框中添加了一个 GestureRecognizer,如下所示:

<Border>
  <Border.GestureRecognizers>
    <TapGestureRecognizer Tapped="HandleBorderTapped"/>
  </Border.GestureRecognizers>
</Border>

该方法如下所示:HandleBorderTapped

private async void HandleBorderTapped(object sender, TappedEventArgs e)
{
  await LoadSomeDataAsync().ConfigureAwait(true);
  await Shell.Current.GoToAsync("Another ContentPage").ConfigureAwait(true);
}

现在,该方法需要几秒钟的时间,不耐烦的用户可能会在边框内多次点击。不幸的是,这会导致应用程序多次打开 ContentPage,从而导致包含许多 ContentPage 的导航堆栈。这是我想避免的。LoadSomeDataAsync

我尝试通过添加这样的布尔值来阻止第二次点击处理:

private volatile bool isLoading;

private async void HandleBorderTapped(object sender, TappedEventArgs e)
{
  if (isLoading)
    return;

  isLoading = true;

  await LoadSomeDataAsync().ConfigureAwait(true);
  await Shell.Current.GoToAsync("Another ContentPage").ConfigureAwait(true);

  isLoading = false;
}

这种帮助,但并不能完全解决我的问题。现在,如果我点击 5 次,ContentPage 只会打开两次,而不是 5 次。不过,ContentPage 应该只打开一次......

谁能告诉我如何防止 GestureRecognizer 识别多个点击?或者如何限制我的 -method 或类似的东西中的并发线程?HandleBorderTapped

提前致谢!

C# 异步 async-await maui

评论

0赞 Jason 11/7/2023
在导航回页面之前,不要将标志重置为 false

答:

0赞 Swati Chandra 11/7/2023 #1

可以使用 NumberOfTapsRequired 属性,如下所示。

<Border>
  <Border.GestureRecognizers>
    <TapGestureRecognizer 
        Tapped="HandleBorderTapped" 
        NumberOfTapsRequired="1"/>
  </Border.GestureRecognizers>
</Border>

参考链接: https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/gestures/tap#create-a-tapgesturerecognizer

评论

0赞 IV. 11/7/2023
如果您想要求用户“点击 5 次”,这可能很有用,但这可能与 OP 的问题相反?
0赞 IV. 11/8/2023 #2

此答案显示了如何防止处理多个 GestureRecognizers.Tapped 事件,并提供了一个最小示例,即锁定 .NET Maui 默认应用程序上的 [单击我] 按钮,直到最后一次单击后的某个间隔(在本例中为 2 秒)。对于可重新启动的时间间隔,需要某种看门狗定时器。可以使用以下说明将合适计时器的 NuGet 添加到项目中。该解决方案的第二个元素是具有覆盖整个屏幕的覆盖框架。

通常,属性是 ,并且覆盖不会干扰点击。点击五次,你会得到五。IsVisiblefalse

Modified .NET MAUI default app

当叠加层可见时,外观设置为 并在启用时为屏幕提供“禁用”外观。该 可防止 [Click Me] 按钮识别点击,但仅当 overly 设置为 时。BackgroundColor="DarkGray"Opacity="0.25"InputTransparent="False"IsVisible

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

    <Grid>
        <ScrollView>
            <VerticalStackLayout
                Spacing="25"
                Padding="30,0"
                VerticalOptions="Center">

                <Image
                    Source="dotnet_bot.png"
                    SemanticProperties.Description="Cute dot net bot waving hi to you!"
                    HeightRequest="200"
                    HorizontalOptions="Center" />

                <Label
                    Text="Hello, World!"
                    SemanticProperties.HeadingLevel="Level1"
                    FontSize="32"
                    HorizontalOptions="Center" />

                <Label
                    Text="Welcome to .NET Multi-platform App UI"
                    SemanticProperties.HeadingLevel="Level2"
                    SemanticProperties.Description="Welcome to dot net Multi platform App U I"
                    FontSize="18"
                    HorizontalOptions="Center" />

                <Button
                    x:Name="CounterBtn"
                    Text="Click me"
                    SemanticProperties.Hint="Counts the number of times you click"
                    Clicked="OnCounterClicked"
                    HorizontalOptions="Center" />
                <HorizontalStackLayout HorizontalOptions="Center">
                    <CheckBox 
                        x:Name="checkboxIsLockOutMechanismEnabled"
                        IsChecked="False" 
                        Margin="10" 
                        HorizontalOptions="Start"
                        VerticalOptions="Center"/>
                    <Label 
                        Text="Enable Lock Out Mechanism" 
                        VerticalTextAlignment="Center"
                        HorizontalOptions="Start" />
                </HorizontalStackLayout>

            </VerticalStackLayout>
        </ScrollView>
        <~--Overlay-->
        <Frame
            BackgroundColor="DarkGray" 
            Opacity="0.25"
            InputTransparent="False"
            IsVisible="{Binding IsLockedOut}">
            <Frame.GestureRecognizers>
                <TapGestureRecognizer Tapped="OnOverlayTapped"/>
            </Frame.GestureRecognizers>
        </Frame>
    </Grid>

</ContentPage>

C#

我们用于将此属性挂接到看门狗计时器。{Binding IsLockedOut}

public partial class MainPage : ContentPage
{
    int count = 0;

    public MainPage()
    {
        BindingContext = this;
        InitializeComponent();
    }
    private void OnCounterClicked(object sender, EventArgs e)
    {
        if (checkboxIsLockOutMechanismEnabled.IsChecked)
        {
            ExtendLockout();
        }
        count++;
        if (count == 1)
            CounterBtn.Text = $"Clicked {count} time";
        else
            CounterBtn.Text = $"Clicked {count} times";

        SemanticScreenReader.Announce(CounterBtn.Text);
    }
    WatchdogTimer _wdtOverlay = new WatchdogTimer();

    private void ExtendLockout()
    {
        _wdtOverlay.StartOrRestart(
            initialAction: () => IsLockedOut = true,
            completeAction: () => IsLockedOut = false);
    }

    public bool IsLockedOut
    {
        get => _isLockedOut;
        set
        {
            if (!Equals(_isLockedOut, value))
            {
                _isLockedOut = value;
                OnPropertyChanged();
            }
        }
    }
    bool _isLockedOut = false;

    private void OnOverlayTapped(object sender, TappedEventArgs e)
    {
        ExtendLockout();
    }
}

结果是,从上次点击屏幕开始,UI 将在两秒钟内没有响应。需要明确的是,如果你坐下来每秒点击一次屏幕,你将永远“永远不会”看到第二次点击。


将 WatchdogTimer 添加到项目

有多种方法可以制作看门狗定时器。这是我测试这个答案的那个。

watchdog timer on NuGet

评论

0赞 IV. 11/8/2023
克隆此示例。