JButton pozostaje "wciśnięty" po kliknięciu

0

Mam applet z JButtonem. Po kliknięciu ActionListener ma do wykonania mnóstwo akcji. Dlatego, kiedy zostanie naciśnięty zostaje przez pewien czas w takiej "wciśniętej" pozycji i wraca do normy dopiero za chwilę.

public void actionPerformed(ActionEvent e) {
  JButton.setEnabled(false);
  //...
}

Chciałbym żeby taka sytuacja nie była widoczna przez uzytkownika - operacje mają być wykonywane w tle.

Panowie ze StackOverflow radzą wsadzić cały kod do wykonania do osobnego Thread'a, aczkolwiek ja w tym kodzie mam już zdefiniowane kilka Runnable() {}.
Ponadto gdy chciałem spróbowac powyższego rozwiązania, straciłem możliwosć wywoływania (this) np. showSaveDialog(this) - teraz NetBeans wyrzuca błąd. Może ktoś wie jak to naprawić?

0

Bo go wyłączasz całkowicie, przestaje w ogóle cokolwiek zmieniać, więc zostaje wciśnięty. JButton po naciśnięciu się "odciska" standardowo, chyba że zablokujesz wątek Swinga jakimiś długimi operacjami, to się odciśnie dopiero po ich zakończeniu, wtedy musisz cały wykonywany kod umieścić w osobnym wątku.

Wywal całe te setEnabled, bo to jest tylko po to, żeby wyłączyć jakiś element żeby w ogóle nie reagował, a instrukcje umieść w czymś takim:

new Thread(new Runnable(){
  @Override
  public void run(){
    //twoje instrukcje
  }
}).start();

Niezbyt poprawnie, ale te instrukcje wykonają się w osobnym, nowym wątku.

0

Dokładnie to napisałem w ostatnim akapicie.

Nie mogę wywalić setEnabled bo kiedy użytkownik wciśnie przycisk rozpocznie się pewna sekwencja akcji. Wciśnięcie go po raz drugi zanim skończy sie owa sekwencja spowoduje zawalenie się całego programu i nieobliczalne skutki.

Jak również napomknąłem w ostatnim akapicie umieszczenie tego wszystkiego w Runnable byłoby OK gdybym nadal mógł uzywać (this).

0

Patent z JButton.setEnabled(false); zaraz po wejściu do procedury obsługi jest poprawny. Wyłączenie źródła komunikatów na czas wykonywania akcji jest właściwe. To co powinieneś zrobić w procedurze obsługi zaraz potem zależy od tego czy te długotrwałe działania będą używać wyłącznie operacji na obiektach Swinga, czy będą robić inne długotrwałe działania. W pierwszym wypadku wywołujesz po prostu metodę invokeLater() z argumentem będącym najprościej instancją klasy, w której metodzie run zapiszesz te długotrwałe działania. W drugim wypadku albo tworzysz nowy wątek i odpalasz go za pomocą Thread.start(), albo tworzysz obiekt bazujący na SwingWorker<T,V> (również może być anonimowy). W obu tych opcjach metoda Thread.run(), albo SwingWorker<T,V>.doInBackground() ma zapisaną długotrwałą sekwencję działań, która wykona się osobnym wątku niezależnym od wątku obsługi zdarzeń Swinga. Jeżeli wśród tych sekwencji potrzebne będą przełączenia lub zmiany stanu kontrolek, to będziesz musiał również tam użyć metody invokeLater() (wszelkie operacje na GUI Swinga należy wykonywać w wątku obsługi Swinga, a invokeLater to zapewnia). Gdy ta długotrwała sekwencja się zakończy, wtedy powinieneś ponownie wywołać właściwy JButton.setEnable(true). W przypadku użycia Thread powinieneś zrobić to na końcu metody run (oczywiście invokeLater), a w przypadku SwingWorkera możesz to zrobić w przeciążonej przez Ciebie metodzie SwingWorker<T,V>.done().

W przypadku SwingWorkera wiele osób ma problemy z jego użyciem z powodu zakłopotania z typami parametryzowanymi. W Twoim wypadku typem wyniku powinien być Void, a typem wyniku pośredniego albo Void, albo Integer. W tym drugim wypadku (SwingWorker<Void, Integer>) świetnie nadaje się on jako wskaźnik postępu zadania we wszelkich kontrolkach pokazujących postęp zadania (np. w JProgressBar). Przeszukaj sobie nawet to forum z kluczem SwingWorker, to Ci się rozjaśni. I przy okazji nauczysz się bardzo wygodnego mechanizmu.

W przypadku takiego użycia jakie opisałem cała obsługa klawisza (która jest wywołana z wątku Event Dispatch Thread) sprowadza się do jednego szybkiego (bo asynchronicznego) wywołania Thread.start() lub równie szybkiego (i również asynchronicznego) SwingWorker<Void, Integer>.execute().

Co do wciśniętego i zamrożonego klawisza, to działa to w ten sposób: Zdarzenie wywołania akcji (actionPerformed) generuje się po otrzymaniu (typowo) i opracowaniu zdarzenia keyReleased lub MouseReleased, tak więc kiedy wywołuje się actionPerformed JButtona, to jest on w stanie wciśniętym i gotowy do "wyciśnięcia". To ostanie zdarzy się dopiero po powrocie z procedury actionPerformed ( w wątku Event Dispatch Thread), więc dopóki procedura ta trwa button jest wciśnięty i zamrożony. Dlatego kluczowe jest w Swingu aby wszelkie procedury były jak najkrótsze lub asynchroniczne.

Co do showSaveDialog(this), to jest to wywołanie metody int JFileChooser.showSaveDialog(Component parent), która wymaga jako argument po prostu swingowego komponentu rodzica, którym może być dowolne okno najwyższego poziomu lub podokno czy inny komponent AWT. Używałeś this ponieważ robiłeś wszystko pewnie w jednym oknie, którego klasa zawiera zapewne cały kod aplikacji. Możesz użyć dowolnej innej zmiennej, pola lub wyniku metody (typowo get-cośtam) będącej referencją do tego obiektu okna, która u Ciebie przyjmowała szczególną postać this. Możesz ją przekazać do obiektu klasy takiej jak Thread czy SwingWorker tak samo jak każdą inną referencję - przez konstruktor i pole, albo jako wywołanie metody, którą sobie stworzysz.

0

Na te this znalazłem rozwiązanie. Załóżmy że twoja klasa nazywa się Wnd:

Wnd ptr=this;
new Thread(new Runnable(){
  @Override
  public void run(){
    //twoje instrukcje
    Wnd.jbutton.setEnabled(true);
  }
}).start();

this wskazuje na Runnable, ale można używać obiektów lokalnych;p

0

To nie dziala bo Wnd ptr musi byc final.
Mozna zawsze Wnd.this dac i juz po klopocie. Skladnia jezyka przede wszystkim, panie.

0

Zawarłem wszystko w nowej Runnable która posiada konstruktor przyjmujący this.

Jednak kod:

Thread x = new Thread(xyz, "");
        x.start(this);

Wyrzuca bład, że required no arguments.

0

Zrób klasę, która dziedziczy po Thread, ma swój konstruktor z argumentem i przeciążoną metodę run().

0

@Olamagato
Dzięki, wszystko działa.

Mam jednakże wrażenie, że wszystko odbywa się jakby wolniej.

0

Pewne spowolnienie jest zawsze jakąś ceną synchronizacji. Spróbuj pobawić się profilerem, żeby zobaczyć co jest źródłem tego spowolnienia.

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