自己紹介
サマーインターンでウハウハな大学生
List View
UI ToolkitにはList ViewというVisual Elementが存在します。
基本的に、なにかの一覧を並べるものなのですが、少しだけ工夫しないとその一覧から選択するなど複雑なUIを正しく構築するのが難しいです。今回はそのTipsを紹介します。
List Viewについて
List Viewの詳細は下記の記事が良くまとまっているので、ご覧ください。
Toggle in List View
以下のようなUIがあったとしましょう。
ストレートに考えると、イベント発火させたいときは以下のようなコードを書くと思います。
// なにかしらのList Viewの初期化処理
listView.makeItem += () => new Toggle();
listView.bindItem += (element, index) =>
{
var toggle = (Toggle)element;
toggle.text = strings[index];
toggle.style.fontSize = slider.value;
toggle.RegisterValueChangedCallback(e =>
{
Debug.Log(index);
});
};
しかし、bindItemの発火タイミングを考えると、これではバグが生まれます。
bindItemの発火タイミング
List Viewは画面に表示される要素だけをmakeItem
で生成し、そのVisual Elementを使いまわす形で、画面に表示していきます。これによって、パフォーマンスの最適化がされているのです。
bindItem
はスクロールしていったときに要素が切り替わるときに呼び出されます。
そのため、要素が切り替わるタイミングでRegisterValueChangeCallback
(Register
なんちゃら全部)を動かしてしまうと、2重で登録されてしまうのです。
これによって、正しく発火しなかったり、使いまわす過程で表示が崩れたりします。
indexがない問題
しかし、makeItem
ではindexが取得できません。これはVisualElement.userData
というプロパティを使うことで解決できます。このプロパティはobject
型です。つまり、なんでも入れられます。
makeItem
でイベントを登録し、bindItem
でuserData
にindexを入れることで更新するという処理を行うことが良いでしょう。
// なにかしらのList Viewの初期化処理
listView.makeItem += () =>
{
var toggle = new Toggle();
toggle.RegisterValueChangedCallback(e =>
{
var index = (int)toggle.userData;
Debug.Log(index);
});
return toggle;
};
listView.bindItem += (element, index) =>
{
var toggle = (Toggle)element;
toggle.text = strings[index];
toggle.style.fontSize = slider.value;
toggle.userData = index;
};
まだ終わらない、バグ
このコードを実行すると正しくイベントは発火されるはずです。
しかし、スクロールしていくと選択してもいないToggle
のvalue
がtrue
になる問題が発生します。
これはVisualElement
を使いまわすからですね。
純粋に、状態を保存する配列やListを生成してあげるのが良いでしょう。
// なにかしらのList Viewの初期化処理
// なにかしらの状態を保存する配列などの初期化処理
listView.makeItem += () =>
{
var toggle = new Toggle();
toggle.RegisterValueChangedCallback(e =>
{
var index = (int)toggle.userData;
_listState[index] = e.newValue;
Debug.Log(index);
});
return toggle;
};
listView.bindItem += (element, index) =>
{
var toggle = (Toggle)element;
toggle.text = strings[index];
toggle.style.fontSize = slider.value;
toggle.SetValueWithoutNotify(_listState[index]);
toggle.userData = index;
};
おわり
癖がありますが、まぁ言われてみたらそうだよなって感じの内容でした。
ではでは、またお会いしましょう。いずれ、どこかで。