[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)) using (new EditorGUI.DisabledScope(isPlaying))
{ {
bool previousScrollValue = _scroll.boolValue; EditorGUILayout.PropertyField(_scroll, new GUIContent("Scroll Mode"));
EditorGUILayout.PropertyField(_scroll);
if (_scroll.boolValue != previousScrollValue)
{
HandleScrollToggle();
}
}
if (_scroll.boolValue)
{
DrawScrollBarSettings(isPlaying);
} }
DrawScrollBarSettings(isPlaying);
EditorGUILayout.PropertyField(_snap); EditorGUILayout.PropertyField(_snap);
} }
EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical();
@ -369,8 +359,6 @@ namespace AlicizaX.UI.Editor
private void DrawScrollerSection(bool isPlaying) private void DrawScrollerSection(bool isPlaying)
{ {
if (!_scroll.boolValue) return;
EditorGUILayout.BeginVertical("box"); EditorGUILayout.BeginVertical("box");
{ {
EditorGUILayout.LabelField("Scroller Settings", EditorStyles.boldLabel); EditorGUILayout.LabelField("Scroller Settings", EditorStyles.boldLabel);
@ -500,29 +488,6 @@ namespace AlicizaX.UI.Editor
_selectedScrollerIndex = 0; _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 #endregion
#region Scrollbar Handling #region Scrollbar Handling

View File

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

View File

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