Skip to content
This repository was archived by the owner on Mar 20, 2023. It is now read-only.

Commit 0a5444b

Browse files
Updated Fancy ScrollView and added Example 8
1 parent c41ad9a commit 0a5444b

17 files changed

+760
-90
lines changed

Examples

Submodule Examples updated from a5914a9 to 4878f8d

Scripts/Layout/FancyScrollView/Core/FancyScrollView.cs

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,109 @@
55

66
namespace UnityEngine.UI.Extensions
77
{
8+
/// <summary>
9+
/// スクロールビューを実装するための抽象基底クラス.
10+
/// 無限スクロールおよびスナップに対応しています.
11+
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> が不要な場合は
12+
/// 代わりに <see cref="FancyScrollView{TItemData}"/> を使用します.
13+
/// </summary>
14+
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
15+
/// <typeparam name="TContext"><see cref="Context"/> の型.</typeparam>
816
public abstract class FancyScrollView<TItemData, TContext> : MonoBehaviour where TContext : class, new()
917
{
18+
/// <summary>
19+
/// セル同士の間隔.
20+
/// </summary>
1021
[SerializeField, Range(1e-2f, 1f)] protected float cellInterval = 0.2f;
22+
23+
/// <summary>
24+
/// スクロール位置の基準.
25+
/// </summary>
26+
/// <remarks>
27+
/// たとえば、 <c>0.5</c> を指定してスクロール位置が <c>0</c> の場合, 中央に最初のセルが配置されます.
28+
/// </remarks>
1129
[SerializeField, Range(0f, 1f)] protected float scrollOffset = 0.5f;
30+
31+
/// <summary>
32+
/// セルを循環して配置させるどうか.
33+
/// </summary>
34+
/// <remarks>
35+
/// <c>true</c> にすると最後のセルの後に最初のセル, 最初のセルの前に最後のセルが並ぶようになります.
36+
/// 無限スクロールを実装する場合は <c>true</c> を指定します.
37+
/// </remarks>
1238
[SerializeField] protected bool loop = false;
39+
40+
/// <summary>
41+
/// セルの親要素となる <c>Transform</c>.
42+
/// </summary>
1343
[SerializeField] protected Transform cellContainer = default;
1444

1545
readonly IList<FancyScrollViewCell<TItemData, TContext>> pool =
1646
new List<FancyScrollViewCell<TItemData, TContext>>();
1747

48+
/// <summary>
49+
/// 初期化済みかどうか.
50+
/// </summary>
51+
protected bool initialized;
52+
53+
/// <summary>
54+
/// 現在のスクロール位置.
55+
/// </summary>
1856
protected float currentPosition;
1957

58+
/// <summary>
59+
/// セルの Prefab.
60+
/// </summary>
2061
protected abstract GameObject CellPrefab { get; }
62+
63+
/// <summary>
64+
/// アイテム一覧のデータ.
65+
/// </summary>
2166
protected IList<TItemData> ItemsSource { get; set; } = new List<TItemData>();
67+
68+
/// <summary>
69+
/// <typeparamref name="TContext"/> のインスタンス.
70+
/// セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
71+
/// </summary>
2272
protected TContext Context { get; } = new TContext();
2373

2474
/// <summary>
25-
/// Updates the contents.
75+
/// 初期化を行います.
76+
/// </summary>
77+
/// <remarks>
78+
/// 最初にセルが生成される直前に呼び出されます.
79+
/// </remarks>
80+
protected virtual void Initialize() { }
81+
82+
/// <summary>
83+
/// 渡されたアイテム一覧に基づいて表示内容を更新します.
2684
/// </summary>
27-
/// <param name="itemsSource">Items source.</param>
85+
/// <param name="itemsSource">アイテム一覧.</param>
2886
protected virtual void UpdateContents(IList<TItemData> itemsSource)
2987
{
3088
ItemsSource = itemsSource;
3189
Refresh();
3290
}
3391

3492
/// <summary>
35-
/// Refreshes the cells.
93+
/// セルの表示内容を更新します.
3694
/// </summary>
3795
protected virtual void Refresh() => UpdatePosition(currentPosition, true);
3896

3997
/// <summary>
40-
/// Updates the scroll position.
98+
/// スクロール位置を更新します.
4199
/// </summary>
42-
/// <param name="position">Position.</param>
100+
/// <param name="position">スクロール位置.</param>
43101
protected virtual void UpdatePosition(float position) => UpdatePosition(position, false);
44102

45-
protected void UpdatePosition(float position, bool forceRefresh)
103+
void UpdatePosition(float position, bool forceRefresh)
46104
{
105+
if (!initialized)
106+
{
107+
Initialize();
108+
initialized = true;
109+
}
110+
47111
currentPosition = position;
48112

49113
var p = position - scrollOffset / cellInterval;
@@ -66,7 +130,8 @@ void ResizePool(float firstPosition)
66130
var addCount = Mathf.CeilToInt((1f - firstPosition) / cellInterval) - pool.Count;
67131
for (var i = 0; i < addCount; i++)
68132
{
69-
var cell = Instantiate(CellPrefab, cellContainer).GetComponent<FancyScrollViewCell<TItemData, TContext>>();
133+
var cell = Instantiate(CellPrefab, cellContainer)
134+
.GetComponent<FancyScrollViewCell<TItemData, TContext>>();
70135
if (cell == null)
71136
{
72137
throw new MissingComponentException(
@@ -118,7 +183,9 @@ void UpdateCells(float firstPosition, int firstIndex, bool forceRefresh)
118183

119184
void LateUpdate()
120185
{
121-
if (cachedLoop != loop || cachedCellInterval != cellInterval || cachedScrollOffset != scrollOffset)
186+
if (cachedLoop != loop ||
187+
cachedCellInterval != cellInterval ||
188+
cachedScrollOffset != scrollOffset)
122189
{
123190
cachedLoop = loop;
124191
cachedCellInterval = cellInterval;
@@ -130,7 +197,16 @@ void LateUpdate()
130197
#endif
131198
}
132199

200+
/// <summary>
201+
/// <see cref="FancyScrollView{TItemData}"/> のコンテキストクラス.
202+
/// </summary>
133203
public sealed class FancyScrollViewNullContext { }
134204

205+
/// <summary>
206+
/// スクロールビューを実装するための抽象基底クラス.
207+
/// 無限スクロールおよびスナップに対応しています.
208+
/// </summary>
209+
/// <typeparam name="TItemData"></typeparam>
210+
/// <seealso cref="FancyScrollView{TItemData, TContext}"/>
135211
public abstract class FancyScrollView<TItemData> : FancyScrollView<TItemData, FancyScrollViewNullContext> { }
136212
}

Scripts/Layout/FancyScrollView/Core/FancyScrollViewCell.cs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,64 @@
33

44
namespace UnityEngine.UI.Extensions
55
{
6+
/// <summary>
7+
/// <see cref="FancyScrollView{TItemData, TContext}"/> のセルを実装するための抽象基底クラス.
8+
/// <see cref="FancyScrollViewCell{TItemData, TContext}.Context"/> が不要な場合は
9+
/// 代わりに <see cref="FancyScrollViewCell{TItemData}"/> を使用します.
10+
/// </summary>
11+
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
12+
/// <typeparam name="TContext"><see cref="Context"/> の型.</typeparam>
613
public abstract class FancyScrollViewCell<TItemData, TContext> : MonoBehaviour where TContext : class, new()
714
{
815
/// <summary>
9-
/// Gets or sets the index of the data.
16+
/// このセルで表示しているデータのインデックス.
1017
/// </summary>
11-
/// <value>The index of the data.</value>
1218
public int Index { get; set; } = -1;
1319

1420
/// <summary>
15-
/// Gets a value indicating whether this <see cref="T:FancyScrollView.FancyScrollViewCell`2"/> is visible.
21+
/// このセルの可視状態.
1622
/// </summary>
17-
/// <value><c>true</c> if is visible; otherwise, <c>false</c>.</value>
1823
public virtual bool IsVisible => gameObject.activeSelf;
1924

2025
/// <summary>
21-
/// Gets the context.
26+
/// <see cref="FancyScrollView{TItemData, TContext}.Context"/> の参照.
27+
/// セルとスクロールビュー間で同じインスタンスが共有されます. 情報の受け渡しや状態の保持に使用します.
2228
/// </summary>
23-
/// <value>The context.</value>
2429
protected TContext Context { get; private set; }
2530

2631
/// <summary>
27-
/// Setup the context.
32+
/// <see cref="Context"/> のセットアップを行います.
2833
/// </summary>
29-
/// <param name="context">Context.</param>
34+
/// <param name="context">コンテキスト.</param>
3035
public virtual void SetupContext(TContext context) => Context = context;
3136

3237
/// <summary>
33-
/// Sets the visible.
38+
/// このセルの可視状態を設定します.
3439
/// </summary>
35-
/// <param name="visible">If set to <c>true</c> visible.</param>
40+
/// <param name="visible">可視状態なら <c>true</c>, 非可視状態なら <c>false</c>.</param>
3641
public virtual void SetVisible(bool visible) => gameObject.SetActive(visible);
3742

3843
/// <summary>
39-
/// Updates the content.
44+
/// アイテムデータに基づいてこのセルの表示内容を更新します.
4045
/// </summary>
41-
/// <param name="itemData">Item data.</param>
46+
/// <param name="itemData">アイテムデータ.</param>
4247
public abstract void UpdateContent(TItemData itemData);
4348

4449
/// <summary>
45-
/// Updates the position.
50+
/// <c>0.0f</c> ~ <c>1.0f</c> の値に基づいてこのセルのスクロール位置を更新します.
4651
/// </summary>
47-
/// <param name="position">Position.</param>
52+
/// <param name="position">ビューポート範囲の正規化されたスクロール位置.</param>
4853
public abstract void UpdatePosition(float position);
4954
}
5055

56+
/// <summary>
57+
/// <see cref="FancyScrollView{TItemData}"/> のセルを実装するための抽象基底クラス.
58+
/// </summary>
59+
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
60+
/// <seealso cref="FancyScrollViewCell{TItemData, TContext}"/>
5161
public abstract class FancyScrollViewCell<TItemData> : FancyScrollViewCell<TItemData, FancyScrollViewNullContext>
5262
{
63+
/// <inheritdoc/>
5364
public sealed override void SetupContext(FancyScrollViewNullContext context) => base.SetupContext(context);
5465
}
5566
}

Scripts/Layout/FancyScrollView/GridView.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/// Credit setchi (https://github.com/setchi)
2+
/// Sourced from - https://github.com/setchi/FancyScrollView
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using UnityEngine.UI.Extensions.EasingCore;
8+
9+
namespace UnityEngine.UI.Extensions
10+
{
11+
/// <summary>
12+
/// グリッドレイアウトのスクロールビューを実装するための抽象基底クラス.
13+
/// 無限スクロールおよびスナップには対応していません.
14+
/// </summary>
15+
/// <typeparam name="TItemData">アイテムのデータ型.</typeparam>
16+
/// <typeparam name="TContext"><see cref="FancyScrollView{TItemData, TContext}.Context"/> の型.</typeparam>
17+
public abstract class FancyGridView<TItemData, TContext> : FancyScrollRect<TItemData[], TContext>
18+
where TContext : class, IFancyScrollRectContext, IFancyGridViewContext, new()
19+
{
20+
/// <summary>
21+
/// カラム同士の余白.
22+
/// </summary>
23+
[SerializeField] protected float columnSpacing = 0f;
24+
25+
GameObject cachedRowPrefab;
26+
27+
/// <summary>
28+
/// 行の Prefab.
29+
/// </summary>
30+
/// <remarks>
31+
/// <see cref="FancyGridView{TItemData, TContext}"/> では,
32+
/// <see cref="FancyScrollView{TItemData, TContext}.CellPrefab"/> を行オブジェクトとして使用します.
33+
/// </remarks>
34+
protected sealed override GameObject CellPrefab => cachedRowPrefab ?? (cachedRowPrefab = SetupRowTemplate());
35+
36+
/// <summary>
37+
/// 一行あたりの要素数.
38+
/// </summary>
39+
protected abstract int ColumnCount { get; }
40+
41+
/// <summary>
42+
/// セルのテンプレート.
43+
/// </summary>
44+
protected abstract FancyScrollViewCell<TItemData, TContext> CellTemplate { get; }
45+
46+
/// <summary>
47+
/// 行オブジェクトのテンプレート.
48+
/// </summary>
49+
protected abstract FancyGridViewRow<TItemData, TContext> RowTemplate { get; }
50+
51+
/// <summary>
52+
/// アイテムの総数.
53+
/// </summary>
54+
public int DataCount { get; private set; }
55+
56+
/// <inheritdoc/>
57+
protected override void Initialize()
58+
{
59+
base.Initialize();
60+
61+
Debug.Assert(RowTemplate != null);
62+
Debug.Assert(CellTemplate != null);
63+
Debug.Assert(ColumnCount > 0);
64+
65+
Context.CellTemplate = CellTemplate.gameObject;
66+
Context.ScrollDirection = Scroller.ScrollDirection;
67+
Context.GetColumnCount = () => ColumnCount;
68+
Context.GetColumnSpacing = () => columnSpacing;
69+
}
70+
71+
/// <summary>
72+
/// 行オブジェクトのセットアップを行います.
73+
/// </summary>
74+
/// <returns>行を構成する <c>GameObject</c>.</returns>
75+
protected virtual GameObject SetupRowTemplate()
76+
{
77+
var cell = CellTemplate.GetComponent<RectTransform>();
78+
var row = RowTemplate.GetComponent<RectTransform>();
79+
80+
row.sizeDelta = Scroller.ScrollDirection == ScrollDirection.Horizontal
81+
? new Vector2(cell.rect.width, row.sizeDelta.y)
82+
: new Vector2(row.sizeDelta.x, cell.rect.height);
83+
84+
return row.gameObject;
85+
}
86+
87+
/// <summary>
88+
/// 渡されたアイテム一覧に基づいて表示内容を更新します.
89+
/// </summary>
90+
/// <param name="items">アイテム一覧.</param>
91+
public virtual void UpdateContents(IList<TItemData> items)
92+
{
93+
DataCount = items.Count;
94+
95+
var rows = items
96+
.Select((item, index) => (item, index))
97+
.GroupBy(
98+
x => x.index / ColumnCount,
99+
x => x.item)
100+
.Select(group => group.ToArray())
101+
.ToArray();
102+
103+
UpdateContents(rows);
104+
}
105+
106+
/// <summary>
107+
/// 指定したアイテムの位置まで移動します.
108+
/// </summary>
109+
/// <param name="itemIndex">アイテムのインデックス.</param>
110+
/// <param name="duration">移動にかける秒数.</param>
111+
/// <param name="alignment"><see cref="Alignment"/>.</param>
112+
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
113+
public override void ScrollTo(int itemIndex, float duration, Alignment alignment = Alignment.Center, Action onComplete = null)
114+
{
115+
var rowIndex = itemIndex / Context.GetColumnCount();
116+
base.ScrollTo(rowIndex, duration, alignment, onComplete);
117+
}
118+
119+
/// <summary>
120+
/// 指定したアイテムの位置まで移動します.
121+
/// </summary>
122+
/// <param name="itemIndex">アイテムのインデックス.</param>
123+
/// <param name="duration">移動にかける秒数.</param>
124+
/// <param name="easing">移動に使用するイージング.</param>
125+
/// <param name="alignment"><see cref="Alignment"/>.</param>
126+
/// <param name="onComplete">移動が完了した際に呼び出されるコールバック.</param>
127+
public override void ScrollTo(int itemIndex, float duration, Ease easing, Alignment alignment = Alignment.Center, Action onComplete = null)
128+
{
129+
var rowIndex = itemIndex / Context.GetColumnCount();
130+
base.ScrollTo(rowIndex, duration, easing, alignment, onComplete);
131+
}
132+
133+
/// <summary>
134+
/// 指定したアイテムの位置までジャンプします.
135+
/// </summary>
136+
/// <param name="itemIndex">アイテムのインデックス.</param>
137+
/// <param name="alignment"><see cref="Alignment"/>.</param>
138+
public virtual void JumpTo(int itemIndex, Alignment alignment = Alignment.Center)
139+
{
140+
var rowIndex = itemIndex / Context.GetColumnCount();
141+
UpdatePosition(rowIndex, alignment);
142+
}
143+
}
144+
}

0 commit comments

Comments
 (0)