需求
想要一個(gè)工具,能夠同時(shí)集成單擊、雙擊、長按的功能
思路
通過IPointerDownHandler, IPointerUpHandler, IPointerClickHandler這三個(gè)接口就可以監(jiān)聽點(diǎn)擊狀態(tài),然后再通過不同的點(diǎn)擊狀態(tài)來處理相應(yīng)的事件文章來源:http://www.zghlxwxcb.cn/news/detail-512392.html
遇到的問題
- 由于可能同時(shí)存在多個(gè)事件,實(shí)際開發(fā)過程中會(huì)出現(xiàn)多余事件通知,如下:
- 同時(shí)擁有單擊和雙擊事件,點(diǎn)擊雙擊的同時(shí)會(huì)響應(yīng)兩次單擊
- 同時(shí)擁有單擊事件和長按事件時(shí),當(dāng)長按事件響應(yīng)的時(shí)候會(huì)同時(shí)響應(yīng)單擊事件
- 同時(shí)擁有單擊雙擊和長按事件時(shí)點(diǎn)擊雙擊時(shí)會(huì)同時(shí)響應(yīng)兩次單擊,長按事件響應(yīng)時(shí)會(huì)同時(shí)響應(yīng)單擊事件
解決問題
- 由于我們無法預(yù)測用戶的具體行為,所以當(dāng)需要響應(yīng)單擊事件時(shí),檢測其他事件有沒有在安全時(shí)效內(nèi)的檢測體,例如,單擊響應(yīng)的時(shí)候如果有其他事件正在檢測過程中我們忽略這次的事件響應(yīng),并把事件存起來,等待所有事件的安全時(shí)效結(jié)束時(shí)響應(yīng)這次單擊事件?;蛘呤堑却^程中有其他事件成功響應(yīng)那么我們就舍棄這次的單擊事件而響應(yīng)對應(yīng)的雙擊或者長按事件。那么這樣就完美解決了上述所有問題。
具體代碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-512392.html
ClickEvent
public class ClickEvent : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerClickHandler
{
private List<IClickListener> listenerList = new List<IClickListener>();
private Dictionary<IClickListener, Action> callBackDict = new Dictionary<IClickListener, Action>();
public void OnPointerDown(PointerEventData eventData)
{
for (int i = listenerList.Count - 1; i >= 0; i--)
{
listenerList[i]?.OnPointerDown(eventData);
}
}
public void OnPointerUp(PointerEventData eventData)
{
for (int i = listenerList.Count - 1; i >= 0; i--)
{
listenerList[i]?.OnPointerUp(eventData);
}
}
public void OnPointerClick(PointerEventData eventData)
{
for (int i = listenerList.Count - 1; i >= 0; i--)
{
listenerList[i]?.OnPointerClick(eventData);
}
}
private void Update()
{
for (int i = listenerList.Count - 1; i >= 0; i--)
{
listenerList[i]?.Update();
}
}
public T Add<T>() where T : ClickListener, new()
{
T t = Get<T>();
if (t == null)
{
t = new T();
(t as IClickListener).OnCallBack((success) => OnReceive(success, t));
if (listenerList.Count == 0 || t is ClickButton) listenerList.Add(t);
else listenerList.Insert(listenerList.Count - 1, t);
}
return t;
}
public void Remove<T>() where T : ClickListener
{
if (!Contains<T>()) return;
T t = Get<T>();
if (callBackDict.ContainsKey(t)) callBackDict.Remove(t);
listenerList.Remove(t);
}
public void RemoveAll()
{
listenerList.Clear();
callBackDict.Clear();
}
public T Get<T>() where T : ClickListener
{
if (listenerList.Count == 0) return default;
IClickListener listener = default;
for (int i = listenerList.Count - 1; i >= 0; i--)
{
listener = listenerList[i];
if (listener is T) return (T) listener;
}
return default;
}
public bool Contains<T>() where T : ClickListener
{
if (listenerList.Count == 0) return false;
IClickListener listener = default;
for (int i = listenerList.Count - 1; i >= 0; i--)
{
listener = listenerList[i];
if (listener is T) return true;
}
return false;
}
public bool ContainsCallBack<T>() where T : ClickListener
{
T t = Get<T>();
if (t == null) return false;
if (callBackDict.ContainsKey(t))
{
if (callBackDict[t] == null)
{
callBackDict.Remove(t);
return false;
}
return true;
}
else return false;
}
public Action GetCallBack<T>() where T : ClickListener
{
T t = Get<T>();
if (t == null) return null;
if (callBackDict.ContainsKey(t))
{
Action callBack = callBackDict[t];
if (callBack == null)
{
callBackDict.Remove(t);
return null;
}
return callBack;
}
else return null;
}
public bool HasRuning()
{
bool isRuning = false;
foreach (var lis in listenerList)
{
if (lis.isRuning)
{
isRuning = true;
break;
}
}
return isRuning;
}
public T OnRegister<T>(Action callBack) where T : ClickListener, new()
{
T t = Add<T>();
if (callBackDict.ContainsKey(t)) callBackDict[t] = callBack;
else callBackDict.Add(t, callBack);
return t;
}
private Action clickCallBack = null;
void OnReceive(bool success, IClickListener listener)
{
if (success)
{
if (listener is ClickButton) //單擊事件
{
Action cbk = GetCallBack<ClickButton>();
if (cbk == null) return;
if (HasRuning()) //有正在運(yùn)行的事件檢測單擊事件滯后分發(fā)
{
clickCallBack = cbk;
}
else
{
clickCallBack = null;
ResetOther();
cbk.Invoke();
}
}
else //其他事件
{
clickCallBack = null;
ResetOther();
if (callBackDict.ContainsKey(listener)) callBackDict[listener]?.Invoke();
}
}
else
{
if (!HasRuning())
{
clickCallBack?.Invoke();
clickCallBack = null;
}
}
void ResetOther()
{
foreach (var btn in listenerList)
{
if (btn != listener) btn?.Reset();
}
}
}
#if UNITY_EDITOR
[UnityEditor.CustomEditor(typeof(ClickEvent))]
class ClickEventInspector : UnityEditor.Editor
{
private ClickEvent clickEvent;
private void OnEnable()
{
clickEvent = target as ClickEvent;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Label("ClickListeners: ");
IClickListener listener;
for (int i = clickEvent.listenerList.Count - 1; i >= 0; i--)
{
listener = clickEvent.listenerList[i];
GUILayout.Label(" " + listener);
}
}
}
#endif
private interface IClickListener
{
bool isRuning { get; set; }
void OnPointerDown(PointerEventData eventData);
void OnPointerUp(PointerEventData eventData);
void OnPointerClick(PointerEventData eventData);
void Update();
void OnCallBack(Action<bool> callBack);
void Reset();
void Dispose();
}
public abstract class ClickListener : IClickListener
{
bool IClickListener.isRuning
{
get => isRuning;
set { isRuning = value; }
}
protected bool isRuning = false;
protected Action<bool> callBack { get; private set; }
void IClickListener.OnPointerDown(PointerEventData eventData) => OnPointerDown(eventData);
void IClickListener.OnPointerUp(PointerEventData eventData) => OnPointerUp(eventData);
void IClickListener.OnPointerClick(PointerEventData eventData) => OnPointerClick(eventData);
void IClickListener.Update() => Update();
void IClickListener.Reset() => Reset();
void IClickListener.Dispose() => Dispose();
void IClickListener.OnCallBack(Action<bool> callBack)
{
this.callBack = callBack;
}
protected virtual void OnPointerDown(PointerEventData eventData)
{
}
protected virtual void OnPointerUp(PointerEventData eventData)
{
}
protected virtual void OnPointerClick(PointerEventData eventData)
{
}
protected virtual void Update()
{
}
protected abstract void Reset();
protected virtual void Dispose()
{
callBack = null;
}
}
}
Buttons
//單擊按鈕
public class ClickButton : ClickEvent.ClickListener
{
protected override void OnPointerDown(PointerEventData eventData)
{
isRuning = true;
}
protected override void OnPointerUp(PointerEventData eventData)
{
if (isRuning)
{
Reset();
callBack?.Invoke(true);
}
}
protected override void Reset()
{
isRuning = false;
}
}
//雙擊按鈕
public class DoubleClickButton : ClickEvent.ClickListener
{
public int maxSpaceTime = 250;
private int clickCount = 0;
private Stopwatch stopWatch;
private float lastPointTime = 0;
public DoubleClickButton()
{
stopWatch = new Stopwatch();
Reset();
}
protected override void OnPointerDown(PointerEventData eventData)
{
isRuning = true;
}
protected override void OnPointerClick(PointerEventData eventData)
{
stopWatch.Start();
clickCount++;
if (clickCount == 2)
{
Reset();
callBack?.Invoke(true);
}
}
protected override void Update()
{
if (!stopWatch.IsRunning) return;
if (stopWatch.ElapsedMilliseconds > maxSpaceTime)
{
Reset();
callBack?.Invoke(false);
}
}
protected override void Reset()
{
isRuning = false;
if (stopWatch.IsRunning) stopWatch.Stop();
stopWatch.Reset();
clickCount = 0;
}
}
//長按按鈕
public class PressButton : ClickEvent.ClickListener
{
public int pressMinTime = 2000;
private Stopwatch stopWatch;
public PressButton()
{
stopWatch = new Stopwatch();
}
protected override void OnPointerDown(PointerEventData eventData)
{
isRuning = true;
stopWatch.Start();
}
protected override void OnPointerUp(PointerEventData eventData)
{
Reset();
callBack?.Invoke(false);
}
protected override void Update()
{
if (!stopWatch.IsRunning) return;
if (stopWatch.ElapsedMilliseconds > pressMinTime)
{
Reset();
callBack?.Invoke(true);
}
}
protected override void Reset()
{
isRuning = false;
if (stopWatch.IsRunning) stopWatch.Stop();
stopWatch.Reset();
}
}
GameObjectExpand
public static class GameObjectExpand
{
public static ClickButton OnClick(this GameObject obj, Action callBack)
{
if (obj == null) return null;
ClickEvent clickEvent = GetClickEvent(obj);
ClickButton button = clickEvent.OnRegister<ClickButton>(callBack);
return button;
}
public static DoubleClickButton OnDoubleClick(this GameObject obj, Action callBack)
{
if (obj == null) return null;
ClickEvent clickEvent = GetClickEvent(obj);
DoubleClickButton button = clickEvent.OnRegister<DoubleClickButton>(callBack);
return button;
}
public static PressButton OnPress(this GameObject obj, Action callBack)
{
if (obj == null) return null;
ClickEvent clickEvent = GetClickEvent(obj);
PressButton button = clickEvent.OnRegister<PressButton>(callBack);
return button;
}
static ClickEvent GetClickEvent(GameObject obj)
{
ClickEvent clickEvent = obj.GetComponent<ClickEvent>();
if (clickEvent == null) clickEvent = obj.AddComponent<ClickEvent>();
return clickEvent;
}
}
使用
public GameObject uiObj;
//點(diǎn)擊事件注冊
uiObj.OnClick(() =>
{
Debug.Log("OnClick");
});
//雙擊事件注冊
uiObj.OnDoubleClick(() =>
{
Debug.Log("OnDoubleClick");
});
//長按事件注冊
uiObj.OnPress(() =>
{
Debug.Log("OnPress");
});
到了這里,關(guān)于Unity 單擊、雙擊、長按事件處理的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!