【Unity】【エディタ拡張】ReorderableListの使い方

ReorderableListの使い方です。

Unity2018.3.1

ReorderableList?

ReordearableListは並べ替え可能なリストをエディタで表示するためにUnityが用意しているクラスです。
こんな感じの見た目が簡単に作れます。

f:id:halya_11:20190203214603p:plain

使い方(EditorGUILayout環境で使う)

使い方は簡単です。

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;

public class Example : MonoBehaviour
{
    [SerializeField]
    private List<int> _exampleList;
}

[CustomEditor(typeof(Example))]
public class ExampleEditor : Editor
{
    private ReorderableList _reorderableList;
    
    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        var listProp = serializedObject.FindProperty("_exampleList");

        // ReorderableListを作る
        if (_reorderableList == null) {
            _reorderableList = new ReorderableList(serializedObject, listProp);
        }
        
        // 描画
        _reorderableList.DoLayoutList();

        serializedObject.ApplyModifiedProperties();
    }
}

ReorderableListのコンストラクタにserializedObjectとリストのserializedPropertyを渡すだけです。
あとはDoLayoutList()で描画されます。

結果は次のようになります。

f:id:halya_11:20190203214603p:plain

いろんな設定

ReorderableListのプロパティやコールバックにアクセスするといろんな設定ができます。
タイトル部分や要素部分をカスタムできます。

[CustomEditor(typeof(Example))]
public class ExampleEditor : Editor
{
    private ReorderableList _reorderableList;
    
    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        var listProp = serializedObject.FindProperty("_exampleList");
        
        if (_reorderableList == null) {
            _reorderableList = new ReorderableList(serializedObject, listProp);

            // 並び替え可能か
            _reorderableList.draggable = false;

            // タイトル描画時のコールバック
            // 上書きしてEditorGUIを使えばタイトル部分を自由にレイアウトできる
            _reorderableList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Title");

            // プロパティの高さを指定
            _reorderableList.elementHeightCallback = index => 30;

            // 要素の描画時のコールバック
            // 上書きしてEditorGUIを使えば自由にレイアウトできる
            _reorderableList.drawElementCallback = (rect, index, isActive, isFocused) => 
            {
                var elementProperty = listProp.GetArrayElementAtIndex(index);
                rect.height = EditorGUIUtility.singleLineHeight;
                EditorGUI.PropertyField(rect, elementProperty, new GUIContent("要素" + index));
            };
        
            // +ボタンが押された時のコールバック
            _reorderableList.onAddCallback = list => Debug.Log("+ clicked.");

            // -ボタンが押された時のコールバック
            _reorderableList.onRemoveCallback = list => Debug.Log("- clicked : " + list.index + ".");
        }
        
        _reorderableList.DoLayoutList();

        serializedObject.ApplyModifiedProperties();
    }
}

主要な項目だけ設定してみました。
結果は次のようになります。

f:id:halya_11:20190203220012p:plain

EditorGUI環境で使う

ここまでの例はEditorGUILayout環境でReorderableListを使ってきました。
EditorGUI環境でこれを使うためには、描画時にDoLayoutList()の代わりにDoList()メソッドを使います。

たとえばPropertyDrawerに使うとこんな感じです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;

public class Example : MonoBehaviour
{
    [System.Serializable]
    public class Example2
    {
        [SerializeField]
        private List<int> _exampleList;
    }

    [SerializeField]
    private Example2 _example2;
}

[CustomPropertyDrawer(typeof(Example.Example2))]
public class PostalCodeDrawer : PropertyDrawer
{
    private class PropertyData
    {
        public SerializedProperty exampleListProperty;
        public ReorderableList reorderableList;
    }
    
    private Dictionary<string, PropertyData> _propertyDataPerPropertyPath = new Dictionary<string, PropertyData>();
    private PropertyData _property;
    private float LineHeight { get { return EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; } }

    private void Init(SerializedProperty property)
    {
        if (_propertyDataPerPropertyPath.TryGetValue(property.propertyPath, out _property)){
                return;
        }

        _property = new PropertyData();
        _property.exampleListProperty = property.FindPropertyRelative("_exampleList");

        // ReorderableListを初期化
        var reorderableList = new ReorderableList(property.serializedObject, _property.exampleListProperty);
        _property.reorderableList = reorderableList;

        _propertyDataPerPropertyPath.Add(property.propertyPath, _property);
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        Init(property);
        var fieldRect = position;
        fieldRect.height = LineHeight;

        using (new EditorGUI.PropertyScope(fieldRect, label, property)) 
        {
            // プロパティを描画
            // EditorGUI環境の場合はDoListを使う
            _property.reorderableList.DoList(fieldRect);
        }
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        Init(property);

        // ReorderableListの高さを取得する
        return _property.reorderableList.GetHeight();
    }
}

結果は次のようになります。

f:id:halya_11:20190203220958p:plain

関連

light11.hatenadiary.com