Tanoda
FancyScrollRect.cs
Go to the documentation of this file.
1
3
4using System;
5using System.Collections.Generic;
7
9{
18 [RequireComponent(typeof(Scroller))]
19 public abstract class FancyScrollRect<TItemData, TContext> : FancyScrollView<TItemData, TContext>
20 where TContext : class, IFancyScrollRectContext, new()
21 {
29 [SerializeField] protected float reuseCellMarginCount = 0f;
30
34 [SerializeField] protected float paddingHead = 0f;
35
39 [SerializeField] protected float paddingTail = 0f;
40
44 [SerializeField] protected float spacing = 0f;
45
49 protected abstract float CellSize { get; }
50
57 protected virtual bool Scrollable => MaxScrollPosition > 0f;
58
59 Scroller cachedScroller;
60
67 protected Scroller Scroller => cachedScroller ?? (cachedScroller = GetComponent<Scroller>());
68
69 float ScrollLength => 1f / Mathf.Max(cellInterval, 1e-2f) - 1f;
70
71 float ViewportLength => ScrollLength - reuseCellMarginCount * 2f;
72
73 float PaddingHeadLength => (paddingHead - spacing * 0.5f) / (CellSize + spacing);
74
75 float MaxScrollPosition => ItemsSource.Count
76 - ScrollLength
79
81 protected override void Initialize()
82 {
83 base.Initialize();
84
85 Context.ScrollDirection = Scroller.ScrollDirection;
86 Context.CalculateScrollSize = () =>
87 {
88 var interval = CellSize + spacing;
89 var reuseMargin = interval * reuseCellMarginCount;
90 var scrollSize = Scroller.ViewportSize + interval + reuseMargin * 2f;
91 return (scrollSize, reuseMargin);
92 };
93
95 Scroller.OnValueChanged(OnScrollerValueChanged);
96 }
97
102 void OnScrollerValueChanged(float p)
103 {
104 base.UpdatePosition(Scrollable ? ToFancyScrollViewPosition(p) : 0f);
105
107 {
108 if (p > ItemsSource.Count - 1)
109 {
110 ShrinkScrollbar(p - (ItemsSource.Count - 1));
111 }
112 else if (p < 0f)
113 {
114 ShrinkScrollbar(-p);
115 }
116 }
117 }
118
123 void ShrinkScrollbar(float offset)
124 {
125 var scale = 1f - ToFancyScrollViewPosition(offset) / (ViewportLength - PaddingHeadLength);
126 UpdateScrollbarSize((ViewportLength - PaddingHeadLength) * scale);
127 }
128
130 protected override void Refresh()
131 {
134 base.Refresh();
135 }
136
138 protected override void Relayout()
139 {
142 base.Relayout();
143 }
144
148 protected void RefreshScroller()
149 {
151 Scroller.ScrollSensitivity = ToScrollerPosition(ViewportLength - PaddingHeadLength);
153
155 {
156 Scroller.Scrollbar.gameObject.SetActive(Scrollable);
157 UpdateScrollbarSize(ViewportLength);
158 }
159 }
160
162 protected override void UpdateContents(IList<TItemData> items)
163 {
164 Debug.Assert(Context.CalculateScrollSize != null);
165
167 base.UpdateContents(items);
168
169 Scroller.SetTotalCount(items.Count);
171 }
172
177 protected new void UpdatePosition(float position)
178 {
179 Scroller.Position = ToScrollerPosition(position, 0.5f);
180 }
181
187 protected virtual void JumpTo(int itemIndex, float alignment = 0.5f)
188 {
189 Scroller.Position = ToScrollerPosition(itemIndex, alignment);
190 }
191
199 protected virtual void ScrollTo(int index, float duration, float alignment = 0.5f, Action onComplete = null)
200 {
201 Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, onComplete);
202 }
203
212 protected virtual void ScrollTo(int index, float duration, Ease easing, float alignment = 0.5f, Action onComplete = null)
213 {
214 Scroller.ScrollTo(ToScrollerPosition(index, alignment), duration, easing, onComplete);
215 }
216
221 protected void UpdateScrollbarSize(float viewportLength)
222 {
223 var contentLength = Mathf.Max(ItemsSource.Count + (paddingHead + paddingTail - spacing) / (CellSize + spacing), 1);
224 Scroller.Scrollbar.size = Scrollable ? Mathf.Clamp01(viewportLength / contentLength) : 1f;
225 }
226
232 protected float ToFancyScrollViewPosition(float position)
233 {
234 return position / Mathf.Max(ItemsSource.Count - 1, 1) * MaxScrollPosition - PaddingHeadLength;
235 }
236
242 protected float ToScrollerPosition(float position)
243 {
244 return (position + PaddingHeadLength) / MaxScrollPosition * Mathf.Max(ItemsSource.Count - 1, 1);
245 }
246
253 protected float ToScrollerPosition(float position, float alignment = 0.5f)
254 {
255 var offset = alignment * (ScrollLength - (1f + reuseCellMarginCount * 2f))
256 + (1f - alignment - 0.5f) * spacing / (CellSize + spacing);
257 return ToScrollerPosition(Mathf.Clamp(position - offset, 0f, MaxScrollPosition));
258 }
259
266 {
267 var totalSize = Scroller.ViewportSize + (CellSize + spacing) * (1f + reuseCellMarginCount * 2f);
268 cellInterval = (CellSize + spacing) / totalSize;
270 }
271
272 protected virtual void OnValidate()
273 {
275
276 if (loop)
277 {
278 loop = false;
279 Debug.LogError("Loop is currently not supported in FancyScrollRect.");
280 }
281
283 {
284 Scroller.SnapEnabled = false;
285 Debug.LogError("Snap is currently not supported in FancyScrollRect.");
286 }
287
288 if (Scroller.MovementType == MovementType.Unrestricted)
289 {
291 Debug.LogError("MovementType.Unrestricted is currently not supported in FancyScrollRect.");
292 }
293 }
294 }
295
302 public abstract class FancyScrollRect<TItemData> : FancyScrollRect<TItemData, FancyScrollRectContext> { }
303}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
ScrollRect スタイルのスクロールビューを実装するための抽象基底クラス. 無限スクロールおよびスナップには対応していません. FancyScrollView<TItemData,...
void UpdateScrollbarSize(float viewportLength)
ビューポートとコンテンツの長さに基づいてスクロールバーのサイズを更新します.
float paddingTail
コンテンツ末尾の余白.
float reuseCellMarginCount
スクロール中にセルが再利用されるまでの余白のセル数.
float spacing
スクロール軸方向のセル同士の余白.
override void Initialize()
初期化を行います.
abstract float CellSize
セルのサイズ.
float ToFancyScrollViewPosition(float position)
Scroller が扱うスクロール位置を FancyScrollRect<TItemData, TContext> が扱うスクロール位置に変換します.
void RefreshScroller()
Scroller の各種状態を更新します.
override void Refresh()
セルのレイアウトと表示内容を強制的に更新します.
void AdjustCellIntervalAndScrollOffset()
指定された設定を実現するための FancyScrollView<TItemData,TContext>.cellInterval と FancyScrollView<TItemData,...
float paddingHead
コンテンツ先頭の余白.
new void UpdatePosition(float position)
スクロール位置を更新します.
virtual void ScrollTo(int index, float duration, float alignment=0.5f, Action onComplete=null)
指定したアイテムの位置まで移動します.
override void UpdateContents(IList< TItemData > items)
渡されたアイテム一覧に基づいて表示内容を更新します.
float ToScrollerPosition(float position)
FancyScrollRect<TItemData, TContext> が扱うスクロール位置を Scroller が扱うスクロール位置に変換します.
virtual bool Scrollable
スクロール可能かどうか.
virtual void JumpTo(int itemIndex, float alignment=0.5f)
指定したアイテムの位置までジャンプします.
float ToScrollerPosition(float position, float alignment=0.5f)
FancyScrollRect<TItemData, TContext> が扱うスクロール位置を Scroller が扱うスクロール位置に変換します.
virtual void ScrollTo(int index, float duration, Ease easing, float alignment=0.5f, Action onComplete=null)
指定したアイテムの位置まで移動します.
override void Relayout()
セルのレイアウトを強制的に更新します.
IList< TItemData > ItemsSource
アイテム一覧のデータ.
TContext Context
TContext のインスタンス. セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
bool loop
セルを循環して配置させるどうか.
スクロール位置の制御を行うコンポーネント.
Definition: Scroller.cs:14
void OnValueChanged(Action< float > callback)
スクロール位置が変化したときのコールバックを設定します.
float Position
現在のスクロール位置.
Definition: Scroller.cs:128
ScrollDirection ScrollDirection
スクロール方向.
Definition: Scroller.cs:29
void SetTotalCount(int totalCount)
アイテムの総数を設定します.
float ScrollSensitivity
ViewportSize の端から端まで Drag したときのスクロール位置の変化量.
Definition: Scroller.cs:59
MovementType MovementType
コンテンツがスクロール範囲を越えて移動するときに使用する挙動.
Definition: Scroller.cs:37
bool Draggable
Drag 入力を受付けるかどうか.
Definition: Scroller.cs:111
bool SnapEnabled
true ならスナップし, falseならスナップしません.
Definition: Scroller.cs:100
Scrollbar Scrollbar
スクロールバーのオブジェクト.
Definition: Scroller.cs:121
float ViewportSize
ビューポートのサイズ.
Definition: Scroller.cs:20
void ScrollTo(float position, float duration, Action onComplete=null)
指定した位置まで移動します.
Credit Erdener Gonenc - @PixelEnvision.