大量のリストを高速に表示する TableScrollView - 使い方

こちらの記事の続きになります。ダウンロードもこちら。

導入

ダウンロードした unitypackage をインポート

InputSystem の場合、Padd(PadInput) というライブラリを使用します。
(レガシー Input であれば Padd は必要ありません)

UI/ScrollView を作成する

適当な大きさにしてください。ここでは width: 400、height: 800 の縦スクロールビューとします。

ScrollRect の設定

Scrollbar Horizontal は消してください

縦スクロールビューにしたいので、Horizontal のチェックは外し、Horizontal Scrollbar の登録を削除します。
(ヒエラルキーから Scrollbar Horizontal も消してしまいましょう)

ノードを作成する

ここで作成したノードはデモサンプルに入っています。

NodeSimple が親オブジェクト

1マスに表示するための情報(ノード)を作成します。
今回は絵、番号、テキスト、フォーカス下地の4種類にしました。

自分で作る時に気をつけることとして、一番上(親オブジェクト)の Pivot や Anchor は 0.5、0.5 にしておいてください。
(これを守らないとライブラリから警告が出ます。また、表示もズレてしまいます)

他もOKにするのは面倒だった

TableScrollViewer を ScrollView にアタッチする

ScrollRect のあるオブジェクトに、TableScrollViewer をつけます。
また、SourceNode に先ほどの NodeSimple を設定しておいてください。

SourceNode はソースコードから設定することもできます。

ノードを表示するためのコードを作成する

下準備は出来たので、TableScrollViewer を表示するためのコード(Sample.cs)を記述します。
コードは ScrollView か、その一つ上の Canvas にアタッチしてください。
テストコードはこれだけで構いません。

using System.Collections.Generic;
using UnityEngine;

public class Sample : MonoBehaviour
{
    List<object>         viewerList = new List<object>();

    void Awake()
    {
        TableScrollViewer viewer = this.gameObject.GetComponentInChildren<TableScrollViewer>();

        for (int i = 0; i < 16; i++)
        {
           viewerList.Add(i * 2);
        }

        viewer?.Initialize();
        viewer?.SetTable(viewerList.ToArray());
    }
}

サンプルでは全16 個の viewerList が各ノードに分配されます。
この場合は 0, 2, 4, 6, 8 ... 30 の int 値だけですが、object なので独自のテーブルや ScriptableObject を渡しても構いません。(というより、ほとんどそういう使い方がメインだと思います)

NodeSimple(Node.cs) は、ViewerList と itemIndex を元にノードの表示を決定しています。
Node.cs の次のコードです。

    /// <summary>
    /// 行の表示更新通知があった場合、ここで表示を更新する
    /// </summary>
    public override void onEffectChange(int itemIndex)
    {
        int no = (int)table[itemIndex];

        No.SetText("Line: " + (no+1).ToString("00"));
        Desc.SetText(Descriptions[no % Descriptions.Length]);
        Icon.sprite = IconSprites[no % IconSprites.Length];
    }

table というのは Sample.cs の viewerList の名前を変えたものです。
itemIndex が、「上から何番目か」を示す数字です。
viewerList は 0, 2, 4, 6, 8 ... という数字が入っていました。それを参照している Line もまた、1, 3, 5, 7 ... と表示されています。(no+1 なので 1 からになっています)

Sample.cs の要素数を 16 から増やしたり、Node.cs で表示物を変更するなどテストしてみてください。
10000 個にしても特に問題なく動くのがわかるかと思います。

文章やアイコンは、サンプルなので Node.cs に直接設定しています。
実際に運用する場合は viewerList に情報を含めた方がいいかと思います。

イベント

Sample.cs を次のように拡張しておいてください。
3つのイベントについて説明します。

using System.Collections.Generic;
using UnityEngine;

public class Sample : MonoBehaviour
{
    List<object>         viewerList = new List<object>();

    void Awake()
    {
        TableScrollViewer viewer = this.gameObject.GetComponentInChildren<TableScrollViewer>();

        for (int i = 0; i < 16; i++)
        {
           viewerList.Add(i*2);
        }

        viewer?.Initialize();
        viewer?.SetTable(viewerList.ToArray());
        viewer?.OnSelect.AddListener(OnSelect);
        viewer?.OnKeyDown.AddListener(OnKeyDown);
        viewer?.OnCursorMove.AddListener(onCursorMove);
    }

    public void onCursorMove(object[] table, int itemIndex, int subIndex, bool userInput)
    {
        int row = (int)table[itemIndex];
        Debug.Log($"move: {row+1}");
    }

    public void OnSelect(object[] table, int itemIndex, int subIndex, bool isCancel)
    {
        int row = (int)table[itemIndex];
        Debug.Log($"selected : {row+1}");
    }

    public void OnKeyDown(TableScrollViewer.KeyDownArgs args)
    {
        if (Input.GetKeyDown(KeyCode.Return) == true)
        {
            args.Flag = TableScrollViewer.eKeyMoveFlag.Select;
        }
        else
        if (Input.GetKeyDown(KeyCode.UpArrow) == true)
        {
            args.Flag = TableScrollViewer.eKeyMoveFlag.Up;
        }
        else
        if (Input.GetKeyDown(KeyCode.DownArrow) == true)
        {
            args.Flag = TableScrollViewer.eKeyMoveFlag.Down;
        }
    }
}

レガシー Input を使っている場合、次のように変更してください。
Padd -> Input
GetKeyDelay -> GetKeyDown
ePad -> KeyCode

OnSelect(): 選択された時に通知する

table itemIndex を組み合わせることで、選択された項目(行)を知ることができます。
また、後述するキャンセル操作を行った場合は isCancel == true を返します。

OnKeyDown(): キーボード入力を可能にする

args.Flag に入力値を指示しておくと、TableScrollViewer はその指示に従って動作します。

このイベントによって、アプリ側で自由にキー操作を制御することができます。
例えば Shift(L1) キーを押しながら ↑ を押すとページスクロール…なんてことも可能でしょう。

eKeyMoveFlag に可能な操作が全て記載されています。
上下左右に加えて決定、キャンセルがあります。
キャンセルを指示した場合、OnSelect()isCancel は true を返します。

OnCursorMove(): カーソルが移動された時の通知

カーソル音や、選択した項目によって説明メッセージを変える時などに使えます。
userInput は false が「プログラムで強制的にカーソル位置を変えた場合」、true の時は「ユーザーが入力して変わった場合」を示します。

とても地味なパラメータですが、例えばカーソル初期位置を指定する場合はカーソル音を鳴らしたくない…など、かゆい所に手が届くパラメータです。

ノードイベント

Content のノードには TableNodeElement を継承したカスタムクラスをアタッチする必要があります。
サンプルでは Node.cs がそれにあたります。
このクラスは3つのイベントがあり、必要なイベント処理を実装します。
(必須なのは onEffectChange() のみです)

onEffectChange(): ノード表示内容変更通知

高速表示するためにノードを使いまわす関係上、要求に応じて表示内容を変える必要があります。
「ノードを表示するためのコードを作成する」でも説明した通り、table と引数の itemIndex によって表示するための情報を取得し、描画アイテムに伝えます。

    /// <summary>
    /// 行の表示更新通知があった場合、ここで表示を更新する
    /// </summary>
    public override void onEffectChange(int itemIndex)
    {
        int no = (int)table[itemIndex];

        No.SetText("Line: " + (no+1).ToString("00"));
        Desc.SetText(Descriptions[no % Descriptions.Length]);
        Icon.sprite = IconSprites[no % IconSprites.Length];
    }

onEffectFocus(): ノードの選択・非選択変化時に呼ばれる

focus true(選択)、false(非選択)時にコールされるイベントです。
コルーチンなどでアニメーション処理を行いたい場合、isAnimationfalse であれば即書き換え、true であればコルーチンアニメーションを仕込むようにしてください。

isAnimation は「初期状態の選択にはアニメーションいらない、ユーザーが選択した時だけアニメーションする」などのこだわりがある人向けなので、それを気にしなかったり、サンプルのようにそもそもアニメーションさせない場合は無視して構いません。

onEffectClick(): ノードがクリック(決定)された時に呼ばれる

このイベントは多くの場合、何も書かなくても済んでしまうイベントです。(サンプルでも記述していません)
もし選択された場合にちょっとしたアニメーションをさせたい場合など、このイベントを使ってください。

細かい引数の解説については、次の記事に

細切れになってしまって申し訳ありませんが、以下をご覧ください。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA