Unity编辑器拓展之七:如何在PreferencesWindow中新增条目

本文介绍如何在PreferencesWindow中新增条目

Unity编辑器拓展之六中,有介绍到部分PreferencesWindow.cs的代码,而本文从源码开始给大家介绍。

PreferencesWindow.cs中:

private void AddCustomSections()
{
    Assembly[] loadedAssemblies = EditorAssemblies.loadedAssemblies;
    for (int i = 0; i < loadedAssemblies.Length; i++)
    {
        Assembly assembly = loadedAssemblies[i];
        Type[] typesFromAssembly = AssemblyHelper.GetTypesFromAssembly(assembly);
        Type[] array = typesFromAssembly;
        for (int j = 0; j < array.Length; j++)
        {
            Type type = array[j];
            MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
            for (int k = 0; k < methods.Length; k++)
            {
                MethodInfo methodInfo = methods[k];

                //关键在这一行
                PreferenceItem preferenceItem = Attribute.GetCustomAttribute(methodInfo, typeof(PreferenceItem)) as PreferenceItem;

                if (preferenceItem != null)
                {
                    PreferencesWindow.OnGUIDelegate onGUIDelegate = Delegate.CreateDelegate(typeof(PreferencesWindow.OnGUIDelegate), methodInfo) as PreferencesWindow.OnGUIDelegate;
                    if (onGUIDelegate != null)
                    {
                        this.m_Sections.Add(new PreferencesWindow.Section(preferenceItem.name, onGUIDelegate));
                    }
                }
            }
        }
    }
}

代码片中标注的那一行,MSDN中的介绍是:
“检索应用于程序集、 模块、 类型成员或方法参数的指定类型的自定义属性。”

MSDN中关于该接口的介绍

那么,上述代码中检索了程序中使用 PreferenceItem 的所有method,随后将带有PreferenceItem的methodInfo实例化成一个委托,进而构造一个Section对象,添加到m_Setions集合中。

接着,看Unity 文档,关于 PreferenceItem 的介绍。

https://docs.unity3d.com/2017.2/Documentation/ScriptReference/PreferenceItem.html

PreferenceItem属性允许您添加首选项到preferences窗口。

解释跟源码分析的一样,接下来进行代码测试。

    [PreferenceItem("Preference Item")]
    private static void SelfPreferenceItem()
    {
        EditorGUILayout.LabelField("Self Preference Item", EditorStyles.boldLabel);
        if (GUILayout.Button("Button"))
        {
            Debug.LogError("Click");
        }
        EditorGUILayout.Space();
    }

这里写图片描述

但是,发现有点不对劲,编辑器拓展之六中提到的选中左侧,出现问题了。没有自动选中新增的那一项。

然后通过断点,查看到反射出来的m_Sections集合里,并没有新增的那一项,使得在遍历的时候,没法自动选中它。

也就是说,AddCustomSections函数在此时并没有执行到,使得新增的 “Preference Item” 并没有被添加到m_Sections这个集合里。

查看一下源码中是合适调用的 AddCustomSections函数,

private void OnGUI()
{
    if (this.m_RefreshCustomPreferences)
    {
        this.AddCustomSections();
        this.m_RefreshCustomPreferences = false;
    }
    //节省篇幅,后面代码省略
}

也就是当m_RefreshCustomPreferences值为true时,执行一遍AddCustomSections函数。

完善代码:

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

public class OpenPreferencesWindow : EditorWindow
{
    [MenuItem("Tool/OpenPreferencesWindow")]
    public static void Open()
    {
        OpenPreferencesWindow editor = EditorWindow.GetWindow<OpenPreferencesWindow>();
    }

    private void OnGUI()
    {
        if (GUILayout.Button("打开PerferencesWindow"))
        {
            Assembly assembly = Assembly.GetAssembly(typeof(UnityEditor.EditorWindow));
            Type type = assembly.GetType("UnityEditor.PreferencesWindow");
            type.GetMethod("ShowPreferencesWindow", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);

            EditorWindow window = EditorWindow.GetWindow(type);

            //解决不能自动定位 新增条目 的问题
            //手动调用一遍AddCustomSections函数
            FieldInfo refreshField = type.GetField("m_RefreshCustomPreferences", BindingFlags.NonPublic | BindingFlags.Instance);
            if ((bool)refreshField.GetValue(window))
            {
                type.GetMethod("AddCustomSections", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(window, null);
                refreshField.SetValue(window, false);
            }

            FieldInfo sectionsField = type.GetField("m_Sections", BindingFlags.Instance | BindingFlags.NonPublic);
            IList sections = sectionsField.GetValue(window) as IList;

            Type sectionType = sectionsField.FieldType.GetGenericArguments()[0];
            FieldInfo sectionContentField = sectionType.GetField("content", BindingFlags.Instance | BindingFlags.Public);
            for (int i = 0; i < sections.Count; i++)
            {
                GUIContent sectionContent = sectionContentField.GetValue(sections[i]) as GUIContent;
                if (sectionContent.text == "Preference Item")
                {
                    FieldInfo sectionIndexField = type.GetField("m_SelectedSectionIndex", BindingFlags.Instance | BindingFlags.NonPublic);
                    sectionIndexField.SetValue(window, i);
                    return;
                }
            }
        }
    }

    [PreferenceItem("Preference Item")]
    private static void SelfPreferenceItem()
    {
        EditorGUILayout.LabelField("Self Preference Item", EditorStyles.boldLabel);
        if (GUILayout.Button("Button"))
        {
            Debug.LogError("Click");
        }
        EditorGUILayout.Space();
    }
}

完美收工!

以上知识分享,如有错误,欢迎指出,共同学习,共同进步。

猜你喜欢

转载自blog.csdn.net/qq_26999509/article/details/79834218