Java keyListiner w komponentach wewnątrz JFrame/Jpanel

0

Witam

Mam pytanie , szukam i szukam, problem polega na tym iż stworzyłem sobie fajnego Beansa, z obsługą myszy i klawiatury.
Wszystko fajnie działa(tj. funkcjonalność) ale jak zwykle GUI opóźnia i wpienia i dobija !!! :(

Dlaczego beansy dodane do JFrame albo JPanel nie reagują na klawisze ? Na mysz a i owszem.
Pierwsza przeszkoda absurdalna ! To to iż muszą być "focus", to już pokonałem ale wystarczyło zmienić układ komponentów i już nie działa.
Wciąż zdarzenie KeyPressed nie jest nigdzie przekazywane z danego komoponentu do innych. Jak to ominąć?
Oczywiście zastosowałem łopatologiczne rozwiązanie iż JFrame listiner przekazuje zdarzenie do reszty komponentów a te z kolei do komponentów które są w nich itd.
Problem w tym że to gubi sens tworzenia Beansów które mają być "samowystarczalne". To w Beansie powinno być iż np. ESC skasuje coś tam. Dzięki temu nie muszę instalować zdarzenia ESC tysiąc razy. Podobnie jak nie muszę implementować tysiąc razy zdarzeń myszy, ot mają one siedzieć już w samym Beansie.

Tak więc pytanie jak obsłużyć zdarzenia KeyPressed globalnie dla wszystkich komponentów nie tylko dla tych focus albo JFrame ???

Byłbym wdzięczny za sugestię bo to wydaje mi się głupim absurdem w Swingu :(

0

A jak wg Ciebie GUI ma rozpoznać czy wciśnięty klawisz ma obsłużyć ten lub inny komponent? "Rozpoznaje" to właśnie przez przypisywanie focusa. Najpierw event idzie do elementu, który ma focus, a jeżeli tam nie zostanie obsłużone, to leci do rodzica, aż jeżeli nie zostanie obsłużony nigdzie, to ląduje na najwyższym poziomie którym jest już okienko JFrame. Wtedy event może zostać zgubiony lub obsłużony "globalnie" dla całej aplikacji. Na przykład jeżeli w dwóch różnych kontrolkach wciśnięcie klawisza Home oznaczać będzie dwie zupełnie różne akcje, to jak masz rozpoznać, która z nich ma się wykonać? Wykonać obie? (bez sensu). W przypadku myszy problem ten nie istnieje ponieważ zdarzenie myszy ma swoją pozycję i można dzięki temu jednoznacznie przypisać ("najwyższy") komponent nad którym kursory myszy się znajduje. Klawiatura nie daje takiego komfortu, więc aplikacja musi sama zarządzać focusem, żeby obsługa aplikacji z klawiatury była właściwa. Najwyraźniej Twój program tego nie robi. Stąd problem.
Jeżeli potrzebujesz aby na jedno zdarzenie klawisza reagowały wszystkie komponenty, to żaden z nich nie powinien obsługiwać klawiszy jako takich, ale powinien na takie zdarzenie zareagować rodzic wszystkich komponentów wśród których jeden ma focus, a ten z kolei powinien rozgłosić to zdarzenie do wszystkich swoich "dzieci". Przy czym nie powinno to być zdarzenie z klawiatury, tylko Twoje specyficzne.
Część komponentów obsługuje standardowo takie zdarzenia jak np. klawisz Tab i Shift-Tab, których standardową obsługą jest przekazanie focusa następnemu komponentowi swojego rodzica (przy czym kolejność określa rodzic). Tą obsługę zdarzenia klawiatury można zablokować lub zmienić na inną.
Dlatego poszczególne komponenty raczej nie powinny obsługiwać bezpośrednio zdarzeń z klawiatury (szczególnie takich jak obsługa dowolnego klawisza), a powinny to za nie robić kontenery, które mają większą "wiedzę" o swoich komponentach i relacjach między nimi.

0

Nie podzielam tej argumentacji, uważam że Swing w tym miejscu jest źle zorganizowany.
Np. JButton, po co one mają zdarzenia klawiszy defaultowo, gdy focus jest na takim klawiszu to inne komponenty nie reagują na klawisze. Taka uzurpacja zdarzeń jest sensowna w przypadku JTextArea etc, ale BUTTON ! to oczywiste że programy nie są konstruowane tak aby klawisze działały inaczej dla jednego Butona i inaczej dla drugiego, mają działać globalnie bądź dla pewnych grup komponentów jak np. ten tu edytor tekstu w którym pisze powinien działać niezależnie od całej przeglądarki, i bez sensu jest aby każdy kto dołącza ten edytor do swojego forum musiał na nowo ustawiać skróty klawiszowe etc.

No nic, poradziłem sobie rozsyłając zdarzenia i zarządzając nimi z poziomu komponentu nadrzędnego, jak sam proponowałeś, ale dalej pachnie mi to łopatologią.

pzdr

0

W takim razie źle zorganizowany jest nie tylko Swing, ale również Windows, systemy okienne Linuxa, Maca i praktycznie wszystkie inne GUI.
Komponenty domyślnie obsługują tylko cztery klawisze: Enter, spację, Tab i Shift-Tab. Tyle są w stanie sensownie obsłużyć. Komponent taki jak JTextField obsługuje dodatkowo wszystkie klawisze alfanumeryczne, backspace i delete bo tyle jest w stanie sensownie obsłużyć. Jeżeli jednak chcesz użyć klawiszy do np. nawigacji między komponentami, to musi to obsłużyć jakiś kontener (np. panel) bo same komponenty nie mają żadnej wiedzy o położeniu lub wzajemnych relacjach sąsiadów. Wiedzę taką posiada dopiero dopiero komponent wyższego rzędu, czyli kontener.
Zdarzenia takie jak keyPressed nie są przekazywane do innych komponentów ponieważ domyślnie jedynym źródłem tych zdarzeń jest klawiatura (również np. wirtualna na ekranie). Gdyby źródłem takich zdarzeń były komponenty, to byłoby to trochę bez sensu.
W Twoim przypadku jeżeli masz beana, który potrafi sensownie obsługiwać wiele zdarzeń z klawiatury, to powinieneś postarać się aby było on domyślnie focused (lub był nim zawsze). Obsługa klawiszy w GUI zawsze powoduje trochę problemów organizacyjnych ponieważ ścierają się dwie koncepcje (używania klawiszy globalnie oraz zależnie od kontekstu), ale jak na razie nikt nic lepszego nie wymyślił. W drugim wypadku wypadku za obsługę odpowiada focus (np. spacja, enter, czasem litery i cyfry), a w pierwszym okno najwyższego poziomu (zwykle np. skróty klawiszowe).

0

Jak nie chcesz, żeby Ci buttony czy inne komponenty zabierały focusa, to każdemu możesz zrobić coś takiego:
component.setFocusable(false);
oraz przy każdej jakiejś takiej akcji, po której Twój bean mógłby chcieć focusa (np, jak całe okno programu dostanie focusa) mógłbyś robić
bean.requestFocusInWindow();

Lub, jak mówi oficjalny sunowy/oraclowy tutorial:

If a focus listener is not appropriate, you can instead register a PropertyChangeListener on the KeyboardFocusManager. The property change listener is notified of every change involving the focus, including changes to the focus owner, the focused window, and the default focus traversal policy. See the KeyboardFocusManager Properties table for a complete list.

Innymi słowy możesz dodać listener do tego KeyboardFocusManager'a i sprawdzać jakie komponenty zyskują focus... i jeśli nie jest to twój bean, ani nie jest to textarea/jtextfield etc, to wtedy ustawiać focus na swój bean.

1 użytkowników online, w tym zalogowanych: 0, gości: 1