2009-02-16 5 views
6

Supposons que vous avez le haricot java suivant:Surveiller les changements à une collection

public class MyBean 
{ 
    private List<String> names = new ArrayList<String>(); 

    public void addName(String name) 
    { 
     names.add(name); 
     fireNamesPropertyChange(name); 
    } 
} 

Comment décririez-vous mettre en œuvre normalement un événement de changement de propriété pour une collection? Essayez-vous d'utiliser la propriété index qui semble être plus pour les tableaux que pour les collections?

Répondre

8

Jetez un oeil à la bibliothèque Glazed Lists, qui prend en charge les collections observables.

Si avec elementsAdded, méthodes elementsRemoved ou :-) similaire (également en fonction de mes besoins)

+0

C'est ce que je fais normalement aussi bien. Juste curieux de savoir ce que tout le monde faisait. Au moins, j'ai un joli lien vers Collection qui sont en fait oberserable. :) – willcodejavaforfood

+1

La réponse de AndreyChaschev n'a pas besoin de bibliothèque externe (mais nécessite Java7) – Benj

+0

@Benj: pas de surprise que la réponse d'il y a 8 ans n'est plus pertinente ;-) –

0

Pour un événement GUI swing j'utiliserais normalement un EventListenerList pour faire le travail pour moi.

EDIT: sur la reformulation des questions: comment traiter les collections, j'utilise généralement un événement similaire au type de collection, par exemple un événement TreeModel prend généralement un argument TreePath, ou quelque chose dans une map Je voudrais indiquer la clé.

Cependant, pour les JavaBeans simples, le plus commun est de supposer un list/array et d'utiliser simplement l'index.

+0

Je ne suis pas intéressé par la façon dont les auditeurs sont stockés. Ce que je cherche est une alternative à PropertyChange qui peut gérer les collections. – willcodejavaforfood

+0

et notez que ce n'est pas spécifique à Swing – willcodejavaforfood

0

Il me semble que vous aurez besoin de fireNamesPropertyAdd, fireNamesProperyDelete. Une notification de niveau de liste ne fonctionnera pas à mon humble avis, même si c'était un tableau et un index a été ajouté car il ne peut pas gérer les suppressions. Si l'élément d'un index peut être modifié, vous aurez également besoin de fireNamesProperyChange. Il peut être utile d'avoir un index en paramètre en plus de la valeur de la chaîne.

8

(NOTE: Je mis à jour ce poste après avoir réalisé quelques erreurs de ma propre si ce n'est pas l'original mais un plus raffiné à la place)

A cet effet, je ferais deux nouvelles interfaces, ListListener et Listenable puis je voudrais créer une nouvelle classe comme ListenableArrayList qui encapsulerait chaque List méthode avec un appel à une (ou plusieurs) des méthodes pertinentes définies dans ListListener. Dans le code, il serait quelque chose comme ceci:

public class ListenableArrayList<T> extends ArrayList<T> 
            implements Listenable<T> { 

    private ArrayList<T> internalList; 
    private ListListener<T> listener; 

    /* .. */ 

    public void add(T item) { 
     listener.beforeAdd(T item); 
     internalList.add(item); 
     listener.afterAdd(T item); 
    } 

    /* .. */ 

    public void setListener(ListListener<T> listener) { 
     this.listener = listener; 
    } 

} 

public interface ListListener<T> { 
    /* .. */ 
    void beforeAdd(T item); 
    void afterAdd(T item); 
    /* .. */ 
} 

public interface Listenable<T> { 
    /* .. */ 
    void setListener(ListListener<T> listener); 
    /* .. */ 
} 

La raison pour laquelle je le ferais de cette façon serait de permettre la création d'auditeurs vraiment ad hoc à la volée au lieu de lier le ListenableArrayList à une mise en œuvre spécifique. Par exemple, avec ce qui suit serait possible:

Listenable<String> list = new ListenableArrayList<String>(); 

list.setListener(new ListListener<String>() { 
    @Override 
    public void beforeAdd(String item) { 
     System.out.println("About to add element "+item+"..."); 
    } 
    @Override 
    public void afterAdd(String item) { 
     System.out.println("...element "+item+" has been added."); 
    } 
}); 

Un peu encombrés, peut-être, mais d'autre part, cela permettrait l'extension facile aux collections, jeux et autres joyeusetés assez facilement.

+0

... vous pouvez bien sûr appeler directement les méthodes d'écoute aussi, j'essaie juste d'être aussi clair que possible avec l'exemple. – Esko

+0

Liste list = new ListenableArrayList (); (Liste (écoutable)) .setListener (new ListListener() { ? Icky! –

+0

Effrayant c'est la seule chose dont vous vous plaignez puisqu'il y a un autre problème plus important que j'ai remarqué. – Esko

0

Vous cherchez peut-être pour java.beans.PropertyChangeSupport je devais me le faire, je créerais probablement interface Listener personnalisée? À mon avis, vous devriez éviter PropertyChangeEvent. IndexedPropertyChangeEvent est pire, et très rarement utilisé par Swing de toute façon. Il est préférable de restreindre la mise au point de vos types, et de déclencher un javax.swing.event.ChangeEvent ou similaire (même simplement appeler un Runnable).

Pour certains types (comme les listes!), Les listes glacées (ou équivalentes) mentionnées dans un autre article de Peter Štibraný semblent être un bon choix.

+0

Je suis à la recherche d'une alternative :) – willcodejavaforfood

2

Normalement, je ferais ce qui suit:

public class MyBean { 
    private PropertyChangeSupport pcs = new PropertyChangeSupport(this); 
    private List<String> names = new ArrayList<String>(); 
    public void addName(String name) { 
     names.add(name); 
     pcs.firePropertyChange("names", null, Collections.unmodifiableList(names)); 
    } 
    public void addPropertyChangeListener(PropertyChangeListener l) { 
     pcs.addPropertyChangeListener(l); 
    } 
    public void removePropertyChangeListener(PropertyChangeListener l) { 
     pcs.removePropertyChangeListener(l); 
    } 
} 

PropertyChangeSupport gère les auditeurs et déclenche les événements en votre nom. En passant null comme "ancienne valeur", force l'événement à être déclenché. (Il est probable que les auditeurs ne se soucient vraiment de l'ancienne valeur de toute façon)

+0

Je suis tellement obsédé par les propriétés indexées que je n'ai même pas pensé à ça. Cela fonctionne bien quand vous ne vous souciez pas vraiment d'un objet a été ajouté/supprimé juste que quelque chose a changé. – willcodejavaforfood

+0

@willcodejavaforfood Ceci est un million d'années plus tard, mais si vous vous souciez de savoir si un objet a été ajouté ou supprimé, vous pouvez changer l'identifiant de 'PropertyChangeEvent'. – sdasdadas

+2

@sdasdadas - whoa - ne fais pas ça! Le nom dans le PropertyChangeEvent _must_ correspond au nom de la propriété réelle (le même que le nom de la méthode get/set - le "get"/"set" et avec une première lettre minuscule.) Ne jamais le mettre à quelque chose comme "nom -added "ou" name-removed ". Sinon, vous cassez un framework ou un humain qui comprend comment fonctionne JavaBeans ... –

1

solution 7+ JDK:

import javafx.collections.*; 
import java.util.*; 

public class Test { 
    public static void main(String[] args) { 
     List<String> list = new ArrayList<>(); 

     list.add("s1"); 
     list.add("s2"); 

     ObservableList<String> observableList = FXCollections.observableList(list); 
     observableList.addListener(new ListChangeListener<String>() { 
      @Override 
      public void onChanged(Change<? extends String> change) { 
       while(change.next()){ 
        System.out.println("added: " + change.getAddedSubList()); 
       } 
      } 
     }); 

     observableList.add("s3"); 
    } 
} 
+1

c'est la solution javafx – dit

+0

qu'est-ce qui ne va pas avec ça? Il est isolé de la partie UI –

Questions connexes