Automatyczny wybór paneli ze zmianą kolorów (Timer/SwingWorker)

0

Aktualnie jestem w trakcie tworzenia projektu, podobnego, do tego przedstawionego na poniższym filmiku:

Problem, który mnie dręczy jest co prawda złożony, ale dość prosty.
Program opiera się na wyborze odpowiednich wierszy i kolumn (czyt. zmianie kolorów). Na ten moment stworzyłem dużą liczbę paneli i ustawiłem zmiany kolorów, z wykorzystaniem Timerów. Pomimo tego, że wstępnie wszystko jest gotowe, kod wygląda nieco hmmm brzydko. Liczba if/else i zastosowanie trzech timerów wydaje mi się być wyjątkowo nieefektywną metodą. Chciałem napisać to wszystko z wykorzystaniem jednego Timera, ale wtedy liczba casów (choć wolę unikać switcha) jest przerażająca. Dla każdego Timera napisałem też oddzielny własny ActionListener. Dodam, że za wybór odpowiednich wierszy/kolumn (czyli po prostu paneli) odpowiada pojedynczy przycisk Stop. Ponieważ zmiana akcji przycisku w czasie działania programu (chodzi mi o zmianę z podstawianiem innego listenera), z moim poziomem wiedzy, nie wchodzi w grę, postanowiłem zmieniać ją poprzez zmienną pomocniczą. Dodatkowo stworzyłem kilka tablic paneli, co miało uprościć kod, ale chyba jednak zadziałało odwrotnie do zamierzonego celu. Zastanawiałem się również nad użyciem SwingWorkera, ale to chyba jeszcze bardziej zaciemniło by kod, zwłaszcza, że operacje nie są aż tak wymagające.

Kod niestety wygląda strasznie. Prosiłbym o pomoc w jego sprzątnięciu i uproszczeniu o ile to możliwe. Poniżej zamieszczam część kodu, o którą mi chodzi:


//Zmienne, w których przechowuję wszystkie panele dla danego poziomu. Wymiary mojej tabeli to 6x7

// zawiera panel górny i dolny, czyli po 3 wiersze dla każdego panelu
private final JPanel[] firstLevelPanels = new JPanel[2];   

// tutaj zawarte są (osobno nie w całości jak wcześniej) 3 górne wiersze
    private final JPanel[] secondLevelPanels1 = new JPanel[3];

// analogicznie 3 dolne wiersze   
    private final JPanel[] secondLevelPanels2 = new JPanel[3];   

// 6 tabeli paneli - każda tabela to wiersz. Każdy posiada 7 paneli, czyli 7 paneli w wierszu
private final JPanel[] rowPanels1 = new JPanel[7];
    private final JPanel[] rowPanels2 = new JPanel[7];
    private final JPanel[] rowPanels3 = new JPanel[7];
    private final JPanel[] rowPanels4 = new JPanel[7];
    private final JPanel[] rowPanels5 = new JPanel[7];
    private final JPanel[] rowPanels6 = new JPanel[7];

//tabela tabeli paneli. Wszystkie tabele są tutaj
    private final JPanel[][] allPanels = new JPanel[9][];

    private int currentlyActivePanel = 0;   
    private int activeAncestorPanel = 0;
    private int stopCounter = 0;
    private int counter = 0;

/*ActionListener dla Timera pierwszego poziomu. Zmiana koloru tła panelu, dopóty dopóki wciśnie się przycisku Stop. Aktualnie aktywny panel ma kolor żółty, a nieaktywny szary. Funkcja changeBackground dokonuje zmian koloru tła.*/
    class FirstLevelAction implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {

            changeBackground(allPanels[activeAncestorPanel], counter);
            counter = (counter+1)%2;
        }
    }

//Analogicznie ActionListener dla drugiego timera. Wybór z jednego z trzech wierszy
    class SecondLevelAction implements java.awt.event.ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {

            changeBackground(allPanels[activeAncestorPanel], counter);
            counter = (counter+1)%3;
        }       
    }

//ActionListener dla Timera trzeciego. Wybór jednego pola z 6
    class ThirdLevelAction implements java.awt.event.ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {

            changeBackground(allPanels[activeAncestorPanel], counter);
            counter = (counter+1)%7;
        }     
    }

/*Funkcja zmieniająca kolor paneli w tabeli paneli. Aktualnie aktywny panel ma kolor żółty, reszta kolor szary. PanelIndex określa, który panel jest aktywny */
    private void changeBackground(JPanel[] panels, int panelIndex){

        for(JPanel panel : panels){
            panel.setBackground(Color.GRAY);
        }
        panels[panelIndex].setBackground(Color.YELLOW);
        currentlyActivePanel = panelIndex;
    }

/*Inicjalizacja Timera1 jest tutaj, ponieważ chcę żeby startował od początku programu. Inicjalizacja pozostałych znajduje się w funkcji obsługującej wciśnięcie Stop */
 private Timer timer1 = new Timer(500, new FirstLevelAction(allPanels, activeAncestorPanel));
private Timer timer2;
private Timer timer3;

//Funkcja obsługująca wciśnięcie Stop
private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {                                           
        // TODO add your handling code here:

//stopCounter jest zmienną, która określa działanie przycisku
        stopCounter++;
        counter = 0;

/*Wstrzymanie Timera1, zapisanie aktywnego panelu w zmiennej activeAncestorPanel, zmiana koloru aktywnego panela na szary, wyzerowanie
zmiennej określającej aktualny panel (dla każdej tabeli idzie od 0, co może być trochę kołujące). Inicjalizacja timera2 i wystartowanie timera*/

        if(stopCounter == 1){
            timer1.stop();
            activeAncestorPanel = currentlyActivePanel + 1;
            allPanels[0][currentlyActivePanel].setBackground(Color.GRAY);
            currentlyActivePanel = 0;
            timer2 = new Timer(500, new SecondLevelAction());
            timer2.start();
        }

/*Analogicznie dla timera2, z tym że określenie numeru aktywnego panela jest trochę mylące. Chodzi o to, że w tabeli allPanels znajdują się wszystkie pozostałe tabele. Z kolei w tabeli np. secondLevelPanels1 (czyli 3 wiersze z góry) numery wierszy to 0-2, podczas gdy w zmiennej allPanels są to tabele rowPanels1, rowPanels2 i rowPanels3, a odpowiadające im indeksy w tej właśnie tabeli to od 3-5.*/

        else if(stopCounter == 2){
            timer2.stop();
            allPanels[activeAncestorPanel][currentlyActivePanel].setBackground(Color.GRAY);
            activeAncestorPanel = activeAncestorPanel + activeAncestorPanel*2 + currentlyActivePanel;
            currentlyActivePanel = 0;
            timer3 = new Timer(500, new ThirdLevelAction());
            timer3.start();
        }

//Analogicznie dla timera3
        else{
            timer3.stop();
            stopCounter = 0;
            allPanels[activeAncestorPanel][currentlyActivePanel].setBackground(Color.GRAY);
            activeAncestorPanel = 0;
            timer1 = new Timer(500, new FirstLevelAction());
            timer1.start();
        }
    }

Jest tego dużo, ale zapewniam nie jest to nic skomplikowanego. Po prostu jest to pisane dość łopatologicznie ze względu na małe doświadczenie. Jeśli potrzeba coś dodać lub wyjaśnić, to proszę pytać śmiało :)

0

Z filmiku nic nie rozumiem, a kod jest absurdalnie rozwlekły i nie mam siły go analizować.
U Ciebie jest np. takie coś

        public void actionPerformed(ActionEvent e) {

            if(counter == 0){
                changeBackground(allPanels[activeAncestorPanel], 0);
                counter = 1;
            }else if(counter == 1){
                changeBackground(allPanels[activeAncestorPanel], 1);
                counter = 2;
            }else{
                changeBackground(allPanels[activeAncestorPanel], 2);
                counter = 0;
            }
        }

Można to zapisać krócej:

        public void actionPerformed(ActionEvent e) {
                changeBackground(allPanels[activeAncestorPanel], counter);
                counter = (counter + 1)%3;
        }
0

Ok :) na bieżąco będę poprawiać. Po prostu na filmiku jest ileś tam kolumn i wierszy. Przy każdym naciśnięciu przycisku (tam niewidoczny, u mnie najzwyczajniejszy JButton) program przechodzi do kolejnego poziomu tej całej tabeli z literami. Pierwszy wybór użytkownik dokonuje pomiędzy górnym, a dolnym panelem (z czego każdy panel posiada 4-5 wierszy, liczba nie ma znaczenia). Następnie użytkownik dokonuje drugiego wyboru - który wiersz, z wcześniej wybranego panelu, go interesuje. Ostatni poziom, to wybór kolumny. W ten sposób następuje wybór litery, którą chcemy zapisać. Na razie pominąłem całą szopkę z literami i wykorzystuje panele. Reszta to raczej prosta sprawa. Rzeczywiście kod jest rozwlekły, ale nie liczę na poprawę wszystkiego w jednym poście. Powoli, powoli myślę, że zdołam to poprawić :)

Rzeczywiście zapomniałem zamienić to na modulo. Wcześniej miałem modulo, ale chciałem dokładnie widzieć co robię i zapomniałem powrócić do modulo. Dziękuję. Po tym uproszczeniu kod wygląda trochę lepiej. Zastanawiałem się nad napisaniem jakiejś klasy generycznej dla ActionListenerów, ale chyba z tego zrezygnuję, bo o javowych "szablonach" tylko słyszałem, a nigdy ich nie używałem.

Postaram się dodać komentarze bo rzeczywiście nie każdy może się domyśleć o co mi chodzi, a zagłębianie się w czyiś kod zawsze jest bolesne.

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