mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 02:36:14 +00:00
提交Unity 联机Pro
This commit is contained in:
304
JNFrame2/Assets/Plugins/SRF/Scripts/UI/Layout/FlowLayoutGroup.cs
Normal file
304
JNFrame2/Assets/Plugins/SRF/Scripts/UI/Layout/FlowLayoutGroup.cs
Normal file
@@ -0,0 +1,304 @@
|
||||
namespace SRF.UI.Layout
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Layout Group controller that arranges children in rows, fitting as many on a line until total width exceeds parent
|
||||
/// bounds
|
||||
/// </summary>
|
||||
[AddComponentMenu(ComponentMenuPaths.FlowLayoutGroup)]
|
||||
public class FlowLayoutGroup : LayoutGroup
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the rects that will make up the current row being processed
|
||||
/// </summary>
|
||||
private readonly IList<RectTransform> _rowList = new List<RectTransform>();
|
||||
|
||||
private float _layoutHeight;
|
||||
public bool ChildForceExpandHeight = false;
|
||||
public bool ChildForceExpandWidth = false;
|
||||
public float Spacing = 0f;
|
||||
|
||||
protected bool IsCenterAlign
|
||||
{
|
||||
get
|
||||
{
|
||||
return childAlignment == TextAnchor.LowerCenter || childAlignment == TextAnchor.MiddleCenter ||
|
||||
childAlignment == TextAnchor.UpperCenter;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsRightAlign
|
||||
{
|
||||
get
|
||||
{
|
||||
return childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.MiddleRight ||
|
||||
childAlignment == TextAnchor.UpperRight;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsMiddleAlign
|
||||
{
|
||||
get
|
||||
{
|
||||
return childAlignment == TextAnchor.MiddleLeft || childAlignment == TextAnchor.MiddleRight ||
|
||||
childAlignment == TextAnchor.MiddleCenter;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsLowerAlign
|
||||
{
|
||||
get
|
||||
{
|
||||
return childAlignment == TextAnchor.LowerLeft || childAlignment == TextAnchor.LowerRight ||
|
||||
childAlignment == TextAnchor.LowerCenter;
|
||||
}
|
||||
}
|
||||
|
||||
public override void CalculateLayoutInputHorizontal()
|
||||
{
|
||||
base.CalculateLayoutInputHorizontal();
|
||||
|
||||
var minWidth = GetGreatestMinimumChildWidth() + padding.left + padding.right;
|
||||
|
||||
SetLayoutInputForAxis(minWidth, -1, -1, 0);
|
||||
}
|
||||
|
||||
public override void SetLayoutHorizontal()
|
||||
{
|
||||
SetLayout(rectTransform.rect.width, 0, false);
|
||||
}
|
||||
|
||||
public override void SetLayoutVertical()
|
||||
{
|
||||
SetLayout(rectTransform.rect.width, 1, false);
|
||||
}
|
||||
|
||||
public override void CalculateLayoutInputVertical()
|
||||
{
|
||||
_layoutHeight = SetLayout(rectTransform.rect.width, 1, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main layout method
|
||||
/// </summary>
|
||||
/// <param name="width">Width to calculate the layout with</param>
|
||||
/// <param name="axis">0 for horizontal axis, 1 for vertical</param>
|
||||
/// <param name="layoutInput">If true, sets the layout input for the axis. If false, sets child position for axis</param>
|
||||
public float SetLayout(float width, int axis, bool layoutInput)
|
||||
{
|
||||
var groupHeight = rectTransform.rect.height;
|
||||
|
||||
// Width that is available after padding is subtracted
|
||||
var workingWidth = rectTransform.rect.width - padding.left - padding.right;
|
||||
|
||||
// Accumulates the total height of the rows, including spacing and padding.
|
||||
var yOffset = IsLowerAlign ? padding.bottom : (float)padding.top;
|
||||
|
||||
var currentRowWidth = 0f;
|
||||
var currentRowHeight = 0f;
|
||||
|
||||
for (var i = 0; i < rectChildren.Count; i++)
|
||||
{
|
||||
// LowerAlign works from back to front
|
||||
var index = IsLowerAlign ? rectChildren.Count - 1 - i : i;
|
||||
|
||||
var child = rectChildren[index];
|
||||
|
||||
var childWidth = LayoutUtility.GetPreferredSize(child, 0);
|
||||
var childHeight = LayoutUtility.GetPreferredSize(child, 1);
|
||||
|
||||
// Max child width is layout group with - padding
|
||||
childWidth = Mathf.Min(childWidth, workingWidth);
|
||||
|
||||
// Apply spacing if not the first element in a row
|
||||
if (_rowList.Count > 0)
|
||||
{
|
||||
currentRowWidth += Spacing;
|
||||
}
|
||||
|
||||
// If adding this element would exceed the bounds of the row,
|
||||
// go to a new line after processing the current row
|
||||
if (currentRowWidth + childWidth > workingWidth)
|
||||
{
|
||||
// Undo spacing addition if we're moving to a new line (Spacing is not applied on edges)
|
||||
currentRowWidth -= Spacing;
|
||||
|
||||
// Process current row elements positioning
|
||||
if (!layoutInput)
|
||||
{
|
||||
var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight);
|
||||
LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth, padding.left, h, axis);
|
||||
}
|
||||
|
||||
// Clear existing row
|
||||
_rowList.Clear();
|
||||
|
||||
// Add the current row height to total height accumulator, and reset to 0 for the next row
|
||||
yOffset += currentRowHeight;
|
||||
yOffset += Spacing;
|
||||
|
||||
currentRowHeight = 0;
|
||||
currentRowWidth = 0;
|
||||
}
|
||||
|
||||
currentRowWidth += childWidth;
|
||||
_rowList.Add(child);
|
||||
|
||||
// We need the largest element height to determine the starting position of the next line
|
||||
if (childHeight > currentRowHeight)
|
||||
{
|
||||
currentRowHeight = childHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (!layoutInput)
|
||||
{
|
||||
var h = CalculateRowVerticalOffset(groupHeight, yOffset, currentRowHeight);
|
||||
|
||||
// Layout the final row
|
||||
LayoutRow(_rowList, currentRowWidth, currentRowHeight, workingWidth, padding.left, h, axis);
|
||||
}
|
||||
|
||||
_rowList.Clear();
|
||||
|
||||
// Add the last rows height to the height accumulator
|
||||
yOffset += currentRowHeight;
|
||||
yOffset += IsLowerAlign ? padding.top : padding.bottom;
|
||||
|
||||
if (layoutInput)
|
||||
{
|
||||
if (axis == 1)
|
||||
{
|
||||
SetLayoutInputForAxis(yOffset, yOffset, -1, axis);
|
||||
}
|
||||
}
|
||||
|
||||
return yOffset;
|
||||
}
|
||||
|
||||
private float CalculateRowVerticalOffset(float groupHeight, float yOffset, float currentRowHeight)
|
||||
{
|
||||
float h;
|
||||
|
||||
if (IsLowerAlign)
|
||||
{
|
||||
h = groupHeight - yOffset - currentRowHeight;
|
||||
}
|
||||
else if (IsMiddleAlign)
|
||||
{
|
||||
h = groupHeight * 0.5f - _layoutHeight * 0.5f + yOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = yOffset;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
protected void LayoutRow(IList<RectTransform> contents, float rowWidth, float rowHeight, float maxWidth,
|
||||
float xOffset, float yOffset, int axis)
|
||||
{
|
||||
var xPos = xOffset;
|
||||
|
||||
if (!ChildForceExpandWidth && IsCenterAlign)
|
||||
{
|
||||
xPos += (maxWidth - rowWidth) * 0.5f;
|
||||
}
|
||||
else if (!ChildForceExpandWidth && IsRightAlign)
|
||||
{
|
||||
xPos += (maxWidth - rowWidth);
|
||||
}
|
||||
|
||||
var extraWidth = 0f;
|
||||
|
||||
if (ChildForceExpandWidth)
|
||||
{
|
||||
var flexibleChildCount = 0;
|
||||
|
||||
for (var i = 0; i < _rowList.Count; i++)
|
||||
{
|
||||
if (LayoutUtility.GetFlexibleWidth(_rowList[i]) > 0f)
|
||||
{
|
||||
flexibleChildCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (flexibleChildCount > 0)
|
||||
{
|
||||
extraWidth = (maxWidth - rowWidth) / flexibleChildCount;
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < _rowList.Count; j++)
|
||||
{
|
||||
var index = IsLowerAlign ? _rowList.Count - 1 - j : j;
|
||||
|
||||
var rowChild = _rowList[index];
|
||||
|
||||
var rowChildWidth = LayoutUtility.GetPreferredSize(rowChild, 0);
|
||||
|
||||
if (LayoutUtility.GetFlexibleWidth(rowChild) > 0f)
|
||||
{
|
||||
rowChildWidth += extraWidth;
|
||||
}
|
||||
|
||||
var rowChildHeight = LayoutUtility.GetPreferredSize(rowChild, 1);
|
||||
|
||||
if (ChildForceExpandHeight)
|
||||
{
|
||||
rowChildHeight = rowHeight;
|
||||
}
|
||||
|
||||
rowChildWidth = Mathf.Min(rowChildWidth, maxWidth);
|
||||
|
||||
var yPos = yOffset;
|
||||
|
||||
if (IsMiddleAlign)
|
||||
{
|
||||
yPos += (rowHeight - rowChildHeight) * 0.5f;
|
||||
}
|
||||
else if (IsLowerAlign)
|
||||
{
|
||||
yPos += (rowHeight - rowChildHeight);
|
||||
}
|
||||
|
||||
if (axis == 0)
|
||||
{
|
||||
#if UNITY_2019_1
|
||||
SetChildAlongAxis(rowChild, 0, 1f, xPos, rowChildWidth);
|
||||
#else
|
||||
SetChildAlongAxis(rowChild, 0, xPos, rowChildWidth);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if UNITY_2019_1
|
||||
SetChildAlongAxis(rowChild, 1, 1f, yPos, rowChildHeight);
|
||||
#else
|
||||
SetChildAlongAxis(rowChild, 1, yPos, rowChildHeight);
|
||||
#endif
|
||||
}
|
||||
|
||||
xPos += rowChildWidth + Spacing;
|
||||
}
|
||||
}
|
||||
|
||||
public float GetGreatestMinimumChildWidth()
|
||||
{
|
||||
var max = 0f;
|
||||
|
||||
for (var i = 0; i < rectChildren.Count; i++)
|
||||
{
|
||||
var w = LayoutUtility.GetMinWidth(rectChildren[i]);
|
||||
|
||||
max = Mathf.Max(w, max);
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3a5149e46522d84cb8079537220a929
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
@@ -0,0 +1,607 @@
|
||||
//#define PROFILE
|
||||
|
||||
namespace SRF.UI.Layout
|
||||
{
|
||||
using System;
|
||||
using Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public interface IVirtualView
|
||||
{
|
||||
void SetDataContext(object data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[AddComponentMenu(ComponentMenuPaths.VirtualVerticalLayoutGroup)]
|
||||
public class VirtualVerticalLayoutGroup : LayoutGroup, IPointerClickHandler
|
||||
{
|
||||
private readonly SRList<object> _itemList = new SRList<object>();
|
||||
private readonly SRList<int> _visibleItemList = new SRList<int>();
|
||||
|
||||
private bool _isDirty = false;
|
||||
private SRList<Row> _rowCache = new SRList<Row>();
|
||||
private ScrollRect _scrollRect;
|
||||
private int _selectedIndex;
|
||||
private object _selectedItem;
|
||||
|
||||
[SerializeField] private SelectedItemChangedEvent _selectedItemChanged;
|
||||
|
||||
private int _visibleItemCount;
|
||||
private SRList<Row> _visibleRows = new SRList<Row>();
|
||||
public StyleSheet AltRowStyleSheet;
|
||||
public bool EnableSelection = true;
|
||||
public RectTransform ItemPrefab;
|
||||
|
||||
/// <summary>
|
||||
/// Rows to show above and below the visible rect to reduce pop-in
|
||||
/// </summary>
|
||||
public int RowPadding = 2;
|
||||
|
||||
public StyleSheet RowStyleSheet;
|
||||
public StyleSheet SelectedRowStyleSheet;
|
||||
|
||||
/// <summary>
|
||||
/// Spacing to add between rows
|
||||
/// </summary>
|
||||
public float Spacing;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the scroll view will stick to the last element when fully scrolled to the bottom and an item is added
|
||||
/// </summary>
|
||||
public bool StickToBottom = true;
|
||||
|
||||
public SelectedItemChangedEvent SelectedItemChanged
|
||||
{
|
||||
get { return _selectedItemChanged; }
|
||||
set { _selectedItemChanged = value; }
|
||||
}
|
||||
|
||||
public object SelectedItem
|
||||
{
|
||||
get { return _selectedItem; }
|
||||
set
|
||||
{
|
||||
if (_selectedItem == value || !EnableSelection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newSelectedIndex = value == null ? -1 : _itemList.IndexOf(value);
|
||||
|
||||
// Ensure that the new selected item is present in the item list
|
||||
if (value != null && newSelectedIndex < 0)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot select item not present in layout");
|
||||
}
|
||||
|
||||
// Invalidate old selected item row
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
InvalidateItem(_selectedIndex);
|
||||
}
|
||||
|
||||
_selectedItem = value;
|
||||
_selectedIndex = newSelectedIndex;
|
||||
|
||||
// Invalidate the newly selected item
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
InvalidateItem(_selectedIndex);
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
|
||||
if (_selectedItemChanged != null)
|
||||
{
|
||||
_selectedItemChanged.Invoke(_selectedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override float minHeight
|
||||
{
|
||||
get { return _itemList.Count*ItemHeight + padding.top + padding.bottom + Spacing*_itemList.Count; }
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (!EnableSelection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hitObject = eventData.pointerPressRaycast.gameObject;
|
||||
|
||||
if (hitObject == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var hitPos = hitObject.transform.position;
|
||||
var localPos = rectTransform.InverseTransformPoint(hitPos);
|
||||
var row = Mathf.FloorToInt(Mathf.Abs(localPos.y)/ItemHeight);
|
||||
|
||||
if (row >= 0 && row < _itemList.Count)
|
||||
{
|
||||
SelectedItem = _itemList[row];
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
ScrollRect.onValueChanged.AddListener(OnScrollRectValueChanged);
|
||||
|
||||
var view = ItemPrefab.GetComponent(typeof (IVirtualView));
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
"[VirtualVerticalLayoutGroup] ItemPrefab does not have a component inheriting from IVirtualView, so no data binding can occur");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnScrollRectValueChanged(Vector2 d)
|
||||
{
|
||||
if (d.y < 0 || d.y > 1)
|
||||
{
|
||||
_scrollRect.verticalNormalizedPosition = Mathf.Clamp01(d.y);
|
||||
}
|
||||
|
||||
//CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
ScrollUpdate();
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
protected void Update()
|
||||
{
|
||||
if (!AlignBottom && !AlignTop)
|
||||
{
|
||||
Debug.LogWarning("[VirtualVerticalLayoutGroup] Only Lower or Upper alignment is supported.", this);
|
||||
childAlignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
|
||||
if (SelectedItem != null && !_itemList.Contains(SelectedItem))
|
||||
{
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
if (_isDirty)
|
||||
{
|
||||
_isDirty = false;
|
||||
ScrollUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidate a single row (before removing, or changing selection status)
|
||||
/// </summary>
|
||||
/// <param name="itemIndex"></param>
|
||||
protected void InvalidateItem(int itemIndex)
|
||||
{
|
||||
if (!_visibleItemList.Contains(itemIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_visibleItemList.Remove(itemIndex);
|
||||
|
||||
for (var i = 0; i < _visibleRows.Count; i++)
|
||||
{
|
||||
if (_visibleRows[i].Index == itemIndex)
|
||||
{
|
||||
RecycleRow(_visibleRows[i]);
|
||||
_visibleRows.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After removing or inserting a row, ensure that the cached indexes (used for layout) match up
|
||||
/// with the item index in the list
|
||||
/// </summary>
|
||||
protected void RefreshIndexCache()
|
||||
{
|
||||
for (var i = 0; i < _visibleRows.Count; i++)
|
||||
{
|
||||
_visibleRows[i].Index = _itemList.IndexOf(_visibleRows[i].Data);
|
||||
}
|
||||
}
|
||||
|
||||
protected void ScrollUpdate()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Debug.Log("[SRConsole] ScrollUpdate {0}".Fmt(Time.frameCount));
|
||||
|
||||
var pos = rectTransform.anchoredPosition;
|
||||
var startY = pos.y;
|
||||
|
||||
var viewHeight = ((RectTransform) ScrollRect.transform).rect.height;
|
||||
|
||||
// Determine the range of rows that should be visible
|
||||
var rowRangeLower = Mathf.FloorToInt(startY/(ItemHeight + Spacing));
|
||||
var rowRangeHigher = Mathf.CeilToInt((startY + viewHeight)/(ItemHeight + Spacing));
|
||||
|
||||
// Apply padding to reduce pop-in
|
||||
rowRangeLower -= RowPadding;
|
||||
rowRangeHigher += RowPadding;
|
||||
|
||||
rowRangeLower = Mathf.Max(0, rowRangeLower);
|
||||
rowRangeHigher = Mathf.Min(_itemList.Count, rowRangeHigher);
|
||||
|
||||
var isDirty = false;
|
||||
|
||||
#if PROFILE
|
||||
Profiler.BeginSample("Visible Rows Cull");
|
||||
#endif
|
||||
|
||||
for (var i = 0; i < _visibleRows.Count; i++)
|
||||
{
|
||||
var row = _visibleRows[i];
|
||||
|
||||
// Move on if row is still visible
|
||||
if (row.Index >= rowRangeLower && row.Index <= rowRangeHigher)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_visibleItemList.Remove(row.Index);
|
||||
_visibleRows.Remove(row);
|
||||
RecycleRow(row);
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
#if PROFILE
|
||||
Profiler.EndSample();
|
||||
Profiler.BeginSample("Item Visible Check");
|
||||
#endif
|
||||
|
||||
for (var i = rowRangeLower; i < rowRangeHigher; ++i)
|
||||
{
|
||||
if (i >= _itemList.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Move on if row is already visible
|
||||
if (_visibleItemList.Contains(i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var row = GetRow(i);
|
||||
_visibleRows.Add(row);
|
||||
_visibleItemList.Add(i);
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
#if PROFILE
|
||||
Profiler.EndSample();
|
||||
#endif
|
||||
|
||||
// If something visible has explicitly been changed, or the visible row count has changed
|
||||
if (isDirty || _visibleItemCount != _visibleRows.Count)
|
||||
{
|
||||
//Debug.Log("[SRConsole] IsDirty {0}".Fmt(Time.frameCount));
|
||||
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
|
||||
}
|
||||
|
||||
_visibleItemCount = _visibleRows.Count;
|
||||
}
|
||||
|
||||
public override void CalculateLayoutInputVertical()
|
||||
{
|
||||
SetLayoutInputForAxis(minHeight, minHeight, -1, 1);
|
||||
}
|
||||
|
||||
public override void SetLayoutHorizontal()
|
||||
{
|
||||
var width = rectTransform.rect.width - padding.left - padding.right;
|
||||
|
||||
// Position visible rows at 0 x
|
||||
for (var i = 0; i < _visibleRows.Count; i++)
|
||||
{
|
||||
var item = _visibleRows[i];
|
||||
|
||||
SetChildAlongAxis(item.Rect, 0, padding.left, width);
|
||||
}
|
||||
|
||||
// Hide non-active rows to one side. More efficient than enabling/disabling them
|
||||
for (var i = 0; i < _rowCache.Count; i++)
|
||||
{
|
||||
var item = _rowCache[i];
|
||||
|
||||
SetChildAlongAxis(item.Rect, 0, -width - padding.left, width);
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetLayoutVertical()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Debug.Log("[SRConsole] SetLayoutVertical {0}".Fmt(Time.frameCount));
|
||||
|
||||
// Position visible rows by the index of the item they represent
|
||||
for (var i = 0; i < _visibleRows.Count; i++)
|
||||
{
|
||||
var item = _visibleRows[i];
|
||||
|
||||
SetChildAlongAxis(item.Rect, 1, item.Index*ItemHeight + padding.top + Spacing*item.Index, ItemHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private new void SetDirty()
|
||||
{
|
||||
base.SetDirty();
|
||||
|
||||
if (!IsActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDirty = true;
|
||||
//CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SelectedItemChangedEvent : UnityEvent<object> {}
|
||||
|
||||
[Serializable]
|
||||
private class Row
|
||||
{
|
||||
public object Data;
|
||||
public int Index;
|
||||
public RectTransform Rect;
|
||||
public StyleRoot Root;
|
||||
public IVirtualView View;
|
||||
}
|
||||
|
||||
#region Public Data Methods
|
||||
|
||||
public void AddItem(object item)
|
||||
{
|
||||
_itemList.Add(item);
|
||||
SetDirty();
|
||||
|
||||
if (StickToBottom && Mathf.Approximately(ScrollRect.verticalNormalizedPosition, 0f))
|
||||
{
|
||||
ScrollRect.normalizedPosition = new Vector2(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveItem(object item)
|
||||
{
|
||||
if (SelectedItem == item)
|
||||
{
|
||||
SelectedItem = null;
|
||||
}
|
||||
|
||||
var index = _itemList.IndexOf(item);
|
||||
|
||||
InvalidateItem(index);
|
||||
_itemList.Remove(item);
|
||||
|
||||
RefreshIndexCache();
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
public void ClearItems()
|
||||
{
|
||||
for (var i = _visibleRows.Count - 1; i >= 0; i--)
|
||||
{
|
||||
InvalidateItem(_visibleRows[i].Index);
|
||||
}
|
||||
|
||||
_itemList.Clear();
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Properties
|
||||
|
||||
private ScrollRect ScrollRect
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_scrollRect == null)
|
||||
{
|
||||
_scrollRect = GetComponentInParent<ScrollRect>();
|
||||
}
|
||||
|
||||
return _scrollRect;
|
||||
}
|
||||
}
|
||||
|
||||
private bool AlignBottom
|
||||
{
|
||||
get
|
||||
{
|
||||
return childAlignment == TextAnchor.LowerRight || childAlignment == TextAnchor.LowerCenter ||
|
||||
childAlignment == TextAnchor.LowerLeft;
|
||||
}
|
||||
}
|
||||
|
||||
private bool AlignTop
|
||||
{
|
||||
get
|
||||
{
|
||||
return childAlignment == TextAnchor.UpperLeft || childAlignment == TextAnchor.UpperCenter ||
|
||||
childAlignment == TextAnchor.UpperRight;
|
||||
}
|
||||
}
|
||||
|
||||
private float _itemHeight = -1;
|
||||
|
||||
private float ItemHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_itemHeight <= 0)
|
||||
{
|
||||
var layoutElement = ItemPrefab.GetComponent(typeof (ILayoutElement)) as ILayoutElement;
|
||||
|
||||
if (layoutElement != null)
|
||||
{
|
||||
_itemHeight = layoutElement.preferredHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
_itemHeight = ItemPrefab.rect.height;
|
||||
}
|
||||
|
||||
if (_itemHeight.ApproxZero())
|
||||
{
|
||||
Debug.LogWarning(
|
||||
"[VirtualVerticalLayoutGroup] ItemPrefab must have a preferred size greater than 0");
|
||||
_itemHeight = 10;
|
||||
}
|
||||
}
|
||||
|
||||
return _itemHeight;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Row Pooling and Provisioning
|
||||
|
||||
private Row GetRow(int forIndex)
|
||||
{
|
||||
// If there are no rows available in the cache, create one from scratch
|
||||
if (_rowCache.Count == 0)
|
||||
{
|
||||
var newRow = CreateRow();
|
||||
PopulateRow(forIndex, newRow);
|
||||
return newRow;
|
||||
}
|
||||
|
||||
var data = _itemList[forIndex];
|
||||
|
||||
Row row = null;
|
||||
Row altRow = null;
|
||||
|
||||
// Determine if the row we're looking for is an alt row
|
||||
var target = forIndex%2;
|
||||
|
||||
// Try and find a row which previously had this data, so we can reuse it
|
||||
for (var i = 0; i < _rowCache.Count; i++)
|
||||
{
|
||||
row = _rowCache[i];
|
||||
|
||||
// If this row previously represented this data, just use that one.
|
||||
if (row.Data == data)
|
||||
{
|
||||
_rowCache.RemoveAt(i);
|
||||
PopulateRow(forIndex, row);
|
||||
break;
|
||||
}
|
||||
|
||||
// Cache a row which is was the same alt state as the row we're looking for, in case
|
||||
// we don't find an exact match.
|
||||
if (row.Index%2 == target)
|
||||
{
|
||||
altRow = row;
|
||||
}
|
||||
|
||||
// Didn't match, reset to null
|
||||
row = null;
|
||||
}
|
||||
|
||||
// If an exact match wasn't found, but a row with the same alt-status was found, use that one.
|
||||
if (row == null && altRow != null)
|
||||
{
|
||||
_rowCache.Remove(altRow);
|
||||
row = altRow;
|
||||
PopulateRow(forIndex, row);
|
||||
}
|
||||
else if (row == null)
|
||||
{
|
||||
// No match found, use the last added item in the cache
|
||||
row = _rowCache.PopLast();
|
||||
PopulateRow(forIndex, row);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
private void RecycleRow(Row row)
|
||||
{
|
||||
_rowCache.Add(row);
|
||||
}
|
||||
|
||||
private void PopulateRow(int index, Row row)
|
||||
{
|
||||
row.Index = index;
|
||||
|
||||
// Set data context on row
|
||||
row.Data = _itemList[index];
|
||||
row.View.SetDataContext(_itemList[index]);
|
||||
|
||||
// If we're using stylesheets
|
||||
if (RowStyleSheet != null || AltRowStyleSheet != null || SelectedRowStyleSheet != null)
|
||||
{
|
||||
// If there is a selected row stylesheet, and this is the selected row, use that one
|
||||
if (SelectedRowStyleSheet != null && SelectedItem == row.Data)
|
||||
{
|
||||
row.Root.StyleSheet = SelectedRowStyleSheet;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise just use the stylesheet suitable for the row alt-status
|
||||
row.Root.StyleSheet = index%2 == 0 ? RowStyleSheet : AltRowStyleSheet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Row CreateRow()
|
||||
{
|
||||
var item = new Row();
|
||||
|
||||
var row = SRInstantiate.Instantiate(ItemPrefab);
|
||||
item.Rect = row;
|
||||
item.View = row.GetComponent(typeof (IVirtualView)) as IVirtualView;
|
||||
|
||||
if (RowStyleSheet != null || AltRowStyleSheet != null || SelectedRowStyleSheet != null)
|
||||
{
|
||||
item.Root = row.gameObject.GetComponentOrAdd<StyleRoot>();
|
||||
item.Root.StyleSheet = RowStyleSheet;
|
||||
}
|
||||
|
||||
row.SetParent(rectTransform, false);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19c408e3f064e184fa5e0d9862ac4d8b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
Reference in New Issue
Block a user