Niepewności dotyczące implementacji akcji i użycia jednego modelu z wieloma widokami
Jestem całkowitym nowicjuszem w zakresie programowania GUI i może mój problem ma całkiem proste rozwiązanie. Usiłuję zaimplementować interfejs GUI Java Swing, który służy jako edytor dla struktury danych typu drzewa. Interfejs GUI jest podzielony na trzy części:
Przeglądarka drzew w lewej ćwiartce okna wyświetla dane o strukturze drzewa.
Duży górny prawy obszar wyświetla edytory zawierające pola tekstowe, tabele i tym podobne. Każdy inny rodzaj obiektu w strukturze drzewa ma swój własny edytor, który jest wyświetlany po wybraniu go w przeglądarce drzewa.
W prawym dolnym obszarze wyświetlana jest przeglądarka konsoli. Służy do wyświetlania komunikatów o konkretnych działaniach.
Staram się ściśle przestrzegać ścisłego oddzielenia modelu od jego wizualizacji w przeglądarkach / edytorach drzew w moim programie. Dlatego stworzyłem instancję podklasy DefaultTreeModel (MyTreeModel), która przechowuje odwołania do danych biznesowych i instancję podklasy JTree, która zapewnia wizualną reprezentację struktury drzewa.
Próbuję zaimplementować funkcjonalność, która modyfikuje dane za pomocą klas Action. Każda akcja (jak CreateNode, RenameNode, DeleteNode) jest zaimplementowana w swojej własnej klasie akcji (podklasa AbstractAction). Akcje są używane w menu kontekstowym przeglądarki drzewa oraz w menu „Edycja” aplikacji. Ale chcę też ponownie wykorzystać niektóre z nich w częściach edytora GUI, np. akcja RenameNode. I tutaj utknąłem.
Przeglądarka drzewa wyświetla ikonę wraz z nazwą dla każdego węzła w drzewie. Odpowiedni edytor zawiera między innymi także pole JTextField, które pokazuje nazwę powiązanego węzła.
Wiem, że mogę dołączyć obiekty akcji do JMenu, JPopupMenu, a nawet do obiektów JTextField za pomocą metody setAction. Zrobiłem tak, a teraz mam wpis menue „Zmień nazwę” w menu „Edycja” programu, w menu podręcznym skojarzonym z JTree, które reprezentuje przeglądarkę drzewa, a pole JTextField pokazujące nazwę węzła ma również tę akcję.
Aby zmienić atrybut „Nazwa” węzła drzewa, utworzyłem klasę RenameNode jako podklasę AbstractAction. Jak już wspomniano, nazwa jest wyświetlana w każdym węźle w przeglądarce drzewa, a akcja po prostu czyni ten tekst edytowalnym. Ten kod wygląda następująco (w klasie RenameNode):
public void actionPerformed(ActionEvent ev) {
// "mouseOverPath" is the Treepath were the mouse was placed on
// when the popup menu was opened
if (tree.existsMouseOverPath()) {
tree.startEditingAtPath(tree.mouseOverPath);
} else if (tree.getSelectionCount() != 0) {
tree.startEditingAtPath(tree.getSelectionPath());
}
}
Jeśli instrukcje są potrzebne do poprawnego działania z:
- menu podręczne (pierwsze jeśli instrukcja; tutaj obiekt znajdujący się pod myszą po otwarciu menu podręcznego jest edytowalny)
- menu aplikacji (drugie jeśli instrukcja; tutaj węzeł drzewa, który jest aktualnie wybrany - jeśli w ogóle - jest edytowalny).
Cóż, to działa dobrze, w zasadzie, ale w rzeczywistości zmiana nazwy węzła nie odbywa się za pomocą kodu w metodzie actionPerformed (ActionEvent ev) klasy RenameAction. Prawdziwa zmiana nazwy węzła jest wykonywana w klasie drzewa MyTreeModel w metodzie valueForPathChanged (), która jest zastępowana w następujący sposób:
public class MyTreeModel extends DefaultTreeModel {
[...]
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
final MyTreeNode aNode = (MyTreeNode)path.getLastPathComponent();
if (newValue instanceof String) {
((MyNode) aNode.getUserObject()).setName((String) newValue);
} else {
aNode.setUserObject(newValue);
}
nodeChanged(aNode);
}
[...]
}
Absolutnie nie mam pojęcia, jak właściwie można tu zastosować koncepcję działań. Jeszcze gorsza jest sytuacja z operacją zmiany nazwy podczas wykonywania zmiany tekstu w obiekcie JTextField. W tej chwili nie wiem, jak wdrożyć to w czysty sposób. JTextField powinien zostać skojarzony z pojedynczym węzłem ze struktury modelu drzewa, ponieważ jego model i dołączona akcja powinny zmodyfikować ten model, a gdy ten model zostanie zmieniony, przeglądarka drzewa musiałaby zostać powiadomiona, aby zaktualizować nazwę odpowiedniego węzła w przeglądarce drzewa.
Zakładam, że klasa MyNode (która jest podklasą DefaultMutableTreeNode) musiałaby zaimplementować interfejs Document, a klasa RenameAction musiałaby go zmodyfikować, a następnie wydać zdarzenie, aby powiadomić przeglądarkę drzewa, która wyświetla zmieniony węzeł.
Konkluzja: Muszę przyznać, że jeszcze nie do końca rozumiałem, jak poprawnie zaimplementować akcję, która ma być używana w wielu miejscach w GUI i nie rozumiem całkowicie, jak zaimplementować model, który może być używany przez wiele obiektów GUI (w moim przypadku JTree i JTextField). Prawdopodobnie wszystko to jest dość proste ...
Z góry dziękuję za wszelką pomoc!
Cóż, udzielone odpowiedzi były bardzo pomocne w wyjaśnieniu, w jaki sposób można używać akcji razem z JTrees. Ale jest jeszcze jeden punkt, który chciałbym omówić. W moim GUI mam drzewo reprezentujące moje dane biznesowe połączone z edytorami danych (drzewo znajdujące się w lewej ćwiartce okna i poza tym edytor specyficzny dla typu węzła). Wszystkie węzły mają nazwy, które można zmienić. Ponadto edytory zawierają pole tekstowe (zaimplementowane za pomocą JTextField), w którym wyświetlana jest nazwa węzła i która może być również edytowana. Moja niepewność jest tutaj następująca: Pole JTextField umożliwia przypisanie do niego obiektu akcji, a także modelu. W rzeczywistości model byłby obiektem węzłowym już oglądanym w JTree. Myślę, że powinien istnieć sposób użycia tego samego obiektu modelu używanego w JTree również jako modelu dla JTextField w edytorze, a także ponownego użycia klasy Action tam. Jeśli chodzi o ponowne użycie modelu, myślę, że moja klasa modelu MyTreeNode również będzie musiała zaimplementować interfejs Document, prawda? A gdy pojawił się edytor specyficzny dla węzła, musiałbym skojarzyć węzeł aktualnie wybrany w JTree z obiektem JTextField za pomocą metody setDocument (). Wreszcie moja RenameNodeAction musiałaby zmienić nazwę węzła JTextField. Tak więc moim głównym punktem jest: wyświetlenie jednego modelu w wielu widokach i ponowne użycie tylko jednej akcji RenameAction wszędzie tam, gdzie nazwa węzła ma zostać zmieniona. Czy to ma sens i czy mój pomysł, jak to osiągnąć, jest możliwy?