虚拟列表增加动态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 _templates;
private SerializedProperty _showScrollBar; private SerializedProperty _showScrollBar;
private SerializedProperty _showScrollBarOnlyWhenScrollable;
private SerializedProperty _scrollbar; private SerializedProperty _scrollbar;
#endregion #endregion
@ -120,6 +121,7 @@ namespace AlicizaX.UI.Editor
{ {
_templates = serializedObject.FindProperty("templates"); _templates = serializedObject.FindProperty("templates");
_showScrollBar = serializedObject.FindProperty("showScrollBar"); _showScrollBar = serializedObject.FindProperty("showScrollBar");
_showScrollBarOnlyWhenScrollable = serializedObject.FindProperty("showScrollBarOnlyWhenScrollable");
_scrollbar = serializedObject.FindProperty("scrollbar"); _scrollbar = serializedObject.FindProperty("scrollbar");
} }
@ -303,6 +305,13 @@ namespace AlicizaX.UI.Editor
{ {
HandleScrollBarToggle(); 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 ViewHolder[] templates;
[HideInInspector] [SerializeField] private RectTransform content; [HideInInspector] [SerializeField] private RectTransform content;
[HideInInspector] [SerializeField] private bool showScrollBar; [HideInInspector] [SerializeField] private bool showScrollBar;
[HideInInspector] [SerializeField] private bool showScrollBarOnlyWhenScrollable;
[HideInInspector] [SerializeField] private Scrollbar scrollbar; [HideInInspector] [SerializeField] private Scrollbar scrollbar;
#endregion #endregion
@ -200,6 +201,20 @@ namespace AlicizaX.UI
} }
} }
/// <summary>
/// 是否仅在内容超出可视区域时显示滚动条并允许滚动
/// </summary>
public bool ShowScrollBarOnlyWhenScrollable
{
get => showScrollBarOnlyWhenScrollable;
set
{
if (showScrollBarOnlyWhenScrollable == value) return;
showScrollBarOnlyWhenScrollable = value;
RequestLayout();
}
}
#endregion #endregion
@ -333,13 +348,13 @@ namespace AlicizaX.UI
scroller.Snap = snap; scroller.Snap = snap;
scroller.OnValueChanged.AddListener(OnScrollChanged); scroller.OnValueChanged.AddListener(OnScrollChanged);
scroller.OnMoveStoped.AddListener(OnMoveStoped); scroller.OnMoveStoped.AddListener(OnMoveStoped);
UpdateScrollerState();
} }
private void ConfigureScrollbar() private void ConfigureScrollbar()
{ {
if (!showScrollBar || scrollbar == null) return; if (!showScrollBar || scrollbar == null) return;
scrollbar.gameObject.SetActive(scroll);
scrollbar.onValueChanged.AddListener(OnScrollbarChanged); scrollbar.onValueChanged.AddListener(OnScrollbarChanged);
var scrollbarEx = scrollbar.gameObject.GetComponent<ScrollbarEx>(); var scrollbarEx = scrollbar.gameObject.GetComponent<ScrollbarEx>();
@ -349,6 +364,7 @@ namespace AlicizaX.UI
} }
scrollbarEx.OnDragEnd = OnScrollbarDragEnd; scrollbarEx.OnDragEnd = OnScrollbarDragEnd;
UpdateScrollbarVisibility();
} }
#endregion #endregion
@ -428,15 +444,28 @@ namespace AlicizaX.UI
/// </summary> /// </summary>
public void RequestLayout() public void RequestLayout()
{ {
if (layoutManager == null)
{
UpdateScrollbarVisibility();
return;
}
layoutManager.SetContentSize(); layoutManager.SetContentSize();
if (scroller == null) return; if (scroller == null)
{
UpdateScrollbarVisibility();
return;
}
scroller.Direction = direction; scroller.Direction = direction;
scroller.ViewSize = layoutManager.ViewportSize; scroller.ViewSize = layoutManager.ViewportSize;
scroller.ContentSize = layoutManager.ContentSize; scroller.ContentSize = layoutManager.ContentSize;
scroller.Position = Mathf.Clamp(scroller.Position, 0, scroller.MaxPosition);
UpdateScrollerState();
UpdateScrollbarVisibility(); UpdateScrollbarVisibility();
UpdateScrollbarValue(scroller.Position);
} }
#endregion #endregion
@ -661,11 +690,14 @@ namespace AlicizaX.UI
private void UpdateScrollbarVisibility() private void UpdateScrollbarVisibility()
{ {
if (scrollbar == null || scroller == null || layoutManager.ContentSize == Vector2.zero) if (scrollbar == null)
{
return; return;
}
bool shouldShow = ShouldShowScrollbar(); bool shouldShow = ShouldShowScrollbar();
scrollbar.gameObject.SetActive(shouldShow); scrollbar.gameObject.SetActive(shouldShow);
scrollbar.interactable = shouldShow;
if (shouldShow) if (shouldShow)
{ {
@ -676,16 +708,22 @@ namespace AlicizaX.UI
private bool ShouldShowScrollbar() 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() 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() private void SnapToNearestItem()
{ {
int index = layoutManager.PositionToIndex(GetScrollPosition()); int index = layoutManager.PositionToIndex(GetScrollPosition());