2009-03-29 8 views
2

J'ai écrit une classe MnemonicsBuilder pour JLabels et AbstractButtons. Je voudrais écrire une méthode pratique setMnemonics(JFrame f) qui parcourra chaque enfant du JFrame et sélectionnera les JLabels et les AbstractButtons. Comment puis-je avoir accès à tout ce qui est contenu dans le JFrame? J'ai essayé:Itérer/recurcir à travers des conteneurs et des composants pour trouver des objets d'une classe donnée?

LinkedList<JLabel> harvestJLabels(Container c, LinkedList<JLabel> l) { 
    Component[] components = c.getComponents(); 
    for(Component com : components) 
    { 
     if(com instanceof JLabel) 
     { 
      l.add((JLabel) com); 
     } else if(com instanceof Container) 
     { 
      l.addAll(harvestJLabels((Container) com, l)); 
     } 
    } 
    return l; 
} 

Dans certaines situations, cela fonctionne très bien. Dans d'autres, il manque de mémoire. A quoi je ne pense pas? Existe-t-il un meilleur moyen de rechercher des composants enfants? Ma récursion est-elle erronée? N'est-ce pas une image de la façon dont les choses "contiennent" d'autres choses dans Swing - par exemple, Swing n'est-il pas un arbre enraciné?

JFrame 
| 
|\__JMenuBar 
| | 
| \__JMenu 
|  | 
|  \__JMenuItem 
| 
|\__JPanel 
| | 
| |\__JButton 
| | 
| |\__JLabel 
| | 
| |\__ ... JCheckBoxes, other AbstractButtons, etc. 
+1

+1 pour le bel arbre ASCII – justSaid

Répondre

5

D'accord avec Tom ici ... Votre problème est que vous êtes déjà passer le List d'ajouter les JLabel s jusqu'à votre méthode récursive et vous retournez également - ajoutant ainsi les mêmes éléments à votre liste plus une fois que. En termes plus politiquement corrects - le List est votre accumulateur.

Votre méthode devrait plutôt ressembler à ceci:

public void harvestJLabels(Container c, List<JLabel> l) { 
    Component[] components = c.getComponents(); 
    for(Component com : components) { 
     if(com instanceof JLabel) { 
      l.add((JLabel) com); 
     } else if(com instanceof Container) { 
      harvestJLabels((Container) com, l)); 
     } 
    } 
} 

Ensuite, vous pouvez avoir une méthode d'aide pour initier cette récolte:

public List<JLabel> harvestJLabels(Container c) { 
    List<JLabel> jLabels = new ArrayList<JLabel>(); 
    harvestJLabels(c, jLabels); 
    return jLabels; 
} 
0

Et si deux composants se trouvaient l'un dans l'autre dans la collection de composants? Il serait infiniment recurse à travers eux, en ajoutant à votre collection.

Vous pourriez avoir une référence circulaire quelque part, qui peut ne pas être aussi évidente ou simple que ce que j'ai décrit. Je ne suis pas familier avec JFrame, donc je ne suis pas sûr que ce soit possible. Lorsque vous faites ce genre de chose, vous pourriez avoir besoin d'une propriété "visitée" de sorte que vous puissiez marquer les objets comme visités, et si c'est le cas, ne faites pas l'appel récursif sur eux.

0

Vous pouvez essayer de supprimer la récursivité pour libérer les tableaux de composants:

LinkedList<JLabel> harvestJLabels(Container c, LinkedList<JLabel> l) { 
    List<Container> containers = new ArrayList<Container(); 
    containers.add(c); 
    while (!containers.isEmpty()) { 
     Container cont = containers.remove(0); 
     Component[] components = cont.getComponents(); 
     for(Component com : components) 
     { 
      if(com instanceof JLabel) 
      { 
        l.add((JLabel) com); 
      } else if(com instanceof Container) 
      { 
       containers.add((Container)com); 
      } 
     } 
    } 
    return l; 
} 
1

Voici votre problème:

LinkedList<JLabel> harvestJLabels(Container c, LinkedList<JLabel> l) { 
    ... 
       l.addAll(harvestJLabels((Container) com, l)); 
    ... 
    return l; 
} 

Vous avez seulement une seule liste. Vous ajoutez une liste à une autre liste. Par conséquent, vous ajoutez une liste à lui-même. Cela peut fonctionner dans un certain sens, mais vous allez doubler la longueur (croissance exponentielle).

Vous pouvez utiliser un seul List (il n'est généralement pas nécessaire de spécifier un algorithme dans les déclarations) ou créer une nouvelle instance de liste chaque fois que la méthode est appelée. Évitez de renvoyer une référence dont vous n'avez pas besoin - c'est juste trompeur.

également ArrayList serait plus approprié que LinkedList. LinkedList est presque toujours le mauvais choix.

0

Une autre méthode utilitaire pour se tenir debout sur les épaules des autres, comme @Maurice et @Cem:

public <T extends Component> List<T> harvestMatches(Container root, Class<T> clazz) { 
    List<Container> containers = new LinkedList<>(); 
    List<T> harvested = new ArrayList<>(); 

    containers.add(root); 
    while (!containers.isEmpty()) { 
     Container container = containers.remove(0); 
     for (Component component : container.getComponents()) { 
      if (clazz.isAssignableFrom(component.getClass())) { 
       harvested.add((T) component); 
      } else if (component instanceof Container) { 
       containers.add((Container) component); 
      } 
     } 
    } 

    return Collections.unmodifiableList(harvested); 
} 

et l'appeler avec quelque chose comme:

List<AbstractButton> buttons = harvestMatches(root, AbstractButton.class); 

ou

List<JLabel> labels = harvestMatches(root, JLabel.class); 

Bien que je aime aussi l'approche Swing-Fest en utilisant org.fest.swing.core.GenericTypeMatcher

Questions connexes