From 21a09edad2e54272ac63a3cf68a1ddfc7ade80a7 Mon Sep 17 00:00:00 2001
From: LiRuoChen <571244399@qq.com>
Date: Thu, 16 Apr 2026 16:46:20 +0800
Subject: [PATCH] =?UTF-8?q?xericScrollRect=E7=BB=84=E4=BB=B6=E4=B8=AD?=
=?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E7=BD=91=E6=A0=BC=E5=B8=83=E5=B1=80?=
=?UTF-8?q?=E5=92=8C=E5=B0=BA=E5=AF=B8=E8=AE=A1=E7=AE=97=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../OverrideLayout/XericScrollRectEditor.cs | 45 +-
Editor/OverrideUI/XericToggleEditor.cs | 11 +-
Runtime/Core/Interface.meta | 3 +
.../Core/Interface/IUIVesselVisibalControl.cs | 22 +
.../Interface/IUIVesselVisibalControl.cs.meta | 3 +
Runtime/Helper/GridLayoutCalculator.cs | 255 +++++++++++
Runtime/Helper/GridLayoutCalculator.cs.meta | 3 +
Runtime/Helper/UIVesselHelp.cs | 19 +
Runtime/Helper/UIVesselHelp.cs.meta | 3 +
Runtime/OverrideLayout/XericScrollRect.cs | 408 +++++++++++++-----
10 files changed, 652 insertions(+), 120 deletions(-)
create mode 100644 Runtime/Core/Interface.meta
create mode 100644 Runtime/Core/Interface/IUIVesselVisibalControl.cs
create mode 100644 Runtime/Core/Interface/IUIVesselVisibalControl.cs.meta
create mode 100644 Runtime/Helper/GridLayoutCalculator.cs
create mode 100644 Runtime/Helper/GridLayoutCalculator.cs.meta
create mode 100644 Runtime/Helper/UIVesselHelp.cs
create mode 100644 Runtime/Helper/UIVesselHelp.cs.meta
diff --git a/Editor/OverrideLayout/XericScrollRectEditor.cs b/Editor/OverrideLayout/XericScrollRectEditor.cs
index 43e2efd..f9bf5f4 100644
--- a/Editor/OverrideLayout/XericScrollRectEditor.cs
+++ b/Editor/OverrideLayout/XericScrollRectEditor.cs
@@ -1,5 +1,6 @@
using UnityEditor;
using UnityEditor.UI;
+using UnityEngine;
using XericUI.OverrideLayout;
namespace XericUIEditor.OverrideLayout
@@ -13,15 +14,33 @@ namespace XericUIEditor.OverrideLayout
SerializedProperty m_preloadMargin;
SerializedProperty m_discernibleDistance;
SerializedProperty m_AAMVUpdateRate;
+ SerializedProperty m_childrenPool;
+
+ SerializedProperty m_cellSize;
+ SerializedProperty m_spacing;
+ SerializedProperty m_padding;
+ SerializedProperty m_startCorner;
+ SerializedProperty m_startAxis;
+ SerializedProperty m_childAlignment;
+ SerializedProperty m_constraint;
protected override void OnEnable()
{
base.OnEnable();
- m_EnableAutoActiveByMaskVisible = serializedObject.FindProperty("enableAutoActiveByMaskVisible");
+ m_EnableAutoActiveByMaskVisible = serializedObject.FindProperty("m_enableAdvancedFunc");
m_layoutGroup = serializedObject.FindProperty("layoutGroup");
- m_preloadMargin = serializedObject.FindProperty("preloadMargin");
- m_discernibleDistance = serializedObject.FindProperty("discernibleDistance");
- m_AAMVUpdateRate = serializedObject.FindProperty("AAMVUpdateRate");
+ m_preloadMargin = serializedObject.FindProperty("m_preloadMargin");
+ m_discernibleDistance = serializedObject.FindProperty("m_discernibleDistance");
+ m_AAMVUpdateRate = serializedObject.FindProperty("m_AAMVUpdateRate");
+
+ m_childrenPool = serializedObject.FindProperty("childrenPool");
+ m_cellSize = serializedObject.FindProperty("m_cellSize");
+ m_spacing = serializedObject.FindProperty("m_spacing");
+ m_padding = serializedObject.FindProperty("m_padding");
+ m_startCorner = serializedObject.FindProperty("m_startCorner");
+ m_startAxis = serializedObject.FindProperty("m_startAxis");
+ m_childAlignment = serializedObject.FindProperty("m_childAlignment");
+ m_constraint = serializedObject.FindProperty("m_constraint");
}
public override void OnInspectorGUI()
@@ -29,7 +48,7 @@ namespace XericUIEditor.OverrideLayout
base.OnInspectorGUI();
serializedObject.Update();
-
+
EditorGUILayout.PropertyField(m_EnableAutoActiveByMaskVisible);
if (m_EnableAutoActiveByMaskVisible.boolValue)
{
@@ -37,11 +56,21 @@ namespace XericUIEditor.OverrideLayout
EditorGUILayout.PropertyField(m_layoutGroup, EditorGUIUtility.TrTextContent("Layout Group"));
EditorGUILayout.PropertyField(m_preloadMargin, EditorGUIUtility.TrTextContent("Preload Margin"));
EditorGUILayout.PropertyField(m_discernibleDistance, EditorGUIUtility.TrTextContent("Discernible Distance"));
- EditorGUILayout.PropertyField(m_AAMVUpdateRate, EditorGUIUtility.TrTextContent("Aamv Update Rate"));
-
+ EditorGUILayout.PropertyField(m_AAMVUpdateRate, EditorGUIUtility.TrTextContent("Update Rate"));
+ EditorGUILayout.PropertyField(m_childrenPool, EditorGUIUtility.TrTextContent("Item Pool"));
+ EditorGUI.indentLevel--;
+ EditorGUILayout.LabelField(new GUIContent("GridLayout"));
+ EditorGUI.indentLevel++;
+ EditorGUILayout.PropertyField(m_cellSize);
+ EditorGUILayout.PropertyField(m_spacing);
+ EditorGUILayout.PropertyField(m_padding);
+ EditorGUILayout.PropertyField(m_startCorner);
+ EditorGUILayout.PropertyField(m_startAxis);
+ EditorGUILayout.PropertyField(m_childAlignment);
+ EditorGUILayout.PropertyField(m_constraint);
EditorGUI.indentLevel--;
}
-
+
serializedObject.ApplyModifiedProperties();
}
}
diff --git a/Editor/OverrideUI/XericToggleEditor.cs b/Editor/OverrideUI/XericToggleEditor.cs
index efb8840..9cb0001 100644
--- a/Editor/OverrideUI/XericToggleEditor.cs
+++ b/Editor/OverrideUI/XericToggleEditor.cs
@@ -70,18 +70,19 @@ namespace XericUI
EditorGUILayout.PropertyField(m_GraphicProperty);
EditorGUILayout.PropertyField(m_TextProperty);
EditorGUILayout.PropertyField(m_EnableTextColorTransitionProperty);
- if (EditorGUILayout.BeginFadeGroup(m_EnableTextColorTransitionProperty.boolValue ? 1 : 0))
+ if (m_EnableTextColorTransitionProperty.boolValue)
{
+ EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_TextTransitionColorModeProperty);
+ EditorGUI.indentLevel--;
}
- EditorGUILayout.EndFadeGroup();
-
EditorGUILayout.PropertyField(m_enableTextContextTransitionProperty);
- if (EditorGUILayout.BeginFadeGroup(m_enableTextContextTransitionProperty.boolValue ? 1 : 0))
+ if (m_enableTextContextTransitionProperty.boolValue)
{
+ EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_textTransitionContextModeProperty);
+ EditorGUI.indentLevel--;
}
- EditorGUILayout.EndFadeGroup();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_GroupProperty);
diff --git a/Runtime/Core/Interface.meta b/Runtime/Core/Interface.meta
new file mode 100644
index 0000000..47b1f7f
--- /dev/null
+++ b/Runtime/Core/Interface.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f441117a49ca43f4a6bf105e18562875
+timeCreated: 1776309301
\ No newline at end of file
diff --git a/Runtime/Core/Interface/IUIVesselVisibalControl.cs b/Runtime/Core/Interface/IUIVesselVisibalControl.cs
new file mode 100644
index 0000000..d6cc6a6
--- /dev/null
+++ b/Runtime/Core/Interface/IUIVesselVisibalControl.cs
@@ -0,0 +1,22 @@
+namespace XericUI.Core.Interface
+{
+ ///
+ /// Xeric UI Vessel容器控制接口
+ ///
+ public interface IUIVesselVisibalControl
+ {
+ ///
+ /// 获取当前元素的可见性
+ ///
+ public bool GetUIVisibal { get; }
+ ///
+ /// 当前元素处于可见范围时(预载范围)
+ ///
+ public void UnderVisibal();
+ ///
+ /// 当前元素离开可见范围时(预载范围)
+ ///
+
+ public void OutOfVisibal();
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Core/Interface/IUIVesselVisibalControl.cs.meta b/Runtime/Core/Interface/IUIVesselVisibalControl.cs.meta
new file mode 100644
index 0000000..23111ef
--- /dev/null
+++ b/Runtime/Core/Interface/IUIVesselVisibalControl.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 055f74c72fcd49e5a99faea1af98ebc1
+timeCreated: 1776309332
\ No newline at end of file
diff --git a/Runtime/Helper/GridLayoutCalculator.cs b/Runtime/Helper/GridLayoutCalculator.cs
new file mode 100644
index 0000000..2046108
--- /dev/null
+++ b/Runtime/Helper/GridLayoutCalculator.cs
@@ -0,0 +1,255 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace XericUI.Helper
+{
+ public static class GridLayoutCalculator
+ {
+ public struct LayoutItem
+ {
+ public Vector2 position; // anchoredPosition
+ public Vector2 size;
+ public int indexInGrid;
+ public int rowIndex;
+ public int columnIndex;
+ }
+
+ ///
+ /// 计算所有项目的布局
+ ///
+ /// 子项数量
+ /// 所需尺寸
+ /// 单元尺寸
+ /// 单元空隙
+ /// 外围空隙
+ /// 起始角
+ /// 起始轴
+ /// 容器尺寸
+ /// 需要的列数
+ /// 子项对齐
+ ///
+ public static List CalculateGridLayout(
+ int childCount,
+ Vector2 containerSize,
+ Vector2 cellSize,
+ Vector2 spacing,
+ RectOffset padding,
+ GridLayoutGroup.Corner startCorner,
+ GridLayoutGroup.Axis startAxis,
+ GridLayoutGroup.Constraint constraint,
+ int constraintCount,
+ TextAnchor childAlignment = TextAnchor.UpperLeft)
+ {
+ List layoutItems = new List(childCount);
+
+ // 1. 计算行列数
+ int cellCountX, cellCountY;
+ CalculateGridDimensions(childCount, containerSize, cellSize, spacing, padding,
+ constraint, constraintCount, startAxis, out cellCountX, out cellCountY);
+
+ // 2. 计算实际使用的行列数
+ int actualCellCountX, actualCellCountY;
+ CalculateActualGridDimensions(childCount, cellCountX, cellCountY, constraint, constraintCount,
+ startAxis, out actualCellCountX, out actualCellCountY);
+
+ // 3. 计算所需空间和起始偏移
+ Vector2 requiredSpace = new Vector2(
+ actualCellCountX * cellSize.x + (actualCellCountX - 1) * spacing.x,
+ actualCellCountY * cellSize.y + (actualCellCountY - 1) * spacing.y
+ );
+
+ Vector2 startOffset = new Vector2(
+ GetStartOffset(0, requiredSpace.x, containerSize.x, padding, childAlignment),
+ GetStartOffset(1, requiredSpace.y, containerSize.y, padding, childAlignment)
+ );
+
+ // 4. 处理特殊情况(约束条件下的子项重新排列)
+ int childrenToMove = 0;
+ if (childCount > constraintCount && constraint != GridLayoutGroup.Constraint.Flexible)
+ {
+ int cellsPerMainAxis = startAxis == GridLayoutGroup.Axis.Horizontal ? cellCountX : cellCountY;
+ if (Mathf.CeilToInt((float)childCount / (float)cellsPerMainAxis) < constraintCount)
+ {
+ childrenToMove = constraintCount - Mathf.CeilToInt((float)childCount / (float)cellsPerMainAxis);
+ childrenToMove += Mathf.FloorToInt((float)childrenToMove / ((float)cellsPerMainAxis - 1));
+ if (childCount % cellsPerMainAxis == 1)
+ childrenToMove += 1;
+ }
+ }
+
+ // 5. 计算每个子项的位置
+ int cornerX = (int)startCorner % 2;
+ int cornerY = (int)startCorner / 2;
+
+ for (int i = 0; i < childCount; i++)
+ {
+ int positionX, positionY;
+ CalculateItemPosition(i, childCount, cellCountX, cellCountY, startAxis, constraint,
+ constraintCount, childrenToMove, cornerX, cornerY,
+ actualCellCountX, actualCellCountY, out positionX, out positionY);
+
+ Vector2 position = new Vector2(
+ startOffset.x + (cellSize.x + spacing.x) * positionX,
+ startOffset.y + (cellSize.y + spacing.y) * positionY
+ );
+
+ layoutItems.Add(new LayoutItem
+ {
+ position = position,
+ size = cellSize,
+ indexInGrid = i,
+ rowIndex = positionY,
+ columnIndex = positionX
+ });
+ }
+
+ return layoutItems;
+ }
+
+ private static void CalculateGridDimensions(
+ int childCount,
+ Vector2 containerSize,
+ Vector2 cellSize,
+ Vector2 spacing,
+ RectOffset padding,
+ GridLayoutGroup.Constraint constraint,
+ int constraintCount,
+ GridLayoutGroup.Axis startAxis,
+ out int cellCountX,
+ out int cellCountY)
+ {
+ cellCountX = 1;
+ cellCountY = 1;
+
+ if (constraint == GridLayoutGroup.Constraint.FixedColumnCount)
+ {
+ cellCountX = constraintCount;
+ if (childCount > cellCountX)
+ cellCountY = childCount / cellCountX + (childCount % cellCountX > 0 ? 1 : 0);
+ }
+ else if (constraint == GridLayoutGroup.Constraint.FixedRowCount)
+ {
+ cellCountY = constraintCount;
+ if (childCount > cellCountY)
+ cellCountX = childCount / cellCountY + (childCount % cellCountY > 0 ? 1 : 0);
+ }
+ else
+ {
+ // Flexible constraint - based on container size
+ float availableWidth = containerSize.x - padding.horizontal;
+ float availableHeight = containerSize.y - padding.vertical;
+
+ if (cellSize.x + spacing.x > 0)
+ cellCountX = Mathf.Max(1,
+ Mathf.FloorToInt((availableWidth + spacing.x + 0.001f) / (cellSize.x + spacing.x)));
+ else
+ cellCountX = int.MaxValue;
+
+ if (cellSize.y + spacing.y > 0)
+ cellCountY = Mathf.Max(1,
+ Mathf.FloorToInt((availableHeight + spacing.y + 0.001f) / (cellSize.y + spacing.y)));
+ else
+ cellCountY = int.MaxValue;
+ }
+ }
+
+ private static void CalculateActualGridDimensions(
+ int childCount,
+ int cellCountX,
+ int cellCountY,
+ GridLayoutGroup.Constraint constraint,
+ int constraintCount,
+ GridLayoutGroup.Axis startAxis,
+ out int actualCellCountX,
+ out int actualCellCountY)
+ {
+ if (startAxis == GridLayoutGroup.Axis.Horizontal)
+ {
+ actualCellCountX = Mathf.Clamp(cellCountX, 1, childCount);
+
+ if (constraint == GridLayoutGroup.Constraint.FixedRowCount)
+ actualCellCountY = Mathf.Min(cellCountY, childCount);
+ else
+ actualCellCountY = Mathf.Clamp(cellCountY, 1, Mathf.CeilToInt(childCount / (float)cellCountX));
+ }
+ else
+ {
+ actualCellCountY = Mathf.Clamp(cellCountY, 1, childCount);
+
+ if (constraint == GridLayoutGroup.Constraint.FixedColumnCount)
+ actualCellCountX = Mathf.Min(cellCountX, childCount);
+ else
+ actualCellCountX = Mathf.Clamp(cellCountX, 1, Mathf.CeilToInt(childCount / (float)cellCountY));
+ }
+ }
+
+ private static void CalculateItemPosition(
+ int index,
+ int childCount,
+ int cellCountX,
+ int cellCountY,
+ GridLayoutGroup.Axis startAxis,
+ GridLayoutGroup.Constraint constraint,
+ int constraintCount,
+ int childrenToMove,
+ int cornerX,
+ int cornerY,
+ int actualCellCountX,
+ int actualCellCountY,
+ out int positionX,
+ out int positionY)
+ {
+ if (startAxis == GridLayoutGroup.Axis.Horizontal)
+ {
+ if (constraint == GridLayoutGroup.Constraint.FixedRowCount && childCount - index <= childrenToMove)
+ {
+ positionX = 0;
+ positionY = constraintCount - (childCount - index);
+ }
+ else
+ {
+ positionX = index % cellCountX;
+ positionY = index / cellCountX;
+ }
+ }
+ else
+ {
+ if (constraint == GridLayoutGroup.Constraint.FixedColumnCount && childCount - index <= childrenToMove)
+ {
+ positionX = constraintCount - (childCount - index);
+ positionY = 0;
+ }
+ else
+ {
+ positionX = index / cellCountY;
+ positionY = index % cellCountY;
+ }
+ }
+
+ // Apply corner flipping
+ if (cornerX == 1)
+ positionX = actualCellCountX - 1 - positionX;
+ if (cornerY == 1)
+ positionY = actualCellCountY - 1 - positionY;
+ }
+
+ private static float GetStartOffset(int axis, float requiredSpace, float availableSpace,
+ RectOffset padding, TextAnchor childAlignment)
+ {
+ float requiredSpaceWithPadding = requiredSpace + (axis == 0 ? padding.horizontal : padding.vertical);
+ float surplusSpace = availableSpace - requiredSpaceWithPadding;
+ float alignmentOnAxis = GetAlignmentOnAxis(axis, childAlignment);
+
+ return (axis == 0 ? padding.left : padding.top) + surplusSpace * alignmentOnAxis;
+ }
+
+ private static float GetAlignmentOnAxis(int axis, TextAnchor childAlignment)
+ {
+ if (axis == 0)
+ return ((int)childAlignment % 3) * 0.5f;
+ else
+ return ((int)childAlignment / 3) * 0.5f;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Helper/GridLayoutCalculator.cs.meta b/Runtime/Helper/GridLayoutCalculator.cs.meta
new file mode 100644
index 0000000..58fd450
--- /dev/null
+++ b/Runtime/Helper/GridLayoutCalculator.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 4f08db3ea0984233a1e58bbea59a576e
+timeCreated: 1776324158
\ No newline at end of file
diff --git a/Runtime/Helper/UIVesselHelp.cs b/Runtime/Helper/UIVesselHelp.cs
new file mode 100644
index 0000000..8308037
--- /dev/null
+++ b/Runtime/Helper/UIVesselHelp.cs
@@ -0,0 +1,19 @@
+using XericUI.Core.Interface;
+
+namespace XericUI.Helper
+{
+ public static class UIVesselHelper
+ {
+ public static void CallVisibal(this IUIVesselVisibalControl control, bool underVisible)
+ {
+ if (underVisible)
+ {
+ control.UnderVisibal();
+ }
+ else
+ {
+ control.OutOfVisibal();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Helper/UIVesselHelp.cs.meta b/Runtime/Helper/UIVesselHelp.cs.meta
new file mode 100644
index 0000000..b1c2e67
--- /dev/null
+++ b/Runtime/Helper/UIVesselHelp.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 05b1a77f026f46a5b6744ad2e8486164
+timeCreated: 1776309745
\ No newline at end of file
diff --git a/Runtime/OverrideLayout/XericScrollRect.cs b/Runtime/OverrideLayout/XericScrollRect.cs
index 5b699e6..8228d56 100644
--- a/Runtime/OverrideLayout/XericScrollRect.cs
+++ b/Runtime/OverrideLayout/XericScrollRect.cs
@@ -2,8 +2,10 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
+using UnityEngine.Serialization;
using UnityEngine.UI;
using XericLibrary.Runtime.MacroLibrary;
+using XericUI.Core.Interface;
using XericUI.Helper;
#if UNITY_EDITOR
@@ -23,58 +25,95 @@ namespace XericUI.OverrideLayout
[RequireComponent(typeof(RectTransform))]
public class XericScrollRect : ScrollRect
{
- #region 事件委托
-
- // public Action
-
- #endregion
-
#region 字段属性
- ///
- /// 启用自动剔除功能
- ///
- public bool enableAutoActiveByMaskVisible = true;
+ [Tooltip("启用自动剔除功能")] [SerializeField]
+ protected bool m_enableAdvancedFunc = true;
+
+ [Tooltip("容器预加载像素范围")] [SerializeField]
+ protected float m_preloadMargin = 200f;
+
+ [Tooltip("容器加载响应可被察觉的变化长度")] [SerializeField]
+ protected float m_discernibleDistance = 10f;
+
+ [Tooltip("自动剔除更新速率,当设定趋近零时,实际上是期望每帧执行更新")] [SerializeField]
+ protected float m_AAMVUpdateRate = .5f;
+
+ // grid布局组件
+ [SerializeField] protected Vector2 m_cellSize = new Vector2(100, 100);
+ [SerializeField] protected Vector2 m_spacing = Vector2.zero;
+ [SerializeField] protected RectOffset m_padding;
+ [SerializeField] protected GridLayoutGroup.Corner m_startCorner = GridLayoutGroup.Corner.UpperLeft;
+ [SerializeField] protected GridLayoutGroup.Axis m_startAxis = GridLayoutGroup.Axis.Horizontal;
+ [SerializeField] protected TextAnchor m_childAlignment = TextAnchor.UpperLeft;
+ [SerializeField] protected GridLayoutGroup.Constraint m_constraint = GridLayoutGroup.Constraint.Flexible;
///
- /// 容器布局
+ /// 对象池
///
- public LayoutGroup layoutGroup;
+ [SerializeField] protected MacroPool.UnionSet childrenPool = new MacroPool.UnionSet();
///
- /// 容器预加载像素范围
+ /// 对象数据
///
- public float preloadMargin = 200f;
-
- ///
- /// 容器加载响应可被察觉的变化长度
- ///
- public float discernibleDistance = 10f;
-
- ///
- /// 自动剔除更新速率
- ///
- [Tooltip("当设定满速率时,实际上是期望每帧执行更新")]
- // ReSharper disable once InconsistentNaming
- [Range(0.02f, 1)]
- public float AAMVUpdateRate = .9f;
+ [SerializeField] private readonly List _childrenDatas = ListPool.Get();
+ public Vector2 ContextSize
+ {
+ get => new Vector2(content.rect.width, content.rect.height);
+ set
+ {
+ UpdateContentLayout(0, value.x);
+ UpdateContentLayout(1, value.y);
+ }
+ }
+
#endregion
#region 布局参考
+ [SerializeField] private LayoutGroup layoutGroup;
+
private float _aamvUpdateTime;
private Vector2 _lastContextSize;
private Vector2 _lastContextAnchoredPosition;
- // 脏标记处理率,每次激活脏标记时给予多份权重,每次空处理则返还一部分权重,如果出现标记大于0的情况,说明脏标记处理次数太多
- private float _dirtyTakeRate;
+ private Queue _activeQueue = new Queue();
- private HashSet _lastActiveSet;
-
#endregion
+ [Serializable]
+ public class ScrollItemData : IUIVesselVisibalControl
+ {
+ public GridLayoutCalculator.LayoutItem layout;
+
+ protected internal XericScrollRect parentScrollRect;
+ private bool _uiVisibal;
+
+ protected GameObject item;
+
+ public bool GetUIVisibal => _uiVisibal;
+
+ public virtual void UnderVisibal()
+ {
+ item = parentScrollRect.childrenPool.Get();
+ var rectTransform = item.RectTransform();
+ parentScrollRect.UpdateChildPivot(layout, item.RectTransform());
+ rectTransform.anchoredPosition = layout.position;
+ }
+
+ public virtual void OutOfVisibal()
+ {
+ parentScrollRect.childrenPool.DirectlyRelease(item);
+ }
+
+ public void UpdateLayout(GridLayoutCalculator.LayoutItem layoutData)
+ {
+ layout = layoutData;
+ }
+ }
+
#region 生命周期
#if UNITY_EDITOR
@@ -84,34 +123,57 @@ namespace XericUI.OverrideLayout
private void OnDrawGizmosSelected()
{
var lossyScale = transform.lossyScale;
- var scale = lossyScale * preloadMargin;
- var dis2 = lossyScale * discernibleDistance;
-
+ var scale = lossyScale * m_preloadMargin;
+ var dis2 = lossyScale * m_discernibleDistance;
+
content.GetWorldCorners(vertex1);
viewport.GetWorldCorners(vertex2);
var rect = new Rect(vertex2[0], new Vector2(vertex2[2].x - vertex2[0].x, vertex2[2].y - vertex2[0].y));
var viewportMin = rect.min * lossyScale;
var viewportMax = rect.max * lossyScale;
+
// vertex3[0] = new Vector3(vertex2[0].x - scale.x, vertex2[0].y - scale.y, vertex2[0].z);
// vertex3[1] = new Vector3(vertex2[1].x - scale.x, vertex2[1].y + scale.y, vertex2[1].z);
// vertex3[2] = new Vector3(vertex2[2].x + scale.x, vertex2[2].y + scale.y, vertex2[2].z);
// vertex3[3] = new Vector3(vertex2[3].x + scale.x, vertex2[3].y - scale.y, vertex2[3].z);
-
- vertex3[0] = new Vector3(rect.min.x - scale.x, rect.min.y - scale.y, vertex2[0].z);
- vertex3[1] = new Vector3(rect.min.x - scale.x, rect.max.y + scale.y, vertex2[1].z);
- vertex3[2] = new Vector3(rect.max.x + scale.x, rect.max.y + scale.y, vertex2[2].z);
- vertex3[3] = new Vector3(rect.max.x + scale.x, rect.min.y - scale.y, vertex2[3].z);
-
+
+ vertex3[0] = new Vector3(horizontal ? rect.min.x - scale.x : rect.min.x,
+ vertical ? rect.min.y - scale.y : rect.min.y, vertex2[0].z);
+ vertex3[1] = new Vector3(horizontal ? rect.min.x - scale.x : rect.min.x,
+ vertical ? rect.max.y + scale.y : rect.max.y, vertex2[1].z);
+ vertex3[2] = new Vector3(horizontal ? rect.max.x + scale.x : rect.max.x,
+ vertical ? rect.max.y + scale.y : rect.max.y, vertex2[2].z);
+ vertex3[3] = new Vector3(horizontal ? rect.max.x + scale.x : rect.max.x,
+ vertical ? rect.min.y - scale.y : rect.min.y, vertex2[3].z);
+
Gizmos.color = Color.yellow;
- MacroGizmosDraw.DrawGizmos2RectLinkCube(vertex1, vertex3, Color.grey, Color.cyan, true);
-
+ MacroGizmosDraw.DrawGizmos2RectLinkCube(vertex3, vertex1, Color.cyan, Color.red, true);
+ MacroGizmosDraw.Label(vertex3[1], "preload Rect");
+ MacroGizmosDraw.Label(vertex1[1], "display Rect");
}
#endif
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+ if (childrenPool.Parent == null)
+ childrenPool.Parent = content;
+
+ var layouts = GetChildrenLayout(content.transform.childCount);
+ for (int i = 0; i < layouts.Count; i++)
+ {
+ var trans = content.transform.GetChild(i) as RectTransform;
+ UpdateChildPivot(layouts[i], trans);
+ trans.anchoredPosition = layouts[i].position;
+ }
+
+ RefreshContentSizeByChildrenLayouts(layouts);
+ }
+
protected override void Awake()
{
base.Awake();
-
+
// 布局
if (layoutGroup == null)
layoutGroup = transform.GetComponentInParent();
@@ -121,113 +183,244 @@ namespace XericUI.OverrideLayout
}
}
- protected override void LateUpdate()
+ protected override void Start()
{
- if (!enableAutoActiveByMaskVisible)
- {
- base.LateUpdate();
- return;
- }
+ base.Start();
+ childrenPool.Initialize();
+ }
+
+ protected override void SetContentAnchoredPosition(Vector2 position)
+ {
+ base.SetContentAnchoredPosition(position);
+ // 如果不启动就直接跳过
+ if (!m_enableAdvancedFunc) return;
// 计算更新速率
- var targetFrameRate = Application.targetFrameRate;
- var frameAamvRate = Time.deltaTime / Mathf.Max(0.02f, AAMVUpdateRate);
- _dirtyTakeRate = Mathf.Max(_dirtyTakeRate - AAMVUpdateRate, 0);
- if (AAMVUpdateRate < 1)
+ if (m_AAMVUpdateRate <= 0)
+ Next();
+ else if ((_aamvUpdateTime += Time.deltaTime) >= m_AAMVUpdateRate)
{
- if (_aamvUpdateTime < frameAamvRate)
+ // var targetFrameRate = Application.targetFrameRate;
+ _aamvUpdateTime %= m_AAMVUpdateRate;
+ Next();
+ }
+
+
+ void Next()
+ {
+ // 记录移动矢量
+ var vector = position - _lastContextAnchoredPosition;
+
+ // 移动矢量距离大于可查觉距离
+ if (vector.sqrMagnitude > m_discernibleDistance * m_discernibleDistance)
{
- _aamvUpdateTime += Time.deltaTime;
- base.LateUpdate();
- return;
+ // 只有距离大于这个才会触发更新
+ _lastContextAnchoredPosition = position;
+ UpdateAAMVSVisibleStatus(position);
}
- _aamvUpdateTime %= frameAamvRate;
}
-
- var sqrDistance = MacroMath.SqrMagnitudeDistance(content.anchoredPosition, _lastContextAnchoredPosition);
- if (sqrDistance > discernibleDistance * discernibleDistance)
- {
- _lastContextAnchoredPosition = content.anchoredPosition;
- SetAAMVDirty();
- }
-
- base.LateUpdate();
}
private Vector3[] viewportVertex = new Vector3[4];
+
///
/// 自动剔除更新标记
///
- protected void SetAAMVDirty()
+ protected void UpdateAAMVSVisibleStatus(Vector2 position)
{
- _dirtyTakeRate += AAMVUpdateRate * 2;
-
- var nowActiveChildrenSet = HashSetPool.Get();
-
// 将视口尺寸转换到content本地空间
// 手动计算视口在content空间的矩形
viewport.GetWorldCorners(viewportVertex);
var viewportRect = new Rect(
content.InverseTransformPoint(viewportVertex[0]),
content.InverseTransformPoint(viewportVertex[2]) - content.InverseTransformPoint(viewportVertex[0]));
+
var contentRect = content.rect;
-
// 计算预加载边距(考虑content缩放)
- var scaledMargin = preloadMargin / ((content.lossyScale.x + content.lossyScale.y) * 0.5f);
-
+ // var scaledMargin = preloadMargin / ((content.lossyScale.x + content.lossyScale.y) * 0.5f);
+
// 扩展可见区域边界
- var visibleMin = viewportRect.min - new Vector2(preloadMargin, preloadMargin);
- var visibleMax = viewportRect.max + new Vector2(preloadMargin, preloadMargin);
-
+ var visibleMin = viewportRect.min - new Vector2(m_preloadMargin, m_preloadMargin);
+ var visibleMax = viewportRect.max + new Vector2(m_preloadMargin, m_preloadMargin);
+
// 获取当前滚动归一化位置
- var scrollPos = new Vector2(
- horizontal ? horizontalScrollbar?.value ?? 0 : 0,
- vertical ? 1 - (verticalScrollbar?.value ?? 1) : 0);
-
- var isinit = _lastActiveSet == null;
-
- foreach (var child in content.GetChildren())
+ // var scrollPos = new Vector2(
+ // horizontal ? horizontalScrollbar?.value ?? 0 : 0,
+ // vertical ? 1 - (verticalScrollbar?.value ?? 1) : 0);
+
+ // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop
+ foreach (RectTransform child in content.GetChildren())
{
+ var visControl = child.GetComponent();
+ var isVisControlValid = visControl != null;
+
var rectTransform = child.RectTransform();
// 直接使用anchoredPosition进行边界检测
var childPos = rectTransform.anchoredPosition;
- var isVisible =
- childPos.x >= visibleMin.x &&
+ var isVisible =
+ childPos.x >= visibleMin.x &&
childPos.x <= visibleMax.x &&
- childPos.y >= visibleMin.y &&
+ childPos.y >= visibleMin.y &&
childPos.y <= visibleMax.y;
- var isActive = isinit || _lastActiveSet.Contains(child);
- if (isVisible)
- nowActiveChildrenSet.Add(child);
- if (isVisible == isActive)
+ var nowActive = child.GetActivity();
+
+ // 以接口为准
+ if (isVisControlValid)
+ nowActive = visControl.GetUIVisibal;
+
+ // 现在的状态与实际一致,跳过
+ if (isVisible == nowActive)
continue;
-
- child.gameObject.SetActive(isVisible);
+
+ // 向成员发送状态(如果成员没有实现接口,就直接通过可见性控制)
+ if (isVisControlValid)
+ visControl.CallVisibal(isVisible);
+ else
+ child.gameObject.SetActive(isVisible);
}
-
- if (!isinit)
- HashSetPool.Release(_lastActiveSet);
- _lastActiveSet = nowActiveChildrenSet;
}
-
- protected override void SetContentAnchoredPosition(Vector2 position)
- {
- base.SetContentAnchoredPosition(position);
- Debug.Log("123");
- }
-
#endregion
+
+ #region 数据管理
+
+ public T GetScrollItemData()
+ where T : ScrollItemData, new()
+ {
+ var result = new T
+ {
+ parentScrollRect = this
+ };
+ return result;
+ }
+
+ public void AddChildData(ScrollItemData Data)
+ {
+ _childrenDatas.Add(Data);
+ }
+
+ public void RemoveChildData(int index)
+ {
+ }
+
+ public void RemoveChildData(ScrollItemData index)
+ {
+ }
+
+ public ScrollItemData GetChildData(int index)
+ {
+ return _childrenDatas[index];
+ }
+
+ #endregion
+
+ #region 布局计算
+
+ private void UpdateContentLayout(int axis, float overrideSize)
+ {
+ content.SetSizeWithCurrentAnchors((RectTransform.Axis)axis, overrideSize);
+ // m_HorizontalFit == ContentSizeFitter.FitMode.MinSize ?
+ // LayoutUtility.GetMinSize(content, axis) :
+ // LayoutUtility.GetPreferredSize(content, axis));
+ }
+
+ private void RefreshContentSizeByChildrenLayouts(List layoutItems)
+ {
+ var first = layoutItems[0];
+ var last = layoutItems[^1];
+
+ var size = first.position - last.position;
+ size = size.Abs();
+ var spacing = new Vector2(first.columnIndex - last.columnIndex * m_spacing.x, first.rowIndex - last.rowIndex * m_spacing.y);
+ switch (m_startAxis)
+ {
+ case GridLayoutGroup.Axis.Horizontal:
+ var ySpacing = first.rowIndex - last.rowIndex * m_spacing.y;
+ size.x = Mathf.Max(size.x, viewport.rect.width);
+ size.y = Mathf.Max(size.y + spacing.y, m_cellSize.y);
+ break;
+ case GridLayoutGroup.Axis.Vertical:
+ var xSpacing = last.columnIndex * m_spacing.x;
+ size.x = Mathf.Max(size.x + spacing.x, m_cellSize.x);
+ size.y = Mathf.Max(size.y, viewport.rect.height);
+ break;
+ }
+ ContextSize = size;
+ }
+
+ internal void UpdateChildPivot(GridLayoutCalculator.LayoutItem layoutItems, RectTransform rectTransform)
+ {
+ rectTransform.anchorMax = rectTransform.anchorMin = m_startCorner switch
+ {
+ // todo 对齐算法总的方向应该是写错了,找找
+ GridLayoutGroup.Corner.UpperLeft => new Vector2(0, 1),
+ GridLayoutGroup.Corner.UpperRight => new Vector2(1, 1),
+ GridLayoutGroup.Corner.LowerLeft => new Vector2(0, 0),
+ GridLayoutGroup.Corner.LowerRight => new Vector2(1, 0),
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ // rectTransform.pivot = m_childAlignment switch
+ // {
+ // TextAnchor.UpperLeft => new Vector2(0, 1),
+ // TextAnchor.UpperCenter => new Vector2(.5f, 1),
+ // TextAnchor.UpperRight => new Vector2(1, 1),
+ // TextAnchor.MiddleLeft => new Vector2(0, .5f),
+ // TextAnchor.MiddleCenter => new Vector2(.5f, .5f),
+ // TextAnchor.MiddleRight => new Vector2(1, .5f),
+ // TextAnchor.LowerLeft => new Vector2(0, 0),
+ // TextAnchor.LowerCenter => new Vector2(.5f, 0),
+ // TextAnchor.LowerRight => new Vector2(1, 0),
+ // _ => throw new ArgumentOutOfRangeException()
+ // };
+ }
+
+ private void GetOffset(List layoutItems)
+ {
+ var first = layoutItems[0];
+ var last = layoutItems[^1];
+
+ // first.position
+ }
+ private List GetChildrenLayout(int childrenCount)
+ => GridLayoutCalculator.CalculateGridLayout(
+ childCount: childrenCount,
+ containerSize: ContextSize,
+ cellSize: m_cellSize,
+ spacing: m_spacing,
+ padding: m_padding,
+ startCorner: m_startCorner,
+ startAxis: m_startAxis,
+ constraint: m_constraint,
+ constraintCount: 0,
+ childAlignment: m_childAlignment
+ );
+
+ public void RefreshChildrenLayout()
+ {
+ var layoutItems = GetChildrenLayout(_childrenDatas.Count);
+ RefreshContentSizeByChildrenLayouts(layoutItems);
+ // 应用计算结果
+ for (int i = 0; i < layoutItems.Count; i++)
+ {
+ var item = layoutItems[i];
+ _childrenDatas[i].UpdateLayout(item);
+ Debug.Log($"Item {i}: Position{item.position}, Row{item.rowIndex}, Column{item.columnIndex}");
+ }
+ }
+
+ #endregion
+
#region 组件替换
+
#if UNITY_EDITOR
[UnityEditor.MenuItem("CONTEXT/ScrollRect/Replace To XericScrollView", true)]
private static bool ValidateReplaceToXeric(UnityEditor.MenuCommand command)
=> command.context is ScrollRect and not XericScrollRect;
[UnityEditor.MenuItem("CONTEXT/ScrollRect/Replace To XericScrollView", false, 10)]
- private static void ReplaceToXeric(UnityEditor.MenuCommand command)
+ private static void ReplaceToXeric(UnityEditor.MenuCommand command)
=> command.ReplaceCommandContext(
o =>
@@ -245,7 +438,7 @@ namespace XericUI.OverrideLayout
=> command.context is XericScrollRect;
[UnityEditor.MenuItem("CONTEXT/ScrollRect/Replace To ScrollView", false, 10)]
- private static void ReplaceToUnity(UnityEditor.MenuCommand command)
+ private static void ReplaceToUnity(UnityEditor.MenuCommand command)
=> command.ReplaceCommandContext(
o =>
@@ -258,6 +451,7 @@ namespace XericUI.OverrideLayout
t.verticalScrollbar = d.VScrollbar;
});
#endif
+
#endregion
}
}
\ No newline at end of file