2014-08-29 4 views
1

Есть ли способ для JComponent получить уведомление о добавлении/удалении изменений в иерархии его потомков?Прослушивание изменений иерархии компонентов потомков

Например, в коде ниже есть кнопка addChild, которая добавит нового ребенка JPanel к корню или к последней добавленной панели. Я хотел бы, чтобы корневая панель получила уведомление об этом. Похоже на HierarchyListener, но наоборот, или ContainerListener, который слушал больше, чем просто детей.

public class DecendantHierarchyListening extends JFrame { 

    private final JPanel root = createChildPanel(null); 

    public JPanel leafComponent = root; 

    public DecendantHierarchyListening() { 
     super("title"); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     JComponent buttons = new JPanel(); 

     JPanel panel = new JPanel(new BorderLayout()); 
     panel.add(createAddChildButton(buttons), BorderLayout.NORTH); 
     panel.add(root, BorderLayout.CENTER); 

     getContentPane().add(panel); 
    } 

    private Button createAddChildButton(JComponent buttons) { 
     Button button = new Button("AddChild"); 
     buttons.add(button); 
     button.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       leafComponent = (JPanel) leafComponent 
         .add(createChildPanel(leafComponent)); 
       DecendantHierarchyListening.this.invalidate(); 
       DecendantHierarchyListening.this.validate(); 
       DecendantHierarchyListening.this.repaint(); 
      } 
     }); 
     return button; 
    } 

    public static JPanel createChildPanel(Container parent) { 
     Color[] colors = new Color[] { Color.RED, Color.BLUE, Color.GREEN }; 
     JPanel panel = new JPanel(new BorderLayout()); 
     panel.setPreferredSize(new Dimension(200, 200)); 
     Color color; 
     if (parent == null) { 
      color = Color.GREEN; 
     } else { 
      int distance = 1; 
      parent = parent.getParent(); 
      while (parent != null) { 
       distance++; 
       parent = parent.getParent(); 
      } 

      color = colors[distance % colors.length]; 
     } 

     panel.setBorder(BorderFactory.createLineBorder(color, 2)); 
     return panel; 
    } 

    public static void runDemo() { 
     JFrame f = new DecendantHeirarchyListening(); 
     f.pack(); 
     f.setVisible(true); 
    } 

    public static void main(String[] args) { 
     DecendantHierarchyListening.runDemo(); 
    } 
} 
+0

Я думаю, что вы можете работать вокруг этого довольно легко. Почему вы даже хотите слушать события, которые не влияют на конкретный компонент? Например, вы можете использовать один контейнер ContainerListener для всех контейнеров, из которых вы хотите получать уведомления. Добавление ребенка к дочернему компоненту компонента технически не влияет на него (если вы не сделаете корректировку для первого ребенка). Это другой подход: используйте компоненты в «середине» иерархии, чтобы уведомлять компоненты верхнего уровня. – J4v4

ответ

1

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

Таким образом, вы в принципе можете создать ContainerListener и добавить его в корневой контейнер. Всякий раз, когда этот слушатель уведомляется о добавлении нового дочернего компонента, он добавляет себя и этому компоненту (если это контейнер). Позже, когда к дочернему компоненту добавляется comonent, слушатель будет проинформирован и может снова добавить себя к внуку и так далее.

я набросал здесь в качестве примера:

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.Container; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ContainerEvent; 
import java.awt.event.ContainerListener; 

import javax.swing.BorderFactory; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 

public class DescendantHierarchyListening extends JPanel 
{ 
    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       createAndShowGUI(); 
      } 
     }); 
    } 

    public static void createAndShowGUI() 
    { 
     JFrame f = new JFrame(); 
     f.add(new DescendantHierarchyListening()); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setSize(400, 400); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 

    ContainerListener containerListener = new ContainerListener() 
    { 
     @Override 
     public void componentAdded(ContainerEvent e) 
     { 
      Component child = e.getChild(); 
      System.out.println("Added " + child); 
      System.out.println(" to " + e.getContainer()); 

      if (child instanceof Container) 
      { 
       Container container = (Container)child; 
       container.addContainerListener(this); 
      } 
     } 

     @Override 
     public void componentRemoved(ContainerEvent e) 
     { 
      Component child = e.getChild(); 
      System.out.println("Removed " + child); 
      System.out.println(" from " + e.getContainer()); 
      if (child instanceof Container) 
      { 
       Container container = (Container)child; 
       container.removeContainerListener(this); 
      } 
     } 
    }; 

    private final JPanel root; 
    public JPanel leafComponent; 

    public DescendantHierarchyListening() 
    { 
     super(new BorderLayout()); 

     JButton button = createAddChildButton(); 
     add(button, BorderLayout.NORTH); 

     root = createChildPanel(null); 
     root.addContainerListener(containerListener); 

     add(root, BorderLayout.CENTER); 
     leafComponent = root; 
    } 

    private JButton createAddChildButton() 
    { 
     JButton button = new JButton("AddChild"); 
     button.addActionListener(new ActionListener() 
     { 
      @Override 
      public void actionPerformed(ActionEvent e) 
      { 
       JPanel child = createChildPanel(leafComponent); 
       leafComponent.add(child); 
       leafComponent = child; 
       revalidate(); 
      } 
     }); 
     return button; 
    } 

    public JPanel createChildPanel(final Container parent) 
    { 
     JPanel panel = new JPanel(new BorderLayout()) 
     { 
      @Override 
      public String toString() 
      { 
       return "Child of " + parent; 
      } 
     }; 
     Color color = getColor(parent); 
     panel.setBorder(BorderFactory.createLineBorder(color, 2)); 
     return panel; 
    } 

    private static Color getColor(Component c) 
    { 
     if (c == null) 
     { 
      return Color.GREEN; 
     } 
     Color[] colors = new Color[] 
     { Color.RED, Color.BLUE, Color.GREEN }; 
     int d = getDepth(c); 
     return colors[d % colors.length]; 
    } 

    private static int getDepth(Component c) 
    { 
     if (c == null) 
     { 
      return 0; 
     } 
     return 1 + getDepth(c.getParent()); 
    } 

} 
+0

Спасибо Марко, это более или менее то, что я придумал. Кроме того, мне нужно было добавить код, который пересекал потоковые компоненты, когда компонентAdded/componentRemoved назывался добавлением/удалением containerListener для этих компонентов, хотя это не было строго необходимо в моем примере. Спасибо за ответ. – Jamesy82