13 listopada
11:22 2008
Przed:
...
showClose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JCheckBox source = (JCheckBox)e.getSource();
closeButton.setVisible(source.isSelected());
}
} );
...
Po:
... showClose.onSelect(closeButton, "visible"); ...
Jak widać, druga wersja jest znacznie krótsza :) Cała tajemnica tkwi w klasie EventHandler, która tworzy za nas odpowiednie zdarzenia oraz ustawia odpowiednie właściwości obiektu. Prosta implementacja klasy MyCheckBox oraz MyButton wygląda tak:
MyCheckBox
public class MyCheckBox extends javax.swing.JCheckBox {
public MyCheckBox(String text) {
super(text);
}
public void onSelect(Object target, String propertyName) {
addActionListener(java.beans.EventHandler.create(
java.awt.event.ActionListener.class,
target,
propertyName,
"source.selected"
));
}
}
Uwaga: "propertyName" musi być nazwą właściwości obiektu "target", zgodną ze specyfikacją JavaBeans™.
MyButton
public class MyButton extends javax.swing.JButton {
public MyButton(String text) {
super(text);
}
public void onClick(Object target, String action) {
addActionListener(java.beans.EventHandler.create(
java.awt.event.ActionListener.class,
target,
action
));
}
}
Uwaga: "action" musi być nazwą publicznej metody obiektu "target".
Program testowy
public class Main extends javax.swing.JFrame {
Main() {
super("Test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
MyButton closeButton = new MyButton("Close");
// po kliknięciu, okno zostanie zamknięte
// ( odpowienik wywołania this.dispose() )
closeButton.onClick(this, "dispose");
add(closeButton);
MyCheckBox showClose = new MyCheckBox("Show Close Button");
showClose.setSelected(true);
// po kliknięciu, przycisk "Close" zostanie ukryty/pokazany
// ( odpowiednik closeButton.setVisible(showClose.isSelected()) )
showClose.onSelect(closeButton, "visible");
add(showClose, java.awt.BorderLayout.NORTH);
MyCheckBox allEnabled = new MyCheckBox("Enabled");
allEnabled.setSelected(true);
allEnabled.onSelect(closeButton, "enabled");
allEnabled.onSelect(showClose, "enabled");
add(allEnabled, java.awt.BorderLayout.SOUTH);
pack();
setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() { new Main(); }
} );
}
}
Proszę o komentarz
Fajne, trochę jak sloty w QT.
Jest jakieś IDE, które generuje coś takiego?
Powyżej, to taka ironia trochę była. Nie widzę sensu, aby tworzyć własną nadbudówkę każdego komponentu swinga, tylko po to, żeby zaoszczędzić na tworzeniu listenerów.
Ale pomysł byłby ciekawy, gdyby go SUN zaimplementował standardowo.
@nullpointer: nadbudówki są fajne, bo niektóre koponenty Swinga mocno obsysają, a nie będe przecież czekał 10 lat, aż Sun coś poprawi w API. Mogłoby być w standardzie, ale na razie bardzo wątpie w to, czy kiedyś tak się stanie ;)
Wynik kolejnych eksperymentów :) Załóżmy, że mamy komponent JList z elementami A, B, C, i chcemy, żeby pole tekstowe JTextField miało zawsze zawartość wybranego elementu z listy:
JTextField text = new JTextField(); MyList textList = new MyList("A", "B", "C"); textList.bind(text, "text"); // <--public class MyList extends javax.swing.JList { public MyList(Object... items) { super(items); } public void bind(Object target, String propertyName) { addListSelectionListener(java.beans.EventHandler.create( javax.swing.event.ListSelectionListener.class, target, propertyName, "source.selectedValue" )); } }Nadbudówki są fajne, to fakt. Niestety ciężko tą fajność wykorzystać w praktyce przy dużym projekcie (a z takimi mam do czynienia). Jeśli masz np. aplikację składającą się z 1000 formularzy i robisz ją w JDeveloperze i ADFie, to powyższe rozwiązanie więcej Ci nabruździ, niż pomoże. Utworzenie odpowiedniego listenera w tym środowisku to dwa kliknięcia + linijka kodu. Zmuszenie go do używania „fajnej nadbudówki” jest albo niemożliwe, albo katorżniczo trudne.
Niemniej, przy czysto akademickim podejściu – pomysł jest ciekawy (powtarzam się, ech ;-))
Jak poprzednik uważam, ze pomysł jest ciekawy, aczkolwiek mało wydajny w praktyce.
Yy, nie mógłbyś sobie zamiast tego utworzyć fabryki generującej i zwracającej takiego listenera, tak żebyś nie musiał nadpisywać każdej Swingowej kontrolki? Pisania sumarycznie mniej ;)
@sztywny: mógłbym, ale nic nie wygląda tak pięknie jak foo.onClick ;)