Отображение одного и того же экземпляра JTree с использованием двух разных средств визуализации одновременно

#java #swing #jtree

#java #swing #jtree

Вопрос:

Я пытаюсь визуализировать дерево различий, которое представляет различия между двумя древовидными структурами. Обычно в таких визуализациях используются два экземпляра компонента для отображения левой и правой сторон с указанием различий между ними пользователю, но в моем случае фактическим результатом алгоритма diffing является одно дерево, где каждый узел может одновременно выполнять до двух разных ролей (слева и справа роль).

Пользователю должно казаться JTree , что рядом друг с другом находятся две буквы s. Как реализация достигает этого, не имеет значения. Мой текущий подход заключается в использовании двух синхронизированных JTree экземпляров, совместно использующих модель. Существует ли другой подход к реализации, который использовал бы один JTree экземпляр для достижения того же самого?

Два JTree s, БОК О БОК, — это обязательное условие.

Это не тот вопрос, в котором я мог бы привести жизнеспособный пример кода, но вот переключаемый рендерер, который эмулирует мою древовидную модель:

 package org.stackoverflow.example;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;

public class SameTreeNodesRenderedDifferently extends JFrame {
    
    private JTree tree;
    private JButton button;
    private MyTreeCellRenderer leftRenderer;
    private MyTreeCellRenderer rightRenderer;
    private MyTreeCellRenderer currentRenderer;
    
    public SameTreeNodesRenderedDifferently() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new BorderLayout()); 
        setTitle("SameTreeNodesRenderedDifferently");
        
        DefaultMutableTreeNode root = new DefaultMutableTreeNode(new MyNode("root"));
        createTree(root);
        tree = new JTree(root);
        JScrollPane scroll = new JScrollPane(tree);
        add(scroll);
        
        leftRenderer = new MyTreeCellRenderer(true);
        rightRenderer = new MyTreeCellRenderer(false);
        currentRenderer = leftRenderer;
        tree.setCellRenderer(currentRenderer);
        expandAll();
        
        button = new JButton("Switch renderer");
        add(button, BorderLayout.PAGE_END);
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                currentRenderer = currentRenderer == leftRenderer ? rightRenderer : leftRenderer;
                tree.setCellRenderer(currentRenderer);
                tree.revalidate();
                tree.repaint();
                expandAll();
            }
        });
        setSize(400, 400);
        setLocationRelativeTo(null);
    }
    
    private void createTree(DefaultMutableTreeNode root) {
        DefaultMutableTreeNode a = new DefaultMutableTreeNode(new MyNode("a"));
        root.add(a);
        DefaultMutableTreeNode b = new DefaultMutableTreeNode(new MyNode("b", "!b"));
        a.add(b);
        DefaultMutableTreeNode c = new DefaultMutableTreeNode(new MyNode("c"));
        a.add(c);
        DefaultMutableTreeNode d = new DefaultMutableTreeNode(new MyNode("d"));
        root.add(d);        
        DefaultMutableTreeNode e = new DefaultMutableTreeNode(new MyNode("e", "!e"));
        d.add(e);       
        DefaultMutableTreeNode f = new DefaultMutableTreeNode(new MyNode("f", "!f"));
        d.add(f);       
        DefaultMutableTreeNode g = new DefaultMutableTreeNode(new MyNode("g", "!g"));
        d.add(g);       
        DefaultMutableTreeNode h = new DefaultMutableTreeNode(new MyNode("h"));
        d.add(h);
    }
    
    private void expandAll() {
        for (int i = 0; i < tree.getRowCount(); i  ) {
            tree.expandRow(i);
        }
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new SameTreeNodesRenderedDifferently().setVisible(true);
            }
        });
    }
    
    private static class MyTreeCellRenderer extends DefaultTreeCellRenderer {

        private final boolean left;
        
        public MyTreeCellRenderer(boolean left) {
            this.left = left;
        }
        
        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
            super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            if (value instanceof DefaultMutableTreeNode) {
                DefaultMutableTreeNode dmtn = (DefaultMutableTreeNode) value;
                MyNode node = (MyNode) dmtn.getUserObject();
                setText(left ? node.leftValue : node.rightValue);
            }
            return this;
        }
        
    }
    
    public static class MyNode {
        private final String leftValue;
        private final String rightValue;
        
        public MyNode(String value) {
            this(value, value);
        }
        
        public MyNode(String leftValue, String rightValue) {
            this.leftValue = leftValue;
            this.rightValue = rightValue;
        }

        public String getLeftValue() {
            return leftValue;
        }

        public String getRightValue() {
            return rightValue;
        }

        @Override
        public String toString() {
            return "MyNode{"   "leftValue="   leftValue   ", rightValue="   rightValue   '}';
        }
    }
}

 

Комментарии:

1. Я все еще задаюсь вопросом: зачем идти на «грязные» обходные пути и странный дизайн, когда вы уже знаете «правильный» дизайн (использовать одну модель, но два дерева) ?! Вы видите: модель — это абстракция, которая предназначена для совместного использования между компонентами пользовательского интерфейса. Вы пытаетесь использовать компонент пользовательского интерфейса для чего-то, для чего он не предназначен… почему?

2. @GhostCat, потому что это кажется пустой тратой ресурсов. Внутренне JTree создает свою собственную «изменяемую модель дерева по умолчанию» для каждого дерева, поэтому больше объектов для одного и того же. Кроме того, управление двумя JTrees для синхронизации является деликатным вопросом.

3. Сколько ресурсов вы собираетесь «тратить», делая это правильно? Это похоже на то, что некоторые из присутствующих здесь людей спрашивают о том, как оптимизировать сортировку массива из 100 int.

4. @NomadMaker мы говорим о множестве объектов, которые пользователь может создавать с помощью простого повторяющегося нажатия кнопки. Если я увижу возможности для оптимизации, я не буду думать дважды. Сам JTree использует один экземпляр компонента средства визуализации ячеек для визуализации каждого узла. Как вы думаете, почему это так?

5. Дональд Кнут сказал: «Преждевременная оптимизация — корень всего зла». Он расширил это и заявил что-то вроде «Если вам абсолютно необходимо оптимизировать, вы должны использовать профилировщик, чтобы определить, где требуется оптимизация». Вы вкладываете свои усилия не в ту область.