Tanoda
FancyScrollView.cs
Go to the documentation of this file.
1
3
4using System.Collections.Generic;
5
7{
16 public abstract class FancyScrollView<TItemData, TContext> : MonoBehaviour where TContext : class, new()
17 {
21 [SerializeField, Range(1e-2f, 1f)] protected float cellInterval = 0.2f;
22
29 [SerializeField, Range(0f, 1f)] protected float scrollOffset = 0.5f;
30
38 [SerializeField] protected bool loop = false;
39
43 [SerializeField] protected Transform cellContainer = default;
44
45 readonly IList<FancyCell<TItemData, TContext>> pool = new List<FancyCell<TItemData, TContext>>();
46
50 protected bool initialized;
51
55 protected float currentPosition;
56
60 protected abstract GameObject CellPrefab { get; }
61
65 protected IList<TItemData> ItemsSource { get; set; } = new List<TItemData>();
66
71 protected TContext Context { get; } = new TContext();
72
79 protected virtual void Initialize() { }
80
85 protected virtual void UpdateContents(IList<TItemData> itemsSource)
86 {
87 ItemsSource = itemsSource;
88 Refresh();
89 }
90
94 protected virtual void Relayout() => UpdatePosition(currentPosition, false);
95
99 protected virtual void Refresh() => UpdatePosition(currentPosition, true);
100
105 protected virtual void UpdatePosition(float position) => UpdatePosition(position, false);
106
107 void UpdatePosition(float position, bool forceRefresh)
108 {
109 if (!initialized)
110 {
111 Initialize();
112 initialized = true;
113 }
114
115 currentPosition = position;
116
117 var p = position - scrollOffset / cellInterval;
118 var firstIndex = Mathf.CeilToInt(p);
119 var firstPosition = (Mathf.Ceil(p) - p) * cellInterval;
120
121 if (firstPosition + pool.Count * cellInterval < 1f)
122 {
123 ResizePool(firstPosition);
124 }
125
126 UpdateCells(firstPosition, firstIndex, forceRefresh);
127 }
128
129 void ResizePool(float firstPosition)
130 {
131 Debug.Assert(CellPrefab != null);
132 Debug.Assert(cellContainer != null);
133
134 var addCount = Mathf.CeilToInt((1f - firstPosition) / cellInterval) - pool.Count;
135 for (var i = 0; i < addCount; i++)
136 {
137 var cell = Instantiate(CellPrefab, cellContainer).GetComponent<FancyCell<TItemData, TContext>>();
138 if (cell == null)
139 {
140 throw new MissingComponentException(string.Format(
141 "FancyCell<{0}, {1}> component not found in {2}.",
142 typeof(TItemData).FullName, typeof(TContext).FullName, CellPrefab.name));
143 }
144
145 cell.SetContext(Context);
146 cell.Initialize();
147 cell.SetVisible(false);
148 pool.Add(cell);
149 }
150 }
151
152 void UpdateCells(float firstPosition, int firstIndex, bool forceRefresh)
153 {
154 for (var i = 0; i < pool.Count; i++)
155 {
156 var index = firstIndex + i;
157 var position = firstPosition + i * cellInterval;
158 var cell = pool[CircularIndex(index, pool.Count)];
159
160 if (loop)
161 {
162 index = CircularIndex(index, ItemsSource.Count);
163 }
164
165 if (index < 0 || index >= ItemsSource.Count || position > 1f)
166 {
167 cell.SetVisible(false);
168 continue;
169 }
170
171 if (forceRefresh || cell.Index != index || !cell.IsVisible)
172 {
173 cell.Index = index;
174 cell.SetVisible(true);
175 cell.UpdateContent(ItemsSource[index]);
176 }
177
178 cell.UpdatePosition(position);
179 }
180 }
181
182 int CircularIndex(int i, int size) => size < 1 ? 0 : i < 0 ? size - 1 + (i + 1) % size : i % size;
183
184#if UNITY_EDITOR
185 bool cachedLoop;
186 float cachedCellInterval, cachedScrollOffset;
187
188 void LateUpdate()
189 {
190 if (cachedLoop != loop ||
191 cachedCellInterval != cellInterval ||
192 cachedScrollOffset != scrollOffset)
193 {
194 cachedLoop = loop;
195 cachedCellInterval = cellInterval;
196 cachedScrollOffset = scrollOffset;
197
199 }
200 }
201#endif
202 }
203
207 public sealed class NullContext { }
208
215 public abstract class FancyScrollView<TItemData> : FancyScrollView<TItemData, NullContext> { }
216}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
スクロールビューを実装するための抽象基底クラス. 無限スクロールおよびスナップに対応しています. FancyScrollView<TItemData, TContext>....
float cellInterval
セル同士の間隔.
IList< TItemData > ItemsSource
アイテム一覧のデータ.
virtual void UpdatePosition(float position)
スクロール位置を更新します.
virtual void Relayout()
セルのレイアウトを強制的に更新します.
float scrollOffset
スクロール位置の基準.
float currentPosition
現在のスクロール位置.
Transform cellContainer
セルの親要素となる Transform.
virtual void Refresh()
セルのレイアウトと表示内容を強制的に更新します.
TContext Context
TContext のインスタンス. セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
virtual void UpdateContents(IList< TItemData > itemsSource)
渡されたアイテム一覧に基づいて表示内容を更新します.
bool loop
セルを循環して配置させるどうか.
bool initialized
初期化済みかどうか.
virtual void Initialize()
初期化を行います.
abstract GameObject CellPrefab
セルの Prefab.
FancyScrollView<TItemData> のコンテキストクラス.
Credit Erdener Gonenc - @PixelEnvision.