原鏈接: "UWP忽略短時間內重覆觸發的事件 超威藍火" 做移動端開發的可能都會遇到這種需求,當用戶點擊一個按鈕之後,由於沒有非同步,或者設備性能很差等等原因,程式卡住了。但是用戶不知道是咋回事啊,就開始狂點按鈕,結果請求很多次資源,或者構造了很多重覆視圖。安卓上有很多介紹如何忽略重覆點擊的情況,uw ...
做移動端開發的可能都會遇到這種需求,當用戶點擊一個按鈕之後,由於沒有非同步,或者設備性能很差等等原因,程式卡住了。但是用戶不知道是咋回事啊,就開始狂點按鈕,結果請求很多次資源,或者構造了很多重覆視圖。安卓上有很多介紹如何忽略重覆點擊的情況,uwp里我好像還沒找到,那接下來就說一說我的方法吧。
首先是官方最常用的,從Windows誕生之初用到現在的,點擊按鈕之後把他Disable掉,等完成了在Enable。其實這算是最好的選擇了,但是總歸是麻煩了一些,尤其是在MVVM的時候,每個Button都要單獨綁定一個IsEnabled,所以接下來就仿照安卓上的通用處理方法試一下。
首先創建一個類,我們就叫他EventWaiter吧,裡面維護一個LastTime,一個IsEnabled,事件可以判斷這個IsEnabled來知道,上一次點擊和這一次點擊的間隔是不是過短,從而決定是否要執行。
public class EventWaiter
{
private DateTime _lastTime;
public EventWaiter(double seconds)
{
Interval = TimeSpan.FromSeconds(seconds);
}
public EventWaiter(TimeSpan interval)
{
Interval = interval;
}
public EventWaiter()
{
Interval = TimeSpan.FromSeconds(0.1d);
}
//間隔
public TimeSpan Interval { get; set; }
public bool IsEnabled
{
get
{
if(DateTime.Now - _lastTime > Interval)
{
_lastTime = DateTime.Now;
return true;
}
return false;
}
}
public void Reset()
{
_lastTime = DateTime.Now;
}
}
大概用法是這樣的:
var waiter = new EventWaiter();
var button = new Button();
button.Click += OnClick;
private void OnClick(object sender,RoutedEventArgs e)
{
if(waiter.IsEnabled)
{
//do something
}
}
接下來就是第三種需求了。對於安卓iOS來說,很少有SizeChanged,但是UWP不一樣啊,UWP能視窗運行啊,所以在一些要響應SizeChanged,在視窗大小改變的時候做一些很重操作的時候,拖拽視窗就會變得非常卡,所以我現在想忽略拖動視窗的中間過程,只讓他響應最後狀態。而上面的EventWaiter,是一段時間內只響應第一次事件的觸發,和我們的需求是完全反著的。
這時候就要請救星:DispatcherTimer出來了(感謝小竹)。
大致思路呢,按時間順序敘述,是當原事件觸發時,讓Timer開始運行;第二次觸發事件時判斷Timer是否運行,如果正在運行呢,就停掉重新啟動,相當於重置了計時器;等Timer第一次跑完,執行Tick的時候停掉Timer,並且激活內部的事件去做真正的操作。
我們再弄個新的類,起個名字叫EventDelayer(原諒我起名困難),裡面需要維護一個DispatcherTimer,還要一個事件Arrived負責在最後被觸發,和一個Delay()方法,負責進入觸發判斷。
public class EventDelayer
{
private DispatcherTimer _timer;
public EventDelayer(double seconds) : this(TimeSpan.FromSeconds(seconds))
{
}
public EventDelayer(TimeSpan interval)
{
_timer = new DispatcherTimer();
_timer.Tick += _timer_Tick;
Interval = interval;
}
public EventDelayer() : this(0.1)
{
}
public TimeSpan Interval
{
get => _timer.Interval;
set => _timer.Interval = value;
}
public bool ResetWhenDelayed { get; set; }
public void Delay()
{
if (!_timer.IsEnabled)
{
_timer.Start();
}
else
{
if (ResetWhenDelayed)
{
_timer.Stop();
_timer.Start();
}
}
}
private void _timer_Tick(object sender, object e)
{
if (_timer.IsEnabled)
{
_timer.Stop();
}
OnArrived();
}
public event EventHandler Arrived;
protected void OnArrived()
{
Arrived?.Invoke(this, EventArgs.Empty);
}
}
用法大概是這個樣子:
var delayer = new EventDelayer();
delayer.Arrived += OnArrived;
this.SizeChanged += OnSizeChanged;
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
delayer.Delay();
}
private void OnArrived(object sender,EventArgs args)
{
//do something
}
炒雞完美。github:https://github.com/cnbluefire/ReaderView/tree/master/ReaderView/Common/Helpers