Pertanyaan Memfilter pada JTree


Masalah

Menerapkan filter pada JTree untuk menghindari node / daun tertentu untuk ditampilkan dalam versi yang diberikan JTree. Idealnya saya mencari solusi yang memungkinkan untuk memiliki filter dinamis, tapi saya sudah senang jika saya bisa mendapatkan filter statis untuk bekerja.

Untuk membuatnya sedikit lebih mudah, mari kita anggap JTree hanya mendukung render, dan tidak mengedit. Memindahkan, menambahkan, menghapus node harus dimungkinkan.

Contohnya adalah bidang pencarian di atas a JTree, dan saat mengetik JTree hanya akan menampilkan subtree dengan korek api.

Beberapa pembatasan: itu harus digunakan dalam proyek yang memiliki akses ke JDK dan SwingX. Saya ingin menghindari menyertakan lib pihak ketiga lainnya.

Saya sudah memikirkan beberapa kemungkinan solusi, tetapi tak satu pun dari keduanya tampak ideal

Pendekatan

Pemfilteran berbasis model

  • menghias TreeModel untuk menyaring beberapa nilai. Versi cepat dan kotor mudah ditulis. Filter node, dan pada setiap perubahan filter atau delegasi TreeModel penghias dapat menembakan peristiwa bahwa seluruh pohon berubah (treeStructureChanged dengan simpul akar sebagai simpul). Gabungkan ini dengan pendengar yang mengembalikan status seleksi dan status ekspansi JTree dan Anda mendapatkan versi yang berfungsi lebih atau kurang, tetapi peristiwa yang berasal dari TreeModel kacau. Ini kurang lebih pendekatan yang digunakan dalam pertanyaan ini
  • menghias TreeModel tetapi cobalah aktifkan acara yang benar. Saya belum (belum) berhasil membuat versi kerja ini. Tampaknya membutuhkan salinan dari delegasi TreeModel untuk dapat mengaktifkan peristiwa dengan indeks turunan yang benar ketika node dihapus dari model delegasi. Saya pikir dengan beberapa waktu lagi saya bisa mendapatkan ini untuk bekerja, tetapi itu hanya terasa salah (penyaringan terasa seperti sesuatu yang seharusnya dilakukan oleh pandangan, dan bukan modelnya)
  • menghias struktur data apa pun yang digunakan untuk membuat awal TreeModel. Namun, ini benar-benar tidak dapat digunakan kembali, dan mungkin sekeras untuk menulis dekorator untuk TreeModel

Lihat berdasarkan pemfilteran

Sepertinya ini jalannya. Pemfilteran tidak boleh mempengaruhi model tetapi hanya tampilan.

  • Saya melihat RowFilter kelas. Meskipun javadoc tampaknya menyarankan Anda dapat menggunakannya dalam kombinasi dengan JTree:

    ketika dikaitkan dengan JTree, sebuah entri berhubungan dengan sebuah node.

    Saya tidak dapat menemukan tautan apa pun RowFilter (atau RowSorter) dan JTree kelas. Penerapan standar dari RowFilter dan tutorial Swing sepertinya menyarankan itu RowFilter hanya dapat digunakan secara langsung dengan JTable (Lihat JTable#setRowSorter). Tidak ada metode serupa yang tersedia di JTree

  • Saya juga melihat JXTree javadoc. Ia memiliki a ComponentAdapter tersedia dan javadoc dari ComponentAdapter menunjukkan a RowFilter dapat berinteraksi dengan komponen target, tetapi saya gagal melihat bagaimana saya membuat tautan di antara RowFilter dan JTree
  • Saya belum melihat bagaimana a JTable menangani penyaringan dengan RowFilters, dan mungkin hal yang sama dapat dilakukan pada versi modifikasi dari JTree.

Singkatnya: Saya tidak tahu apa pendekatan terbaik untuk memecahkan masalah ini

Catatan: pertanyaan ini kemungkinan merupakan duplikat dari pertanyaan ini, tapi pertanyaan itu masih belum terjawab, pertanyaannya agak pendek dan jawabannya sepertinya tidak lengkap, jadi saya berpikir untuk memposting pertanyaan baru. Jika ini tidak dilakukan (FAQ tidak memberikan jawaban yang jelas tentang ini) saya akan memperbarui pertanyaan 3 tahun yang lalu


32
2018-02-10 20:19


asal


Jawaban:


Pemfilteran berbasis tampilan adalah cara terbaik. Anda dapat menggunakan sesuatu seperti contoh yang saya kodekan di bawah ini. Praktik umum lainnya ketika memfilter pohon adalah beralih ke tampilan daftar ketika memfilter pohon, karena daftar tidak mengharuskan Anda untuk menunjukkan node tersembunyi yang keturunannya perlu ditampilkan.

Ini benar-benar kode yang menghebohkan (saya mencoba untuk memotong setiap sudut mungkin untuk mencambuknya sekarang), tetapi seharusnya cukup untuk Anda mulai. Cukup ketik permintaan Anda di kotak pencarian dan tekan Enter, dan itu akan menyaring model default JTree. (FYI, 90 baris pertama hanya menghasilkan kode boiler dan tata letak.)

package com.example.tree;

import java.awt.BorderLayout;

public class FilteredJTreeExample extends JFrame {

    private JPanel contentPane;
    private JTextField textField;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    FilteredJTreeExample frame = new FilteredJTreeExample();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public FilteredJTreeExample() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        setContentPane(contentPane);

        JPanel panel = new JPanel();
        contentPane.add(panel, BorderLayout.NORTH);
        GridBagLayout gbl_panel = new GridBagLayout();
        gbl_panel.columnWidths = new int[]{34, 116, 0};
        gbl_panel.rowHeights = new int[]{22, 0};
        gbl_panel.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE};
        gbl_panel.rowWeights = new double[]{0.0, Double.MIN_VALUE};
        panel.setLayout(gbl_panel);

        JLabel lblFilter = new JLabel("Filter:");
        GridBagConstraints gbc_lblFilter = new GridBagConstraints();
        gbc_lblFilter.anchor = GridBagConstraints.WEST;
        gbc_lblFilter.insets = new Insets(0, 0, 0, 5);
        gbc_lblFilter.gridx = 0;
        gbc_lblFilter.gridy = 0;
        panel.add(lblFilter, gbc_lblFilter);

        JScrollPane scrollPane = new JScrollPane();
        contentPane.add(scrollPane, BorderLayout.CENTER);
        final JTree tree = new JTree();
        scrollPane.setViewportView(tree);

        textField = new JTextField();
        GridBagConstraints gbc_textField = new GridBagConstraints();
        gbc_textField.fill = GridBagConstraints.HORIZONTAL;
        gbc_textField.anchor = GridBagConstraints.NORTH;
        gbc_textField.gridx = 1;
        gbc_textField.gridy = 0;
        panel.add(textField, gbc_textField);
        textField.setColumns(10);
        textField.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                TreeModel model = tree.getModel();
                tree.setModel(null);
                tree.setModel(model);
            }
        });

        tree.setCellRenderer(new DefaultTreeCellRenderer() {
            private JLabel lblNull = new JLabel();

            @Override
            public Component getTreeCellRendererComponent(JTree tree, Object value,
                    boolean arg2, boolean arg3, boolean arg4, int arg5, boolean arg6) {

                Component c = super.getTreeCellRendererComponent(tree, value, arg2, arg3, arg4, arg5, arg6);

                DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
                if (matchesFilter(node)) {
                    c.setForeground(Color.BLACK);
                    return c;
                }
                else if (containsMatchingChild(node)) {
                    c.setForeground(Color.GRAY);
                    return c;
                }
                else {
                    return lblNull;
                }
            }

            private boolean matchesFilter(DefaultMutableTreeNode node) {
                return node.toString().contains(textField.getText());
            }

            private boolean containsMatchingChild(DefaultMutableTreeNode node) {
                Enumeration<DefaultMutableTreeNode> e = node.breadthFirstEnumeration();
                while (e.hasMoreElements()) {
                    if (matchesFilter(e.nextElement())) {
                        return true;
                    }
                }

                return false;
            }
        });
    }

}

Ketika Anda mengimplementasikannya secara nyata, Anda mungkin ingin membuat sendiri TreeNode dan TreeCellRenderer, menggunakan metode yang kurang bodoh untuk memicu pembaruan, dan mengikuti pemisahan MVC. Perhatikan bahwa node "tersembunyi" masih diberikan, tetapi mereka sangat kecil sehingga Anda tidak dapat melihatnya. Jika Anda menggunakan tombol panah untuk menavigasi pohon, Anda akan melihat bahwa mereka masih ada di sana. Jika Anda hanya membutuhkan sesuatu yang berhasil, ini mungkin cukup baik.

Filtered tree (windows)

Edit

Berikut adalah screenshot dari versi pohon yang tidak difilter dan difilter di Mac OS, menunjukkan bahwa ruang putih terlihat di Mac OS:

Unfiltered treeFiltered tree


7
2018-04-03 21:47



Lihatlah penerapan ini: http://www.java2s.com/Code/Java/Swing-Components/InvisibleNodeTreeExample.htm

Ini menciptakan subclass dari DefaultMutableNode menambahkan properti "isVisible" daripada benar-benar menghapus / menambahkan node dari TreeModel. Cukup manis menurut saya, dan itu memecahkan masalah penyaringan saya dengan rapi.


6
2017-12-14 13:31



Pertanyaan Lama, saya menemukan ... bagi mereka yang menginginkan Solusi yang cepat dan mudah

JUST FILTERING THE VIEW:

Saya tahu itu tidak sebersih Filtering the Model dan dilengkapi dengan kemungkinan backdraw, tetapi jika Anda hanya ingin solusi cepat untuk Aplikasi kecil:

Perluas DefaultTableCellRenderer, timpa getTreeCellRendererComponent - aktifkan super.getTreeCellRendererComponent (...) dan setelah itu atur Tinggi Preferred ke ZERO untuk semua Node yang ingin Anda sembunyikan. Ketika membangun JTree Anda pastikan untuk mengatur setRowHeight (0); - jadi itu akan menghormati Tinggi Pilihan dari setiap baris ...

voila - semua Baris yang disaring tidak terlihat!

CONTOH BEKERJA LENGKAP

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;

public class JTreeExample
{
    public static void main( final String[] args ) throws Exception
    {
        UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );

        // The only correct way to create a SWING Frame...
        EventQueue.invokeAndWait( new Runnable()
            {
                @Override
                public void run()
                {
                    swingMain();
                }
            } );
    }

    protected static void swingMain()
    {
        final JFrame f = new JFrame( "JTree Test" );
        f.setLocationByPlatform( true );
        f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

        final int items = 5;

        final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode( "JTree", true );
        final DefaultTreeModel myModel = new DefaultTreeModel( rootNode );

        final Box buttonBox = new Box( BoxLayout.X_AXIS );

        for( int i = 0; i < items; i++ )
        {
            final String name = "Node " + i;
            final DefaultMutableTreeNode newChild = new DefaultMutableTreeNode( name );
            rootNode.add( newChild );

            final JButton b = new JButton( "Show/Hide " + i );
            buttonBox.add( b );
            b.addActionListener( new ActionListener()
                {
                    @Override
                    public void actionPerformed( final ActionEvent e )
                    {
                        // If the node has a Text, set it to null, otherwise reset it
                        newChild.setUserObject( newChild.getUserObject() == null ? name : null );
                        myModel.nodeStructureChanged( newChild.getParent() );
                    }
                } );
        }

        final JTree tree = new JTree( myModel );
        tree.setRowHeight( 0 );
        tree.setCellRenderer( new JTreeExample.TreeRenderer() );

        f.add( tree, BorderLayout.CENTER );
        f.add( buttonBox, BorderLayout.SOUTH );

        f.setSize( 600, 500 );
        f.setVisible( true );
    }

    public static class TreeRenderer extends DefaultTreeCellRenderer
    {
        @Override
        public Component getTreeCellRendererComponent( final JTree tree, final Object value, final boolean selected,
                                                        final boolean expanded, final boolean leaf, final int row, final boolean hasFocus )
        {
            // Invoke default Implementation, setting all values of this
            super.getTreeCellRendererComponent( tree, value, selected, expanded, leaf, row, hasFocus );

            if( !isNodeVisible( (DefaultMutableTreeNode)value ) )
            {
                setPreferredSize( new Dimension( 0, 0 ) );
            }
            else
            {
                setPreferredSize( new Dimension( 200, 15 ) );
            }

            return this;
        }
    }

    public static boolean isNodeVisible( final DefaultMutableTreeNode value )
    {
        // In this example all Nodes without a UserObject are invisible
        return value.getUserObject() != null;
    }
}

4
2017-12-19 16:41



Berikut ini solusi yang mungkin yang hanya menggunakan komponen Swing standar:

Saya belum pernah menggunakan ini, tetapi saya suka penerapannya lebih dari solusi cepat n yang saya temukan online.


1
2018-04-03 22:10



Saya telah bekerja di workarround untuk memfilter perpanjangan JXTreeTable. Saya telah mengikuti dua model pendekatan untuk kesederhanaan.

Model yang Difilter

public abstract class TellapicModelFilter extends DefaultTreeTableModel {

    protected Map<AbstractMutableTreeTableNode, 
                  AbstractMutableTreeTableNode>  family;
    protected Map<AbstractMutableTreeTableNode, 
                  AbstractMutableTreeTableNode>  filter;
    protected MyTreeTable                        treeTable;
    private   boolean                            withChildren;
    private   boolean                            withParents;

    /**
     * 
     * @param model
     */
    public TellapicModelFilter(MyTreeTable treeTable) {
        this(treeTable, false, false);
    }

    /**
    * 
    * @param treeTable
    * @param wp
    * @param wc
    */
    public TellapicModelFilter(MyTreeTable treeTable, boolean wp, boolean wc) {
        super(new DefaultMutableTreeTableNode("filteredRoot"));
        this.treeTable = treeTable;
        setIncludeChildren(wc);
        setIncludeParents(wp);
    }

    /**
     * 
     */
    public void filter() {
        filter = new HashMap<AbstractMutableTreeTableNode, AbstractMutableTreeTableNode>();
        family = new HashMap<AbstractMutableTreeTableNode, AbstractMutableTreeTableNode>();
        AbstractMutableTreeTableNode filteredRoot = (AbstractMutableTreeTableNode) getRoot();
        AbstractMutableTreeTableNode root = (AbstractMutableTreeTableNode) treeTable.getTreeTableModel().getRoot();
        filterChildren(root, filteredRoot);
        for(AbstractMutableTreeTableNode node : family.keySet())
            node.setParent(null);
        for(AbstractMutableTreeTableNode node : filter.keySet())
            node.setParent(filter.get(node));
    }

    /**
     * 
     * @param node
     * @param filteredNode
     */
    private void filterChildren(AbstractMutableTreeTableNode node, AbstractMutableTreeTableNode filteredNode) {
        int count = node.getChildCount();
        for(int i = 0; i < count; i++) {
            AbstractMutableTreeTableNode child = (AbstractMutableTreeTableNode) node.getChildAt(i);
            family.put(child, node);
            if (shouldBeFiltered(child)) {
                filter.put(child, filteredNode);
                if (includeChildren())
                    filterChildren(child, child);
            } else {
                filterChildren(child, filteredNode);
            }
        }
    }

    /**
     * 
     */
    public void restoreFamily() {
        for(AbstractMutableTreeTableNode child : family.keySet()) {
            AbstractMutableTreeTableNode parent = family.get(child);
            child.setParent(parent);
        }  
    }

    /**
     * 
     * @param node
     * @return
     */
    public abstract boolean shouldBeFiltered(AbstractMutableTreeTableNode node); 

    /**
     * Determines if parents will be included in the filtered result. This DOES NOT means that parent will be filtered
     * with the filter criteria. Instead, if a node {@code}shouldBeFiltered{@code} no matter what the parent node is,
     * include it in the filter result. The use of this feature is to provide contextual data about the filtered node,
     * in the terms of: "where was this node that belongs to?"
     * 
     * @return True is parents should be included anyhow.
     */
    public boolean includeParents() {
        return withParents;
    }

    /**
     * Determines if children should be filtered. When a node {@code}shouldBeFiltered{@code} you can stop the filtering
     * process in that node by setting: {@code}setIncludeChildren(false){@code}. In other words, if you want to filter
     * all the tree, {@code}includeChildren{@code} should return true.
     * 
     * By letting this method return {@code}false{@code} all children of the node filtered will be automatically added
     * to the resulting filter. That is, children aren't filtered with the filter criteria and they will be shown with
     * their parent in the filter result.
     * 
     * @return True if you want to filter all the tree.
     */
    public boolean includeChildren() {
        return withChildren;
    }

    /**
     * 
     * @param include
     */
    public void setIncludeParents(boolean include) {
       withParents = include;
    }

   /**
    * 
    * @param include
    */
   public void setIncludeChildren(boolean include) {
       withChildren = include;
   }

Pada dasarnya, idenya adalah untuk menghubungkan / menghubungkan node dari model asli ke model root yang disaring melacak arus keluarga node.

Model yang difilter akan memiliki pemetaan antara anak-anak dan orang tua, dengan metode yang tepat untuk memulihkan keluarga ini. Metode ´restoreFamily´ akan menghubungkan kembali anak-anak yang hilang.

Model yang difilter akan melakukan sebagian besar pekerjaan di dalamnya filter() metode meninggalkan abstract metode shouldBeFiltered(node) untuk implementasi:

Ini harus diperhitungkan, bahwa tidak perlu untuk tidak menghubungkan SEMUA anak-anak dari keluarga sebelum menghubungkan yang disaring ke root yang difilter. Jika kinerja adalah kunci, perilaku itu bisa dianalisis lebih dalam.

Memperluas JXTreeTable

Terakhir, tetapi yang paling penting, ada kebutuhan untuk memperluas landasan yang mendasarinya dengan menerapkan satu metode dan mengesampingkan yang lain:

@Override
public void setTreeTableModel(TreeTableModel treeModel) {
    if (!(treeModel instanceof TellapicModelFilter))
        model = treeModel;

    super.setTreeTableModel(treeModel);
}

public void setModelFilter(TellapicModelFilter mf) {
    if (modelFilter != null) {
        modelFilter.restoreFamily();
        setTreeTableModel(getUnfilteredModel());
    }
    // Is this necessary?
    if (mf == null) {
        setTreeTableModel(getUnfilteredModel());
    } else {
        modelFilter = mf;
        modelFilter.filter();
        setTreeTableModel(modelFilter);
    }
}

Contoh yang lengkap dan bekerja dengan sesuatu yang bisa ditelusuri dapat ditemukan di sini link. Ini termasuk a Main.java dengan pohon siap pakai. Pengujian GUI memiliki tombol yang menambahkan node di node yang dipilih (jika ada) dan di bagian atas frame bidang teks yang menyaring saat menulis.


1
2017-07-02 16:09



Saya akhirnya berhasil menarik sesuatu yang sesuai dengan kebutuhan saya dengan sempurna, dan berpikir saya akan berbagi jika ada orang lain yang dapat menggunakannya.

Saya telah mencoba menampilkan dua JTrees berdampingan - satu yang berisi daftar yang difilter dari yang lain.

Pada dasarnya saya telah membuat dua TreeModels, dan menggunakan node root yang sama untuk keduanya. Ini tampaknya berfungsi dengan baik sejauh ini, selama saya memastikan untuk mengganti SETIAP metode yang dipanggil dari DefaultTreeModel dalam kode saya, seperti nodeChanged (node ​​TreeNode) atau yang lain akan ada rasa sakit.

Rasa sakit berasal dari fakta bahwa satu-satunya waktu node itu sendiri ditanya untuk informasi seperti childcount, adalah ketika metode jenis nodestructure disebut pada DefaultTreeModel. Selain itu, semua panggilan untuk informasi struktur pohon dapat disadap dan disaring seperti yang ditunjukkan di bawah ini.

Ini bisa menjadi buruk karena Anda harus memastikan Anda menggali setiap kali node itu sendiri ditanya jika Anda menggunakan DefaultTreeModel sebagai basis seperti yang saya lakukan. Masalah ini mungkin tidak ada jika Anda menerapkan TreeModel langsung daripada malas seperti saya. Sumber NodesChanged datang langsung dari sumber JDK.

Saya beruntung bahwa apa yang saya inginkan berarti selalu ada jalan kembali ke simpul akar dari setiap item dalam daftar yang difilter.

Ini semua yang perlu saya lakukan, bahkan saya menghabiskan sepanjang hari mencoba penemuan liar dan kacau, seperti membuat ulang salinan dangkal dari pohon dll, belum lagi banyak membaca di Stack !:

public class FilteredSceneModel extends DefaultTreeModel {

public static boolean isSceneItem(Object child) {
    return !(child instanceof DataItem);
}

public FilteredSceneModel(RootSceneNode root, SelectionModel sm) {
    super(root, sm);
}

private boolean isSceneFolder(Object node) {
    return node instanceof RootSceneNode || node instanceof Floor;
}

@Override
public AbstractSceneItem getChild(Object parent, int index) {
    AbstractSceneItem asi = (AbstractSceneItem) parent;
    if (isSceneItem(parent)) {
        int dex = 0;
        for (AbstractSceneItem child : asi.children) {
            if (isSceneItem(child)) {
                if (dex == index) {
                    return child;
                }
                dex++;
            }
        }
    }
    System.out.println("illegal state for: " + parent + " at index: " + index);
    return asi.getChildAt(index);
}

@Override
public int getChildCount(Object parent) {
    if (isSceneItem(parent)) {
        AbstractSceneItem asi = (AbstractSceneItem) parent;
        int count = 0;
        for (AbstractSceneItem child : asi.children) {
            if (isSceneItem(child)) {
                count++;
            }
        }
        return count;
    }
    return -1;
}

@Override
public int getIndexOfChild(Object parent, Object childItem) {
    if (isSceneItem(parent)) {
        AbstractSceneItem asi = (AbstractSceneItem) parent;
        int count = 0;
        for (AbstractSceneItem child : asi.children) {
            if (isSceneItem(child)) {
                if (child == childItem) {
                    return count;
                }
                count++;
            }
        }
    }
    return -1;
}

@Override
public boolean isLeaf(Object node) {
    if (isSceneItem(node)) {
        if (isSceneFolder(node)) {
            return false;
        }
    }
    return true;
}

@Override
public void activeFloorChanged(Floor floor) {
    for (AbstractSceneItem asi : floor) {
        if (isSceneItem(asi)) {
            nodeChanged(asi);
        }
    }
}

@Override
protected void renamed(AbstractSceneItem asi) {
    if (isSceneItem(asi)) {
        nodeChanged(asi);
        System.out.println("scene only model renamed: " + asi.fullPathToString());
    }
}

@Override
public void nodeChanged(TreeNode tn) {
    if (isSceneItem(tn)) {
        filteredNodeChanged(tn);
    }
}

@Override
public void nodeStructureChanged(TreeNode tn) {
    if (isSceneItem(tn)) {
        super.nodeStructureChanged(tn);
    }
}

private void filteredNodeChanged(TreeNode node) {
    if (listenerList != null && node != null) {
        TreeNode parent = node.getParent();

        if (parent != null) {
            int anIndex = getIndexOfChild(parent, node);
            if (anIndex != -1) {
                int[] cIndexs = new int[1];

                cIndexs[0] = anIndex;
                nodesChanged(parent, cIndexs);
            }
        } else if (node == getRoot()) {
            nodesChanged(node, null);
        }
    }
}

@Override
public void nodesChanged(TreeNode node, int[] childIndices) {
    if (node != null) {
        if (childIndices != null) {
            int cCount = childIndices.length;

            if (cCount > 0) {
                Object[] cChildren = new Object[cCount];

                for (int counter = 0; counter < cCount; counter++) {
                    cChildren[counter] = getChild(node, childIndices[counter]);
                }
                fireTreeNodesChanged(this, getPathToRoot(node),
                        childIndices, cChildren);
            }
        } else if (node == getRoot()) {
            fireTreeNodesChanged(this, getPathToRoot(node), null, null);
        }
    }
}
}

1
2017-08-15 10:01



ETable, sebuah subkelas dari JTable dan kelas induk dari Outline, dijelaskan sini, termasuk "fitur Filter Cepat memungkinkan untuk hanya menampilkan baris tertentu dari model (lihat setQuickFilter()). "Meskipun ini melanggar persyaratan" lib pihak ketiga ", OutlineJAR tidak memiliki ketergantungan selain JDK.


1
2018-06-04 11:47