From 8c5ab0b24a670de64d530c2488be5fab78c33f8e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com>
Date: Fri, 27 Mar 2026 13:37:18 +0800
Subject: [PATCH] =?UTF-8?q?=E8=99=9A=E6=8B=9F=E5=88=97=E8=A1=A8=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E5=8A=A8=E6=80=81ScrollBarWenScrollable?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Editor/RecyclerView/RecyclerViewEditor.cs | 9 ++
Runtime/RecyclerView/RecyclerView.cs | 109 ++++++++++++++++++++--
2 files changed, 110 insertions(+), 8 deletions(-)
diff --git a/Editor/RecyclerView/RecyclerViewEditor.cs b/Editor/RecyclerView/RecyclerViewEditor.cs
index d8d886f..69d2dfb 100644
--- a/Editor/RecyclerView/RecyclerViewEditor.cs
+++ b/Editor/RecyclerView/RecyclerViewEditor.cs
@@ -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."));
+ }
}
}
diff --git a/Runtime/RecyclerView/RecyclerView.cs b/Runtime/RecyclerView/RecyclerView.cs
index 4184220..803b8da 100644
--- a/Runtime/RecyclerView/RecyclerView.cs
+++ b/Runtime/RecyclerView/RecyclerView.cs
@@ -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
}
}
+ ///
+ /// 是否仅在内容超出可视区域时显示滚动条并允许滚动
+ ///
+ 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();
@@ -349,6 +364,7 @@ namespace AlicizaX.UI
}
scrollbarEx.OnDragEnd = OnScrollbarDragEnd;
+ UpdateScrollbarVisibility();
}
#endregion
@@ -428,15 +444,28 @@ namespace AlicizaX.UI
///
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());