Editor kustom
Navigasi dan Pathfinding

TreeView

Informasi di halaman ini mengasumsikan pembaca memiliki pengetahuan dasar konsep IMGUI (Immediate Mode GUI). Untuk informasi tentang IMGUI dan menyesuaikan jendela Editor, merujuk ke Memperpanjang Editor dan .

TreeView adalah kontrol IMGUI yang digunakan untuk menampilkan data hirarkis yang dapat Anda kembangkan dan runtuh. Gunakan TreeView untuk membuat tampilan daftar yang sangat dapat disesuaikan dan meja multi-kolumn untuk jendela Editor, yang dapat Anda gunakan bersama kontrol dan komponen IMGUI lainnya.

Lihat dokumentasi API Scripting Unity pada TreeView untuk informasi tentang fungsi API TreeView yang tersedia.

Example of a TreeView with a MultiColumnHeader and a SearchField.
Contoh TreeView dengan MultiColumnHeader dan Penelusuran.

Perhatikan bahwa TreeView bukan model data pohon. Anda dapat membangun TreeView menggunakan struktur data pohon yang Anda sukai. Ini bisa menjadi model pohon C #, atau struktur pohon berbasis Unity seperti hierarki Transform.

rendering TreeView ditangani dengan menentukan daftar item yang diperluas yang disebut baris. Setiap baris mewakili satu TreeViewItem. Setiap TreeViewItem mengandung informasi induk dan anak-anak, yang digunakan oleh TreeView untuk menangani navigasi (kunci dan input mouse).

TreeView memiliki satu akar TreeViewItem yang tersembunyi dan tidak muncul di Editor. Artikel ini adalah akar dari semua barang lainnya.

Kelas dan metode penting

Kelas yang paling penting selain dari TreeView sendiri adalah TreeViewItem dan TreeViewState.

TreeViewState (TreeViewState) mengandung informasi state yang berubah ketika berinteraksi dengan bidang TreeView di Editor, seperti state seleksi, state diperluas, state navigasi, dan state scroll. TreeViewState adalah satu-satunya keadaan yaitu serializable. PohonView sendiri tidak serializable - itu direkonstruksi dari data yang mewakili ketika dibangun atau dimuat kembali. Tambahkan TreeViewState sebagai bidang di kelas EditorWindow-derived Anda untuk memastikan bahwa negara yang berubah pengguna tidak hilang ketika mengisi ulang scriptsSepotong kode yang memungkinkan Anda untuk membuat Komponen Anda sendiri, memicu peristiwa permainan, memodifikasi sifat komponen dari waktu ke waktu dan menanggapi input pengguna dengan cara apa pun yang Anda sukai. More info
Lihat di Glossary
atau memasukkan mode Play (lihat dokumentasi pada memperluas Editor untuk informasi tentang cara melakukan ini). Sebagai contoh kelas yang mengandung bidang TreeViewState, lihat Contoh 1: TreeView sederhana, di bawah ini.

TreeViewItem (TreeViewItem) berisi data tentang item TreeView individu, dan digunakan untuk membangun representasi struktur pohon dalam Editor. Setiap TreeViewItem harus dibangun dengan ID bilangan bulat yang unik (sebagai barang di TreeView). ID digunakan untuk menemukan item di pohon untuk state seleksi, state diperluas, dan navigasi. Jika pohon mewakili benda Unity, gunakan GetInstanceID untuk setiap objek sebagai ID untuk TreeViewItem. ID yang digunakan pada TreeViewState untuk bertahan keadaan yang berubah pengguna (seperti item yang diperluas) ketika memuat ulang skrip atau memasukkan mode Play di Editor.

Semua TreeViewItems memiliki properti depth, yang menunjukkan indentasi visual. Lihat contoh Memastikan TreeView di bawah ini untuk informasi lebih lanjut.

BuildRoot (BuildRoot) adalah metode abstrak tunggal dari kelas TreeView yang harus diimplementasikan untuk membuat TreeView. Gunakan metode ini untuk menangani membuat item akar pohon. Ini disebut setiap kali Reload disebut di pohon. Untuk pohon sederhana yang menggunakan set data kecil, buat seluruh pohon TreeViewItems di bawah item akar di BuildRoot. Untuk pohon yang sangat besar, tidak optimal untuk membuat seluruh pohon pada setiap reload. Dalam situasi ini, membuat akar dan kemudian menimpa metode BuildRows untuk hanya membuat item untuk baris saat ini. Sebagai contoh dari BuildRoot dalam penggunaan, lihat Contoh 1: TreeView sederhana di bawah ini.

BuildRows (BuildRows) adalah metode virtual di mana pelaksanaan default menangani membangun daftar baris berdasarkan pohon penuh yang dibuat di BuildRoot. Jika hanya akar dibuat dalam BuildRoot, metode ini harus ditimidasi untuk menangani baris yang diperluas. Lihat Memastikan TreeView, di bawah ini, untuk informasi lebih lanjut.

Diagram ini meringkas urutan dan pengulangan metode acara BuildRoot dan BuildRows selama masa pakai TreeView. Perhatikan bahwa metode BuildRoot disebut sekali setiap kali Reload disebut. BuildRows disebut lebih sering karena disebut sekali pada Reload (kanan setelah BuildRoot) dan setiap kali TreeViewItem diperluas atau runtuh.

Memastikan TreeView

TreeView diinisialisasi ketika metode Reload disebut dari objek TreeView.

Ada dua cara untuk mengatur TreeView:

  1. Create the full tree - Buat TreeViewItems untuk semua item dalam data model pohon. Ini adalah default dan membutuhkan kode lebih sedikit untuk mengatur. Pohon penuh dibangun ketika BuildRoot disebut dari objek TreeView.

  2. Create only the expanded items - Pendekatan ini mengharuskan Anda untuk menimpa BuildRows untuk mengontrol baris yang ditunjukkan, dan BuildRoot hanya digunakan untuk membuat akar TreeViewItem. Pendekatan ini meningkatkan yang terbaik dengan set data besar atau data yang sering berubah.

Gunakan pendekatan pertama untuk set data kecil, atau untuk data yang tidak berubah sering. Gunakan pendekatan kedua untuk set data besar, atau data yang berubah sering, karena lebih cepat untuk hanya membuat item yang diperluas daripada pohon penuh.

Ada tiga cara Anda dapat mengatur TreeViewItems:

  • Buat TreeViewItems dengan anak-anak, orang tua, dan kedalaman diinisialisasi dari awal.

  • Buat TreeViewItems dengan orang tua dan anak-anak kemudian gunakan SetupDepthsFromParentsAndChildren untuk mengatur kedalaman.

  • Buat TreeViewItems hanya dengan informasi mendalam dan kemudian gunakan SetupDepthsFromParentsAndChildren untuk mengatur referensi induk dan anak-anak.

Examples

Untuk melihat Proyek dan kode sumber untuk contoh yang ditunjukkan di bawah ini, unduh.

Contoh 1: TreeView sederhana

Untuk membuat TreeView, buat kelas yang memperpanjang kelas TreeView dan menerapkan metode abstrak BuildRoot. Contoh berikut membuat TreeView sederhana.

class SimpleTreeView : TreeView
{
    public SimpleTreeView(TreeViewState treeViewState)
        : base(treeViewState)
    {
        Reload();
    }
        
    protected override TreeViewItem BuildRoot ()
    {
        // BuildRoot is called every time Reload is called to ensure that TreeViewItems 
        // are created from data. Here we create a fixed set of items. In a real world example,
        // a data model should be passed into the TreeView and the items created from the model.

        // This section illustrates that IDs should be unique. The root item is required to 
        // have a depth of -1, and the rest of the items increment from that.
        var root = new TreeViewItem {id = 0, depth = -1, displayName = "Root"};
        var allItems = new List<TreeViewItem> 
        {
            new TreeViewItem {id = 1, depth = 0, displayName = "Animals"},
            new TreeViewItem {id = 2, depth = 1, displayName = "Mammals"},
            new TreeViewItem {id = 3, depth = 2, displayName = "Tiger"},
            new TreeViewItem {id = 4, depth = 2, displayName = "Elephant"},
            new TreeViewItem {id = 5, depth = 2, displayName = "Okapi"},
            new TreeViewItem {id = 6, depth = 2, displayName = "Armadillo"},
            new TreeViewItem {id = 7, depth = 1, displayName = "Reptiles"},
            new TreeViewItem {id = 8, depth = 2, displayName = "Crocodile"},
            new TreeViewItem {id = 9, depth = 2, displayName = "Lizard"},
        };
            
        // Utility method that initializes the TreeViewItem.children and .parent for all items.
        SetupParentsAndChildrenFromDepths (root, allItems);
            
        // Return root of the tree
        return root;
    }
}

Dalam contoh ini, informasi kedalaman digunakan untuk membangun TreeView. Akhirnya, panggilan ke SetupDepthsFromParentsAndChildren menetapkan data induk dan anak-anak dari TreeViewItems.

Catatan bahwa ada dua cara untuk mengatur TreeViewItem: Mengatur orang tua dan anak-anak langsung, atau gunakan metode AddChild, seperti yang ditunjukkan dalam contoh berikut:

protected override TreeViewItem BuildRoot()
{
    var root = new TreeViewItem      { id = 0, depth = -1, displayName = "Root" };
    var animals = new TreeViewItem   { id = 1, displayName = "Animals" };
    var mammals = new TreeViewItem   { id = 2, displayName = "Mammals" };
    var tiger = new TreeViewItem     { id = 3, displayName = "Tiger" };
    var elephant = new TreeViewItem  { id = 4, displayName = "Elephant" };
    var okapi = new TreeViewItem     { id = 5, displayName = "Okapi" };
    var armadillo = new TreeViewItem { id = 6, displayName = "Armadillo" };
    var reptiles = new TreeViewItem  { id = 7, displayName = "Reptiles" };
    var croco = new TreeViewItem     { id = 8, displayName = "Crocodile" };
    var lizard = new TreeViewItem    { id = 9, displayName = "Lizard" };

    root.AddChild(animals);
    animals.AddChild(mammals);
    animals.AddChild(reptiles);
    mammals.AddChild(tiger);
    mammals.AddChild(elephant);
    mammals.AddChild(okapi);
    mammals.AddChild(armadillo);
    reptiles.AddChild(croco);
    reptiles.AddChild(lizard);

    SetupDepthsFromParentsAndChildren(root);

    return root;
}

Alternatif metode BuildRoot untuk kelas SimpleTreeView di atas

Contoh berikut menunjukkan EditorWindow yang mengandung SimpleTreeView. TreeViews dibangun dengan instance TreeViewState. Pengelola TreeView harus menentukan bagaimana keadaan pandangan ini harus ditangani: apakah keadaannya harus bertahan sampai sesi Unity berikutnya, atau apakah itu hanya harus melestarikan keadaannya setelah skrip diisi ulang (baik ketika memasuki mode Play atau skrip rekompiling). Dalam contoh ini, TreeViewState di serialisasi dalam EditorWindow, memastikan TreeView melestarikan keadaannya ketika Editor ditutup dan dibuka kembali.

using System.Collections.Generic;
using UnityEngine;
using UnityEditor.IMGUI.Controls;

class SimpleTreeViewWindow : EditorWindow
{
    // SerializeField is used to ensure the view state is written to the window 
    // layout file. This means that the state survives restarting Unity as long as the window
    // is not closed. If the attribute is omitted then the state is still serialized/deserialized.
    [SerializeField] TreeViewState m_TreeViewState;

    //The TreeView is not serializable, so it should be reconstructed from the tree data.
    SimpleTreeView m_SimpleTreeView;

    void OnEnable ()
    {
        // Check whether there is already a serialized view state (state 
        // that survived assembly reloading)
        if (m_TreeViewState == null)
            m_TreeViewState = new TreeViewState ();

        m_SimpleTreeView = new SimpleTreeView(m_TreeViewState);
    }

    void OnGUI ()
    {
        m_SimpleTreeView.OnGUI(new Rect(0, 0, position.width, position.height));
    }

    // Add menu named "My Window" to the Window menu
    [MenuItem ("TreeView Examples/Simple Tree Window")]
    static void ShowWindow ()
    {
        // Get existing open window or if none, make a new one:
        var window = GetWindow<SimpleTreeViewWindow> ();
        window.titleContent = new GUIContent ("My Window");
        window.Show ();
    }
}

Contoh 2: Pohon multi-kolomView

Contoh ini menggambarkan TreeView multi-kolom yang menggunakan kelas MultiColumnHeader.

MultiColumnHeader mendukung fungsi berikut: item renaming, multi-selection, item pemesanan dan konten baris kustom menggunakan kontrol IMGUI normal (seperti slider dan bidang objek), penyortiran kolom, dan penyaringan dan pencarian baris.

Contoh ini menciptakan model data menggunakan kelas TreeElement dan TreeModel. PohonView mengambil data dari "TreeModel". Dalam contoh ini, kelas TreeElement dan TreeModel telah dibangun untuk menunjukkan fitur kelas TreeView. Kelas-kelas ini telah dimasukkan dalam Proyek Contoh TreeView (). Contoh ini juga menunjukkan bagaimana struktur model pohon serialisasi ke Login Sitemap dan disimpan dalam Aset.

[Serializable]
//The TreeElement data class is extended to hold extra data, which you can show and edit in the front-end TreeView.
internal class MyTreeElement : TreeElement
{
    public float floatValue1, floatValue2, floatValue3;
    public Material material;
    public string text = "";
    public bool enabled = true;

    public MyTreeElement (string name, int depth, int id) : base (name, depth, id)
    {
        floatValue1 = Random.value;
        floatValue2 = Random.value;
        floatValue3 = Random.value;
    }
}

Kelas ScriptableObject berikut memastikan bahwa data tetap dalam Aset ketika pohon serial.

[CreateAssetMenu (fileName = "TreeDataAsset", menuName = "Tree Asset", order = 1)]
public class MyTreeAsset : ScriptableObject
{
    [SerializeField] List<MyTreeElement> m_TreeElements = new List<MyTreeElement> ();

    internal List<MyTreeElement> treeElements
    {
        get { return m_TreeElements; }
        set { m_TreeElements = value; }
    }
}

Konstruksi MultiColumn Kelas TreeView

Contoh berikut menunjukkan cuplikan kelas MultiColumnTreeView, yang menggambarkan bagaimana kolom multi GUI dicapai. Temukan kode sumber penuh di Proyek Contoh TreeView ().

public MultiColumnTreeView (TreeViewState state, 
                            MultiColumnHeader multicolumnHeader, 
                            TreeModel<MyTreeElement> model) 
                            : base (state, multicolumnHeader, model)
{
    // Custom setup
    rowHeight = 20;
    columnIndexForTreeFoldouts = 2;
    showAlternatingRowBackgrounds = true;
    showBorder = true;
    customFoldoutYOffset = (kRowHeights - EditorGUIUtility.singleLineHeight) * 0.5f; 
    extraSpaceBeforeIconAndLabel = kToggleWidth;
    multicolumnHeader.sortingChanged += OnSortingChanged;
            
    Reload();
}

Perubahan kustom dalam sampel kode di atas membuat penyesuaian berikut:

  • rowHeight = 20: Mengubah ketinggian default (yang didasarkan pada titik 16 EditorGUIUtility.singleLineHeight) ke 20, untuk menambahkan lebih banyak ruang untuk kontrol GUI.

  • columnIndexForTreeFoldouts = 2: Dalam contoh, panah lipat ditampilkan di kolom ketiga karena nilai ini ditetapkan ke 2 (lihat gambar di atas). Jika nilai ini tidak berubah, lipatan diberikan di kolom pertama, karena “columnIndexForTreeFoldouts” adalah 0 secara default.

  • showAlternatingRowBackgrounds = true: Mengubah warna latar belakang baris, sehingga setiap baris berbeda.

  • showBorder = true: Mengirimkan TreeView dengan margin di sekitarnya, sehingga batas tipis ditunjukkan untuk menentukannya dari sisa konten

  • customFoldoutYOffset = (kRowHeights - EditorGUIUtility.singleLineHeight) * 0.5f: Pusat lipatan vertikal di baris - lihat Kustomisasi GUI di bawah ini.

  • extraSpaceBeforeIconAndLabel = 20: Membuat ruang sebelum label pohon sehingga tombol toggle ditunjukkan.

  • multicolumnHeader.sortingChanged += OnSortingChanged: Menetapkan metode untuk acara untuk mendeteksi ketika mengurutkan perubahan dalam komponen header (ketika kolom header diklik), sehingga baris dari perubahan TreeView untuk mencerminkan keadaan penyortiran.

Kustomisasi GUI

Jika penanganan RowGUI default digunakan, TreeView terlihat seperti contoh SimpleTreeView di atas, hanya dengan lipatan dan label. Ketika menggunakan beberapa nilai data untuk setiap item, Anda harus menimpa metode RowGUI untuk memvisualisasikan nilai-nilai ini.

protected override void RowGUI (RowGUIArgs args)

Sampel kode berikut adalah struktur argumen dari struct RowGUIArgs.

protected struct RowGUIArgs
{
    public TreeViewItem item;
    public string label;
    public Rect rowRect;
    public int row;
    public bool selected;
    public bool focused;
    public bool isRenaming;

    public int GetNumVisibleColumns ()
    public int GetColumn (int visibleColumnIndex)
    public Rect GetCellRect (int visibleColumnIndex)
}

Anda dapat memperpanjang TreeViewItem dan menambahkan data pengguna tambahan (yang membuat kelas yang berasal dari TreeViewItem). Anda kemudian dapat menggunakan data pengguna ini di callback RowGUI. Contoh ini disediakan di bawah ini. Lihat override void RowGUI - contoh ini melemparkan item input ke TreeViewItem<MyTreeElement>.

Ada tiga metode yang terkait dengan penanganan kolom: GetNumVisibleColumns, GetColumn, dan GetCellRect. Anda hanya dapat memanggil ini ketika TreeView dibangun dengan MultiColumnHeader, jika tidak terkecuali dibuang.

protected override void RowGUI (RowGUIArgs args)
{
    var item = (TreeViewItem<MyTreeElement>) args.item;

    for (int i = 0; i < args.GetNumVisibleColumns (); ++i)
    {
        CellGUI(args.GetCellRect(i), item, (MyColumns)args.GetColumn(i), ref args);
    }
}
void CellGUI (Rect cellRect, TreeViewItem<MyTreeElement> item, MyColumns column, ref RowGUIArgs args)
{
    // Center the cell rect vertically using EditorGUIUtility.singleLineHeight.
// This makes it easier to place controls and icons in the cells.
    CenterRectUsingSingleLineHeight(ref cellRect);

    switch (column)
    {

        case MyColumns.Icon1:
            
            // Draw custom texture
GUI.DrawTexture(cellRect, s_TestIcons[GetIcon1Index(item)], ScaleMode.ScaleToFit);
            break;

        case MyColumns.Icon2:

//Draw custom texture 
            GUI.DrawTexture(cellRect, s_TestIcons[GetIcon2Index(item)], ScaleMode.ScaleToFit);
            break;

        case MyColumns.Name:

            // Make a toggle button to the left of the label text
            Rect toggleRect = cellRect;
            toggleRect.x += GetContentIndent(item);
            toggleRect.width = kToggleWidth;
            if (toggleRect.xMax < cellRect.xMax)
                item.data.enabled = EditorGUI.Toggle(toggleRect, item.data.enabled); 

            // Default icon and label
            args.rowRect = cellRect;
            base.RowGUI(args);
            break;

        case MyColumns.Value1:

// Show a Slider control for value 1
            item.data.floatValue1 = EditorGUI.Slider(cellRect, GUIContent.none, item.data.floatValue1, 0f, 1f);
            break;

        case MyColumns.Value2:

// Show an ObjectField for materials
            item.data.material = (Material)EditorGUI.ObjectField(cellRect, GUIContent.none, item.data.material, 
                                          typeof(Material), false);
            break;

        case MyColumns.Value3:

// Show a TextField for the data text string
            item.data.text = GUI.TextField(cellRect, item.data.text);
            break;
    }
}

TreeView FAQ

Q: In my TreeView subclass, I have the functions BuildRoot and RowGUI. Is RowGUI called for every TreeViewItem that got added in the build function, or only for items that are visible on screen in the scroll view?

A: RowGUI hanya disebut untuk item yang terlihat pada layar. Misalnya, jika Anda memiliki 10.000 item, hanya 20 item yang terlihat di layar memiliki RowGUI mereka yang disebut.

Q: Can I get the indices of the rows that are visible on the screen?

Sitemap Gunakan metode GetFirstAndLastVisibleRows.

Q: Can I get the list of rows that are built in BuildRows?

Sitemap Gunakan metode GetRows.

Q: Is it mandatory for any of the overridden functions to call base.Method?

A: Hanya jika metode memiliki perilaku default yang ingin Anda perpanjangkan.

Q: I just want to make list of items (not a tree). Do I have to create the root?

A: Ya, Anda harus selalu memiliki akar. Anda dapat membuat item akar dan mengatur root.children = rows untuk setup cepat.

Q: I’ve added a Toggle to my row - why doesn’t the selection jump to that row when I click on it?

A: Secara default, baris hanya dipilih jika mouse turun tidak dikonsumsi oleh isi baris. Di sini, Toggle Anda mengkonsumsi acara. Untuk memperbaiki ini, gunakan metode SelectionClick sebelum tombol Toggle Anda disebut.

Q: Are there methods I can use before or after all RowGUI methods are called?

Sitemap Lihat dokumentasi API pada BeforeRowsGUI dan AfterRowsGUI.

Q: Is there a simple way to return key focus to the TreeView from API? If I select a FloatField in my row, the row selection becomes gray. How do I make it blue again?

A: Warna biru menunjukkan baris yang saat ini memiliki fokus utama. Karena FloatField memiliki fokus, TreeView kehilangan fokus, sehingga ini adalah perilaku yang dimaksudkan. Set GUIUtility.keyboardControl = treeViewControlID bila diperlukan.

Q: How do I convert from id to a TreeViewItem?

A: Gunakan FindItem atau FindRows.

Q: How do I receive a callback when a user changes their selection in the TreeView?

A: Override metode SelectionChanged (pont berguna lainnya: Sitemap Artikel, dan Login Artikel).

Editor kustom
Navigasi dan Pathfinding