use half day to script my favorite list handling tools.
to support different height elements

ReorderableListExtend.cs
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System;
using System.Linq;
using System.Collections.Generic;
namespace Kit.Editor
{
public class ReorderableListExtend
{
#region variable
SerializedObject serializedObject;
SerializedProperty property;
private string propertyName;
List<float> elementHeights;
ReorderableList orderList;
Texture2D backgroundImage;
#endregion
#region System
public ReorderableListExtend(SerializedObject serializedObject, string propertyName,
bool dragable = true, bool displayHeader = true, bool displayAddButton = true, bool displayRemoveButton = true)
{
this.propertyName = propertyName;
this.serializedObject = serializedObject;
this.property = serializedObject.FindProperty(this.propertyName);
elementHeights = new List<float>(property.arraySize);
SetHightLightBackgroundImage();
orderList = new ReorderableList(serializedObject, property, dragable, displayHeader, displayAddButton, displayRemoveButton);
orderList.onAddCallback += OnAdd;
orderList.onSelectCallback += OnSelect;
orderList.onRemoveCallback += OnRemove;
orderList.drawHeaderCallback += OnDrawHeader;
orderList.drawElementCallback += OnDrawElement;
orderList.drawElementBackgroundCallback += OnDrawElementBackground;
orderList.elementHeightCallback += OnCalculateItemHeight;
}
~ReorderableListExtend()
{
orderList.onAddCallback -= OnAdd;
orderList.onSelectCallback -= OnSelect;
orderList.onRemoveCallback -= OnRemove;
orderList.drawHeaderCallback -= OnDrawHeader;
orderList.drawElementCallback -= OnDrawElement;
orderList.drawElementBackgroundCallback -= OnDrawElementBackground;
orderList.elementHeightCallback -= OnCalculateItemHeight;
backgroundImage = null;
}
#endregion
#region API
public virtual void SetHightLightBackgroundImage()
{
backgroundImage = new Texture2D(3, 1);
backgroundImage.SetPixel(0, 0, new Color(0f, .8f, .7f));
backgroundImage.hideFlags = HideFlags.DontSave;
backgroundImage.wrapMode = TextureWrapMode.Clamp;
backgroundImage.Apply();
}
public void DoLayoutList()
{
orderList.DoLayoutList();
}
public void DoList(Rect rect)
{
orderList.DoList(rect);
}
#endregion
#region listener
protected virtual void OnDrawHeader(Rect rect)
{
EditorGUI.LabelField(rect, property.displayName);
}
private void OnAdd(ReorderableList list)
{
int index = list.serializedProperty.arraySize;
list.serializedProperty.arraySize++;
list.index = index;
SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
OnAdd(list, element);
}
private void OnRemove(ReorderableList list)
{
SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(list.index);
OnRemove(list, element);
}
private void OnSelect(ReorderableList list)
{
SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(list.index);
OnSelect(list, element);
}
private void OnDrawElement(Rect rect, int index, bool active, bool focused)
{
if (property == null || property.arraySize <= index)
return;
SerializedProperty element = property.GetArrayElementAtIndex(index);
float height = EditorGUI.GetPropertyHeight(element) + EditorGUIUtility.standardVerticalSpacing;
RenewElementHeight(index, height);
rect.height = height;
rect.width -= 40;
rect.x += 20;
OnDrawElement(rect, index, active, focused, element);
}
private void OnDrawElementBackground(Rect rect, int index, bool active, bool focused)
{
if (property == null || property.arraySize <= index)
return;
SerializedProperty element = property.GetArrayElementAtIndex(index);
float height = elementHeights[index];
rect.height = height;
rect.width -= 4;
rect.x += 2;
OnDrawElementBackground(rect, index, active, focused, element, height);
}
#endregion
#region Template
protected virtual void OnAdd(ReorderableList list, SerializedProperty newElement) { }
protected virtual void OnSelect(ReorderableList list, SerializedProperty selectedElement) { }
protected virtual void OnRemove(ReorderableList list, SerializedProperty deleteElement)
{
if (EditorUtility.DisplayDialog(
"Warning !",
"Are you sure you want to delete:\n\r[ " + deleteElement.displayName + " ] ?",
"Yes", "No"))
{
ReorderableList.defaultBehaviours.DoRemoveButton(list);
}
}
protected virtual void OnDrawElement(Rect rect, int index, bool active, bool focused, SerializedProperty element)
{
EditorGUI.PropertyField(rect, element, true);
}
protected virtual void OnDrawElementBackground(Rect rect, int index, bool active, bool focused, SerializedProperty element, float height)
{
if(active)
EditorGUI.DrawTextureTransparent(rect, backgroundImage, ScaleMode.ScaleAndCrop);
}
#endregion
#region height hotfix
private void RenewElementHeight(int index, float height)
{
try
{
elementHeights[index] = height;
}
catch
{
}
finally
{
ElementListOverflowFix();
}
}
private float OnCalculateItemHeight(int index)
{
float height = 0f;
try
{
if(height != elementHeights[index])
{
height = elementHeights[index];
EditorUtility.SetDirty(serializedObject.targetObject);
}
}
catch
{
}
finally
{
ElementListOverflowFix();
}
return height;
}
private void ElementListOverflowFix()
{
if (property.arraySize != elementHeights.Count)
{
float[] floats = elementHeights.ToArray();
Array.Resize(ref floats, property.arraySize);
elementHeights = floats.ToList();
EditorUtility.SetDirty(serializedObject.targetObject);
}
}
#endregion
}
}
How to use it ?
use it without override.
using UnityEngine;
using UnityEditor;
using Kit.Editor;
namespace Kit.UI
{
[CustomEditor(typeof(PanelQueue))]
public class PanelQueueEditor : UnityEditor.Editor
{
readonly GUIContent prefabListLabel = new GUIContent("Prefab Queue", "Queue for loaded");
ReorderableListExtend prefabList;
private void OnEnable()
{
// Step 1, init
prefabList = new ReorderableListExtend(serializedObject, "m_PrefabBehaviour");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Canvas"));
// Step 2, print it out.
prefabList.DoLayoutList();
if (GUI.changed)
serializedObject.ApplyModifiedProperties();
}
}
}
Use it as template and override with your own method.
// Step 1: inheriting <ReorderableListExtend>
public class MyList : ReorderableListExtend
{
// Step 2: redirect constructor
public MyList(SerializedObject serializedObject, string propertyName,
bool dragable = true, bool displayHeader = true, bool displayAddButton = true, bool displayRemoveButton = true)
: base(serializedObject, propertyName, dragable, displayHeader, displayAddButton, displayRemoveButton)
{}
// Step 3: using override to re-wrote function(s)
//
//
}
hints: use IDE to do that. make your life easier

that’s all we need.
know bugs:
- during reorder the element’s child information might not follow the existing collapse status.