虚拟列表增加动态ScrollBarWenScrollable

This commit is contained in:
陈思海 2026-03-27 13:37:18 +08:00
parent 619efa0d4d
commit 8c5ab0b24a
2 changed files with 110 additions and 8 deletions

View File

@ -57,6 +57,7 @@ namespace AlicizaX.UI.Editor
private SerializedProperty _templates;
private SerializedProperty _showScrollBar;
private SerializedProperty _showScrollBarOnlyWhenScrollable;
private SerializedProperty _scrollbar;
#endregion
@ -120,6 +121,7 @@ namespace AlicizaX.UI.Editor
{
_templates = serializedObject.FindProperty("templates");
_showScrollBar = serializedObject.FindProperty("showScrollBar");
_showScrollBarOnlyWhenScrollable = serializedObject.FindProperty("showScrollBarOnlyWhenScrollable");
_scrollbar = serializedObject.FindProperty("scrollbar");
}
@ -303,6 +305,13 @@ namespace AlicizaX.UI.Editor
{
HandleScrollBarToggle();
}
if (_showScrollBar.boolValue)
{
EditorGUILayout.PropertyField(
_showScrollBarOnlyWhenScrollable,
new GUIContent("Only When Scrollable", "Hide the scrollbar and disable list scrolling when content does not overflow the viewport."));
}
}
}

View File

@ -38,6 +38,7 @@ namespace AlicizaX.UI
[HideInInspector] [SerializeField] private ViewHolder[] templates;
[HideInInspector] [SerializeField] private RectTransform content;
[HideInInspector] [SerializeField] private bool showScrollBar;
[HideInInspector] [SerializeField] private bool showScrollBarOnlyWhenScrollable;
[HideInInspector] [SerializeField] private Scrollbar scrollbar;
#endregion
@ -200,6 +201,20 @@ namespace AlicizaX.UI
}
}
/// <summary>
/// 是否仅在内容超出可视区域时显示滚动条并允许滚动
/// </summary>
public bool ShowScrollBarOnlyWhenScrollable
{
get => showScrollBarOnlyWhenScrollable;
set
{
if (showScrollBarOnlyWhenScrollable == value) return;
showScrollBarOnlyWhenScrollable = value;
RequestLayout();
}
}
#endregion
@ -333,13 +348,13 @@ namespace AlicizaX.UI
scroller.Snap = snap;
scroller.OnValueChanged.AddListener(OnScrollChanged);
scroller.OnMoveStoped.AddListener(OnMoveStoped);
UpdateScrollerState();
}
private void ConfigureScrollbar()
{
if (!showScrollBar || scrollbar == null) return;
scrollbar.gameObject.SetActive(scroll);
scrollbar.onValueChanged.AddListener(OnScrollbarChanged);
var scrollbarEx = scrollbar.gameObject.GetComponent<ScrollbarEx>();
@ -349,6 +364,7 @@ namespace AlicizaX.UI
}
scrollbarEx.OnDragEnd = OnScrollbarDragEnd;
UpdateScrollbarVisibility();
}
#endregion
@ -428,15 +444,28 @@ namespace AlicizaX.UI
/// </summary>
public void RequestLayout()
{
if (layoutManager == null)
{
UpdateScrollbarVisibility();
return;
}
layoutManager.SetContentSize();
if (scroller == null) return;
if (scroller == null)
{
UpdateScrollbarVisibility();
return;
}
scroller.Direction = direction;
scroller.ViewSize = layoutManager.ViewportSize;
scroller.ContentSize = layoutManager.ContentSize;
scroller.Position = Mathf.Clamp(scroller.Position, 0, scroller.MaxPosition);
UpdateScrollerState();
UpdateScrollbarVisibility();
UpdateScrollbarValue(scroller.Position);
}
#endregion
@ -661,11 +690,14 @@ namespace AlicizaX.UI
private void UpdateScrollbarVisibility()
{
if (scrollbar == null || scroller == null || layoutManager.ContentSize == Vector2.zero)
if (scrollbar == null)
{
return;
}
bool shouldShow = ShouldShowScrollbar();
scrollbar.gameObject.SetActive(shouldShow);
scrollbar.interactable = shouldShow;
if (shouldShow)
{
@ -676,16 +708,22 @@ namespace AlicizaX.UI
private bool ShouldShowScrollbar()
{
if (direction == Direction.Custom) return false;
if (!showScrollBar || !scroll || scrollbar == null || scroller == null || layoutManager == null || !SupportsOverflowCheck())
{
return false;
}
if (direction == Direction.Vertical)
if (!HasValidScrollMetrics())
{
return layoutManager.ContentSize.y > layoutManager.ViewportSize.y;
return false;
}
else // Horizontal
if (!ShouldLimitScrollToOverflow())
{
return layoutManager.ContentSize.x > layoutManager.ViewportSize.x;
return true;
}
return HasScrollableContent();
}
private void ConfigureScrollbarDirection()
@ -707,6 +745,61 @@ namespace AlicizaX.UI
}
}
private void UpdateScrollerState()
{
if (scroller == null)
{
return;
}
scroller.enabled = scroll && (!ShouldLimitScrollToOverflow() || HasScrollableContent());
}
private bool ShouldLimitScrollToOverflow()
{
return showScrollBar && showScrollBarOnlyWhenScrollable && SupportsOverflowCheck();
}
private bool HasScrollableContent()
{
if (layoutManager == null)
{
return false;
}
if (direction == Direction.Vertical)
{
return layoutManager.ContentSize.y > layoutManager.ViewportSize.y;
}
if (direction == Direction.Horizontal)
{
return layoutManager.ContentSize.x > layoutManager.ViewportSize.x;
}
return false;
}
private bool SupportsOverflowCheck()
{
return direction == Direction.Vertical || direction == Direction.Horizontal;
}
private bool HasValidScrollMetrics()
{
if (direction == Direction.Vertical)
{
return layoutManager.ContentSize.y > 0f && layoutManager.ViewportSize.y > 0f;
}
if (direction == Direction.Horizontal)
{
return layoutManager.ContentSize.x > 0f && layoutManager.ViewportSize.x > 0f;
}
return false;
}
private void SnapToNearestItem()
{
int index = layoutManager.PositionToIndex(GetScrollPosition());