[Opt] 优化虚拟列表滚动和Scrollbar互斥问题

This commit is contained in:
陈思海 2026-04-29 20:49:46 +08:00
parent db11a4aa1a
commit c879ede2e2
4 changed files with 43 additions and 60 deletions

View File

@ -329,20 +329,10 @@ namespace AlicizaX.UI.Editor
using (new EditorGUI.DisabledScope(isPlaying))
{
bool previousScrollValue = _scroll.boolValue;
EditorGUILayout.PropertyField(_scroll);
if (_scroll.boolValue != previousScrollValue)
{
HandleScrollToggle();
}
EditorGUILayout.PropertyField(_scroll, new GUIContent("Scroll Mode"));
}
if (_scroll.boolValue)
{
DrawScrollBarSettings(isPlaying);
}
EditorGUILayout.PropertyField(_snap);
}
EditorGUILayout.EndVertical();
@ -369,8 +359,6 @@ namespace AlicizaX.UI.Editor
private void DrawScrollerSection(bool isPlaying)
{
if (!_scroll.boolValue) return;
EditorGUILayout.BeginVertical("box");
{
EditorGUILayout.LabelField("Scroller Settings", EditorStyles.boldLabel);
@ -500,29 +488,6 @@ namespace AlicizaX.UI.Editor
_selectedScrollerIndex = 0;
}
private void HandleScrollToggle()
{
if (!_scroll.boolValue)
{
RemoveScrollerComponent();
ClearScrollBar();
}
}
private void RemoveScrollerComponent()
{
var recyclerView = target as RecyclerView;
if (recyclerView == null) return;
var scrollerComponent = recyclerView.GetComponent<IScroller>();
if (scrollerComponent != null)
{
Undo.DestroyObjectImmediate(scrollerComponent as MonoBehaviour);
}
ClearScrollerReferences();
}
#endregion
#region Scrollbar Handling

View File

@ -100,7 +100,7 @@ MonoBehaviour:
wheelSpeed: 30
templates: []
content: {fileID: 7227160576944475251}
showScrollBar: 0
scrollbarVisibility: 0
scrollbar: {fileID: 0}
_layoutManagerTypeName: AlicizaX.UI.LinearLayoutManager
layoutManager:

View File

@ -250,4 +250,11 @@ namespace AlicizaX.UI
AlwaysShow = 1,
WhenScrollable = 2
}
public enum ScrollMode
{
AlwaysDisable = 0,
AlwaysShow = 1,
WhenScrollable = 2
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace AlicizaX.UI
@ -51,7 +52,7 @@ namespace AlicizaX.UI
/// <summary>
/// 是否启用滚动能力<E883BD>?
/// </summary>
[HideInInspector] [SerializeField] private bool scroll;
[HideInInspector] [SerializeField] private ScrollMode scroll = ScrollMode.AlwaysShow;
/// <summary>
/// 是否在停止滚动后自动吸附到最近项<E8BF91>?
@ -87,6 +88,7 @@ namespace AlicizaX.UI
/// <summary>
/// 是否显示滚动条<E58AA8>?
/// </summary>
[FormerlySerializedAs("showScrollBar")]
[HideInInspector] [SerializeField] private ScrollbarVisibility scrollbarVisibility;
/// <summary>
@ -221,7 +223,7 @@ namespace AlicizaX.UI
/// <summary>
/// 获取或设置是否启用滚动能力<E883BD>?
/// </summary>
public bool Scroll
public ScrollMode Scroll
{
get => scroll;
set
@ -231,10 +233,9 @@ namespace AlicizaX.UI
if (scroller != null)
{
scroller.InputEnabled = scroll;
scroller.WheelSpeed = wheelSpeed;
scroller.Snap = snap;
UpdateScrollerState();
}
RequestLayout();
@ -249,9 +250,8 @@ namespace AlicizaX.UI
get => snap;
set
{
bool newSnap = value & scroll;
if (snap == newSnap) return;
snap = newSnap;
if (snap == value) return;
snap = value;
if (scroller != null)
{
@ -687,7 +687,7 @@ namespace AlicizaX.UI
/// </summary>
private void ConfigureScrollbar()
{
if (scrollbarVisibility == ScrollbarVisibility.AlwaysHide || scrollbar == null) return;
if (scrollbar == null) return;
scrollbar.onValueChanged.AddListener(OnScrollbarChanged);
@ -1145,7 +1145,7 @@ namespace AlicizaX.UI
/// <param name="smooth">是否使用平滑滚动<E6BB9A>?/param>
public void ScrollTo(int index, bool smooth = false)
{
if (!scroll || scroller == null) return;
if (scroll == ScrollMode.AlwaysDisable || scroller == null) return;
scroller.ScrollTo(layoutManager.IndexToPosition(index), smooth);
@ -1167,7 +1167,7 @@ namespace AlicizaX.UI
/// <param name="duration">平滑滚动时长单位为秒<E4B8BA>?/param>
public void ScrollToWithAlignment(int index, ScrollAlignment alignment, float offset = 0f, bool smooth = false, float duration = 0.3f)
{
if (!scroll || scroller == null) return;
if (scroll == ScrollMode.AlwaysDisable || scroller == null) return;
float targetPosition = CalculateScrollPositionWithAlignment(index, alignment, offset);
@ -1392,16 +1392,17 @@ namespace AlicizaX.UI
}
bool shouldShow = ShouldShowScrollbar();
bool shouldInteract = shouldShow && ShouldEnableScrollbarInteraction();
if (scrollbarVisibleState != shouldShow || scrollbar.gameObject.activeSelf != shouldShow)
{
scrollbarVisibleState = shouldShow;
scrollbar.gameObject.SetActive(shouldShow);
}
if (scrollbarInteractableState != shouldShow || scrollbar.interactable != shouldShow)
if (scrollbarInteractableState != shouldInteract || scrollbar.interactable != shouldInteract)
{
scrollbarInteractableState = shouldShow;
scrollbar.interactable = shouldShow;
scrollbarInteractableState = shouldInteract;
scrollbar.interactable = shouldInteract;
}
if (shouldShow)
@ -1417,7 +1418,7 @@ namespace AlicizaX.UI
/// <returns>应显示滚动条时返<E697B6>?<see langword="true"/>否则返<E58899>?<see langword="false"/><3E>?/returns>
private bool ShouldShowScrollbar()
{
if (scrollbarVisibility == ScrollbarVisibility.AlwaysHide || !scroll || scrollbar == null || scroller == null || layoutManager == null)
if (scrollbarVisibility == ScrollbarVisibility.AlwaysHide || scrollbar == null || scroller == null || layoutManager == null)
{
return false;
}
@ -1437,11 +1438,6 @@ namespace AlicizaX.UI
return false;
}
if (!ShouldLimitScrollToOverflow())
{
return true;
}
return HasScrollableContent();
}
@ -1523,16 +1519,31 @@ namespace AlicizaX.UI
return;
}
scroller.InputEnabled = scroll && (!ShouldLimitScrollToOverflow() || HasScrollableContent());
scroller.InputEnabled = ShouldEnableScrollInput();
}
/// <summary>
/// 判断是否需要将滚动能力限制在内容溢出场景下才启用<E590AF>?
/// </summary>
/// <returns>需要仅在内容溢出时启用滚动返回 <see langword="true"/>否则返<E58899>?<see langword="false"/><3E>?/returns>
private bool ShouldLimitScrollToOverflow()
private bool ShouldEnableScrollInput()
{
return scrollbarVisibility == ScrollbarVisibility.WhenScrollable && SupportsOverflowCheck();
if (scroll == ScrollMode.AlwaysDisable)
{
return false;
}
if (scroll == ScrollMode.AlwaysShow)
{
return true;
}
return SupportsOverflowCheck() && HasScrollableContent();
}
private bool ShouldEnableScrollbarInteraction()
{
return ShouldEnableScrollInput() && HasValidScrollMetrics() && HasScrollableContent();
}
/// <summary>