[java] JList listener

Pagina: 1
Acties:

  • Zynth
  • Registratie: September 2001
  • Laatst online: 20-05 19:47
Hoe kan ik iets wat lijkt op een itemlistener aan een Jlist hangen?
ik wil namelijk graag iets laten uitvoeren zodra iemand opeen item in de lijst klikt.

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

addListSelectionListener(ListSelectionListener listener)

zie:
http://java.sun.com/j2se/1.4/docs/api/javax/swing/JList.html

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 20-05 22:22
Ik vind dat een JList een groot gebrek heeft: je kan geen ActionCommand meegeven, zoals bij een JButton, JComboBox e.d.

Stel dat je het MVC architectural pattern toepast:

In dezelfde view heb je twee afzonderlijke JList's opgenomen.
Van allebei de JLists wil je selection events afvangen, dit wordt door de view's controller geregeld: een afzonderlijke klasse.
Hoe kan je in de controller klasse nu bepalen van welke JList het selection event afkomstig is? De getSource() methode kan je niet gebruiken, omdat de JList componenten uit de view niet bij de controller bekend zijn en de methodes setActionCommand / getActionCommand zijn niet beschikbaar.

Er is vast een oplossing voor, maar ik begrijp niet waarom de methodes setActionCommand / getActionCommand niet beschikbaar zijn voor het JList component.

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 20-05 22:22
Ben zelf nog even bezig geweest.

Ik heb nu voor ieder GUI component in de view een eigen listener gedefinieerd in de controller class en in de view class voor ieder component een public methode gedefinieerd die een listener aan dat specifieke component hangt. Deze methodes worden vanuit de controller wordt aangeroepen.

Werkt, maar mag dat zo?

[ Voor 13% gewijzigd door Kwistnix op 10-10-2004 18:07 ]


  • Macros
  • Registratie: Februari 2000
  • Laatst online: 30-04 09:28

Macros

I'm watching...

Ik zie je probleem niet echt. Ik geef namelijk altijd elke List en elke button meestal zijn eigen listener. Alleen als 2 listeners bijna hetzelfde zijn generaliseer ik die listener en koppel hem aan beide knoppen.

Voorbeeldje van een generalisatie:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CheckDrawableAction implements ActionListener{
    private Drawable drawable;
    private AbstractButton checkBox;
    
    public CheckDrawableAction(Drawable draw, AbstractButton checkBox){
        assert draw != null && checkBox == null;
        drawable = draw;
        this.checkBox = checkBox;
    }
    public void actionPerformed(ActionEvent e) {
        boolean selected = checkBox.isSelected();
        drawable.setVisible(selected);
    }
}

Maar meestal heeft elke knop zijn eigen listener. Vaak anonymous als ze niet erg lang zijn.

"Beauty is the ultimate defence against complexity." David Gelernter


  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 20-05 22:22
Het probleem was dat ik de controller klasse de ListSelectionListener liet implementeren. Vervolgens gebruikte ik de klasse controller dus als listener voor beide JLists. Dan zit je dus met één valueChanged() methode en moet je dus op één of andere manier bepalen van welke JList het event afkomstig is.
Voor Buttons e.d. kan dat, wel omdat je dan een ActionCommand kan specificeren waarop je kan filteren. De JList klasse biedt die mogelijkheid niet.

De bovenstaande manier is echter in het geheel niet handig en ik maak nu dus ook gebruik van anonymous listeners. Dat werkt veel handiger. Ik wist alleen niet dat zoiets kon. Ik heb me eigenlijk nog nooit verdiept in het hele EventListener verhaal, omdat vanuit m'n opleiding aangeleerd heb om alles in één (bijvoorbeeld) actionPerformed methode te pleuren. Heel erg fout dus. Pas nu gaan we verschillende patterns leren en daarbij komt dus ook het EventListener verhaal om de hoek kijken. Hier besteden ze vreemd genoeg geen aandacht aan. Dus dat moet je zelf maar uitzoeken.

[ Voor 12% gewijzigd door Kwistnix op 10-10-2004 18:50 ]


  • Macros
  • Registratie: Februari 2000
  • Laatst online: 30-04 09:28

Macros

I'm watching...

Je ziet heel erg vaak dat alle listeners door de classe die alles bevat zelf wordt geimplementeerd. Volgens mij stamt dat uit de Java 1.1 tihd toen er nog geen anonymous en inner classes waren.
Erg fout zou ik het niet noemen. In plaats van de ActionCommands te gebruiken kan je natuurlijk ook de getSource() gebruiken om te achterhalen wat de source was. ActionCommand vind ik zelf niet erg handig omdat meerdere objecten dezelfde ActionCommands kan hebben en dat het default de namen van de buttons zijn, wat nogal wierd kan zijn. Vooral bij localisatie van applicaties.

"Beauty is the ultimate defence against complexity." David Gelernter


  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 20-05 22:22
Macros schreef op 10 oktober 2004 @ 19:03:
Je ziet heel erg vaak dat alle listeners door de classe die alles bevat zelf wordt geimplementeerd. Volgens mij stamt dat uit de Java 1.1 tihd toen er nog geen anonymous en inner classes waren.
Erg fout zou ik het niet noemen. In plaats van de ActionCommands te gebruiken kan je natuurlijk ook de getSource() gebruiken om te achterhalen wat de source was. ActionCommand vind ik zelf niet erg handig omdat meerdere objecten dezelfde ActionCommands kan hebben en dat het default de namen van de buttons zijn, wat nogal wierd kan zijn. Vooral bij localisatie van applicaties.
Da's een kwestie van je ActionCommand goed definiëren (ik gebruikte een aparte klasse Constants om alle ActionCommands bij te houden).
Het probleem met getSource() is dat de controller klasse in mijn geval de Listener was en dat alle componenten in een aparte klasse (de view) waren opgenomen.
Dan heb je niet veel aan de getSource() methode.

[ Voor 3% gewijzigd door Kwistnix op 10-10-2004 23:12 ]


  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 20-05 22:22
Voorbeeldje van hoe de events nu afgehandeld worden:

View:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class View implements Observer{

  private Model model;
  private Controller controller;

  public View(Model model){
    this.model = model;
    this.model.attachObserver(this);
    controller = new Controller(this.model, this);
    setupUI();
  }

  private void setupUI{
    JButton knop1 = new JButyon("Knop 1");
    // layout gedoe
    knop1.addActionListener(controller.getKnop1Listener);
 
    JButton knop2 = new JButyon("Knop 2");
    // layout gedoe
    knop2.addActionListener(controller.getKnop2Listener);
  }

  // rest van de methodes...

}



Controller:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Controller implements Observer{

  private Model model;
  private View view;
  
  private ActionListener knop1Listener;
  private ActionListener knop2Listener;

  public MessagePaneController(Model model, View view){
    this.model = model;
    this.model.attachObserver(this);
    this.view = view;
  }

  public ActionListener getKnop1Listener(){
    knop1Listener = new ActionListener(){
      public void actionPerformed(ActionEvent e){
         // doe iets!
      }
    };
    return knop1Listener;
  }

  public ActionListener getKnop2Listener(){
    knop2Listener = new ActionListener(){
      public void actionPerformed(ActionEvent e){
         // doe iets anders!
      }
    };
    return knop2Listener;
  }

  // rest van de methodes...
}


Is dit zo netjes? Ik ben niet helemaal zeker van die getter methodes, maar ik zou niet weten hoe ik het anders moet oplossen.

Verwijderd

Tis op zich een nette implementatie alleen zou ik de listeners niet elke keer opnieuw instantieren.

Misschien iets in de vorm van:
Java:
1
2
3
4
5
6
7
8
9
10
ActionListener knop1Listener = null;

public ActionListener getKnop1Listener() {
    if(knop1Listener == null) {
        knop1Listener = new actionListener() {
            //je actionperformed
        }
    }
    return knop1Listener
}


Je zult er nu nog wel geen last van hebben, maar als je eenmaal doorhebt dat je herhalende taken uitvoert in verschillende actionListeners, ga/kun je dat misschien weer generaliseren. En dan is bovenstaande constructie handig om ervoor te zorgen dat er slechts 1 instantie beschikbaar is.

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 20-05 22:22
Ok :)
Nog een vraagje.
Volgens het MVC-model dat wij moeten gebruiken heeft iedere view zijn eigen controller. De controller is in dat geval ook de Listener voor alle componenten binnen die view. Dat lijkt mij niet echt handig of wel? Ik zou op basis van jullie informatie zeggen dan ieder component binnen een view een eigen listener heeft (gegeneraliseerd of niet).

Dat kan je dus oplossen door per view één controller klasse te maken en binnen die klasse voor ieder component een eigen listener te definiëren, zoals ik in de code hierboven heb beschreven.

In plaats van iedere listener in de controller klasse te definiëren zou je ook aparte klassen aan kunnen maken voor iedere listener. Dan heb je niet één controller per view, maar meerdere controllers per view. Dit laatste lijkt mij veel overzichtelijker en dan is generalisatie volgens mij ook veel makkelijker toe te passen, of niet?

Voor de duidelijkheid, ik zie de view in dit geval als een verzameling componenten op bijvoorbeeld een JPanel. Je zou ook kunnen zeggen dat ieder component op zich een view is, maar dat gaat wel erg ver vind ik.

[ Voor 29% gewijzigd door Kwistnix op 11-10-2004 09:51 ]

Pagina: 1