Java saját szoftver készítése 10. rész – Listener használata

Mielőtt tovább mennénk, írnék a többféle Listener-ről, melyet beépítettem a programomba. Ezekkel figyelem a program futtatását, és ha egy előre meghatározott dolog bekövetkezik, akkor végrehajtatok egy újabb programrészletet. (Kapcsolódó: What is the purpose of a listener in Java?)

Nagyon sok érdekességet találtam, úgyhogy most ezekről lesz szó részletesen.

ListSelectionListener

Ha azt akarom figyelni, hogy egy JList elemét mikor választották ki, akkor a ListSelectionListener-t kell felhasználnom. Én ezt a CompanyMain főképernyőm két táblájára is felhasználtam, mert így találtam példákat az interneten, aztán rájöttem, hogy a JTable az nem JList, így ennek is utána néztem:

A StackoverFlow-n azt mondják, hogy a JTable is JList-ként viselkedik, mivel neki is van ListSelectionModel-je. Ezért lehet ráakasztani a ListSelectionListener-t. Ez lett belőle:

notFinishedOrdersTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
   public void valueChanged(ListSelectionEvent event) {
      notFinishedEditButton.setEnabled(true);
   }
});

finishedOrdersTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
   public void valueChanged(ListSelectionEvent event) {
      finishedEditButton.setEnabled(true);
   }
});

Ezt képen is látható, ahol persze a Listener nem látszik, de a táblázat és az alatta lévő Szerkesztés gomb igen:

notfinishedorderstable.jpg

Az említett részt kinagyítom, hogy jobban látszódjon:

notfinishedorderstable2.jpg

A  ListSelectionListener-t a két táblázatra, a notFinishedOrdersTable-re és a finishedOrdersTable-ra raktam rá. Amint valamelyik elemüket kijelölöm, az adott tábla alatti Szerkesztés gomb aktívvá válik, és rá lehet kattintani. A kattintás pedig az adott sor rendelését megnyitja az OrderDetailsDialog szerkesztőablakban.

WindowsListener

Átírtam az összes Controller osztályt, hogy amikor átadják a listákat a modifyDeleteDialog-nak megjelenítésre, a Controller várja a megnyitott dialog bezárását. Amint be van zárva, megkérni, hogy a modifyDeleteDialog-on a törlés vagy a módosítás gombot nyomták meg, és ennem megfelelően a kiválasztott elemet törli, vagy megnyitja szerkesztésre.

public void modifyDeleteCompany() throws SQLException {
   List<Company> companyList = companyRepo.findAll();
   final ModifyDeleteDialog modifyDelete = new ModifyDeleteDialog(frame, true, companyList);
   modifyDelete.setVisible(true);
   modifyDelete.addWindowListener(new WindowAdapter() {
      public void windowClosed(WindowEvent e) {
         if (modifyDelete.getSelectedEvent().equals("delete")) {
            try {
               deleteCompany(Integer.valueOf(modifyDelete.getSelectedId()));
               modifyDeleteCompany();
            } catch (SQLException ex) {

            }
         } else if (modifyDelete.getSelectedEvent().equals("modify")) {
            try {
               modifyCompany(Integer.valueOf(modifyDelete.getSelectedId()));
               modifyDeleteCompany();
            } catch (SQLException ex) {

            }
         }
      }
   });
}

Miért kellett figyelnem a windowClosed bekövetkezését? A setVisible(true) paranccsal láthatóvá tettem a ModifyDeleteDialog ablakot a neki átadott céglistával. Majd abban a pillanatban le is kérdeztem a megnyomott gombot, ami ugye nem következett be. Ezért a ModifyDeleteDialog Törlés és Módosítás gombjaira is rátettem egy-egy értékadást, egy String selectedEvent változóba egyikük a “delete”, másikuk a “modify” értéket írta be, és utána a dispose gombbal bezárták a ModifyDeleteDialog ablakot.

A vezérlés visszatért a Controller osztályba, aki észlelte a ModifyDeleteDialog ablak bezárását a Listenerrel, és rögtön le is kérdezte a getSelectedEvent metódussal, hogy törlés vagy módosítás a parancs. Ezután már ennek megfelelően lehet tovább lépni.

KeyListener

Már múltkor meséltem az OrderDetailsDialog-ról, ami a megrendelés rögzítéséről, illetve módosításáról gondoskodik. Ő lenne az:

orderdetailsdialog.jpg

Na most mentálisan nagyon fontosnak tartom, hogy már a megrendelés rögzítésekor lássam, mennyit kap a bevételből a megbízottunk, és mennyi lesz a nyereség. Ez a vállalkozóknál nagyon fontos, hiszen ez kb. az egyetlen motiváció, ami miatt megéri többször annyit dolgozni, mint egy 8 órás munkahelyen 🙂

Az Író munkabére és a Nyereség mezőket ezért úgy írtam meg, hogy valós időben változzon az értékük. Ezek föntről lefelé a következők:

1. Amint beírom a mennyiséget és az egységárat, az író munkabére és a nyereség is megjelenik. Azért, mert az író JComboBox-ba betöltöm az írókat, és az éppen első helyen lévő író adatbázisban rögzített vállalási árával számol.

2. Ha kiválasztok egy másik írót az Író-ból, akkor megint csak módosul az író egységára mező, illetve az Író munkabére és a Nyereség.

3. Ha pedig az író munkabére más, mint mait kiolvastunk az adatbázisból, mert mondjuk egyedi megbízásról van szó, akkor átírom az Író egységára mezőt, és ez alapján megint módosul az Író munkabére és a Nyereség.

4. És még ott az eset, amikor nem vagyok elégedett a nyereséggel, ezért visszamegyek az Egységár mezőre, és módosítom a mi vállalási árunkat. Ekkor az Író munkabére nem módosul, de a Nyereség igen.

Hogy ezek az értékek valós időben módosuljanak, figyelni kellett, történik-e valami a kapcsolódó mezőkben. Ezeket figyeli a KeyListener, és neki is a keyReleased metódusa. Ha felengedtem egy billentyűt az adott mezőben, akkor előtte le is kellett nyomnom, tehát már van érték, amivel számolni lehet.

private void addTextFieldListener() {
   quantityField.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent e) {
         fillSallaryAmount();
      }
   });

   unitPriceField.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent e) {
         fillProfitAmount();
      }
   });

   rateField.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent e) {
         fillSallaryAmount();
         fillProfitAmount();
      }
   });

}

Az író fizetésének beírása:

private void fillSallaryAmount() {
   double amount = Double.valueOf(quantityField.getText()) * Double.valueOf(rateField.getText());
   sallaryAmountLabel.setText(String.valueOf(amount));
}

A nyereség beírása a megfelelő helyre:

private void fillProfitAmount() {
   double amount = Double.valueOf(quantityField.getText()) * Double.valueOf(unitPriceField.getText()) - Double.valueOf(quantityField.getText()) * Double.valueOf(rateField.getText()) ;
   profitAmountLabel.setText(String.valueOf(amount));
}

Persze a hibaellenőrzés még hiányzik, illetve észrevettem egy másik problémát. Amikor a vállalási árat írom, a figyelő aktiválódik és számolja folyamatosan az író munkabérét és a nyereségét. Ha beírom, hogy 1000, akkor először az 1-et nyom le, ekkor az 1-gyel számol nyereséget és munkabért. Utána lenyomom a 0-át, ekkor a 10-zel számol. Újabb 0 után a 100-zal számol, és így tovább. Tényleg valós időben. De amikor elrontom a gépelést, és az 1000-et mondjuk egy 2-essel kezdem, akkor mi történik?

Lenyomom a 2-t, a figyelő beírja a 2-re számolt írói munkadíjat és a nyereséget a megfelelő helyre. Én észreveszem a tévedést, visszatörlöm a 2-t, ekkor a mező értéke null, a figyelő pedig a gomblenyomás miatt írná be a nyereséget és az írói munkadíjat. Ami a null érték miatt nem lehet, és hibával elszáll a program. Tehát ezt is kezelnem kell.

Kis kiegészítés, amikor a OrderDetailsDialog-ot nem új megrendelés rögzítésére használom, hanem egy meglévő rendelés részleteinek megjelenítésére, akkor a konstruktor ugye nem ez:

public OrderDetailsDialog(java.awt.Frame parent, boolean modal) {

hanem ez, mivel át kell adni neki a kért megrendelés részleteit:

public OrderDetailsDialog(java.awt.Frame parent, boolean modal, Order order) {

Na ennek a konstruktornak a végén is szerepel a két számoló metódus meghívása, mivel nem csak a megrendelés részleteit töltöm be az űrlapba, hanem ki is számolom az Író munkadíját és a Nyereséget:

fillSallaryAmount();
fillProfitAmount();

Ez pedig egy ilyen megnyitott megrendelést eredményez:

megrendeles_modositasa.jpg

Most látom a képen, hogy az ablaknak nincs címe. Ezt is be kell állítanom, ha az Order nélküli konstruktor dobja fel az ablakot, akkor az ablak címe “Új megrendelés hozzáadása” lesz, ha az Order-t megkapó konstruktor mutatja, akkor pedig a “Meglévő megrendelés módosítása” lesz. Amúgy nagyon feelinges látni, hogyan változnak a számok élőben a két mezőben, ahogy írogatom be az adatokat.