2014-08-29 3 views
1

Y a-t-il un moyen pour un JComponent d'obtenir une notification des modifications ajoutées/supprimées dans sa hiérarchie de composants descendants?Écoute des modifications de la hiérarchie des composants descendants

Par exemple, dans le code ci-dessous, il y a un bouton addChild qui ajoutera un nouvel enfant JPanel à la racine ou au dernier panneau ajouté. Je voudrais que le panneau racine reçoive une notification de ceci. Un peu comme un HierarchyListener mais l'inverse ou un ContainerListener qui a écouté pour plus que les enfants immédiats.

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

Je pense que vous pouvez contourner ce problème assez facilement. Pourquoi voudriez-vous même écouter des événements qui n'affectent pas un composant particulier? Vous pourriez, par exemple, réutiliser un ContainerListener pour tous les conteneurs à partir desquels vous souhaitez recevoir des notifications. L'ajout d'un enfant à l'enfant d'un composant ne l'affecte pas techniquement (sauf si vous faites un ajustement au premier enfant). C'est une autre approche: utilisez les composants dans le "milieu" de la hiérarchie pour notifier vos composants de niveau supérieur. – J4v4

Répondre

1

Une solution possible serait d'utiliser un ContainerListener unique qui s'ajoute aux conteneurs qui sont ajoutés au conteneur « racine » (et, juste pour être complet, se retire aussi des conteneurs qui sont supprimés, pour le cas que cela soit nécessaire).

Vous pouvez donc créer un ContainerListener et l'ajouter au conteneur racine. Chaque fois que cet écouteur est averti qu'un nouveau composant enfant est ajouté, il s'ajoute à ce composant (s'il s'agit d'un conteneur). Plus tard, lorsqu'un comonent est ajouté au composant enfant, l'écouteur en sera informé et pourra s'ajouter à nouveau au petit-enfant, et ainsi de suite.

J'esquissés ici dans un exemple:

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

Merci Marco, c'est plus ou moins ce que j'ai imaginé aussi. J'ai également besoin d'ajouter du code qui a traversé les composants descendants quand componentAdded/componentRemoved a été appelé en ajoutant/supprimant containerListener à ces composants aussi, bien que ce n'était pas strictement nécessaire dans mon exemple. Merci d'avoir répondu. – Jamesy82

Questions connexes