Hoofdstuk 5: Grafische gebruikersinterfaces

Inleiding

  • Tegenwoordig zijn grafische gebruikersinterfaces niet meer weg te denken in een huidig programma. Via de System.out.println kunnen we textueel uitvoer geven aan de gebruiker, maar dit is achterhaald in huidige computersystemen die hoge-resolutieschermen, toetsenbord en een muis bevatten.
  • In dit hoofdstuk gaan we in op hoe we grafische gebruikersinterfaces of GUI's kunnen maken. We zullen ook merken dat dit een leuke toepassing is van Object-geörienteerd programmeren. Elke component in een GUI (een knop, een tekstvak, een venster) kan je immers gaan voorstellen als een object dat bepaalde eigenschappen heeft en bepaalde zaken kan uitvoeren. In Java worden deze componenten reeds voorzien van klasses met gegevensvariabelen en methodes.

Componenten, Layout en gebeurtenisafhandeling

  • Een grafische interfcae wordt gebouwd door componenten op een beeldscherm te rangschikken. Componenten worden voorgesteld door objecten. Dit zijn dingen zoals knoppen, tekstvakken, menu's, aankruisvakjes,...
  • De rangschikking van deze componenten kan gebeuren op basis van een structuur of lay-out. In Java zitten bepaalde lay-outmanagers die de schikking van de componenten automatisch kan doen.
  • Wanneer er op een knop gedrukt wordt, dan moet er iets gebeuren. Die gebeurtenis zal een stuk code uitvoeren. Deze reactie op gebeurtenis wordt gebeurtenisafhandeling genoemd die door gebruikers geïnitieerd wordt, via bv. een muisklik of een toetsenbordinvoer.

AWT, Swing en JavaFX

  • Java heeft nu drie grafische-interfacebibliotheken. De oudste heet AWT (Abstract Window Toolkit) en werd geïntroduceerd als onderdeel van de eerste Java API. Daarna werd Swing geïntroduceerd en was een sterk verbeterde versie. De Derde en nieuwste bibliotheek is JavaFX en is een ganse herwerking van grafische-interfacebibliotheek omdat huidige systemen veel geavanceerder werden (applicatie op verschillende schermen, multitouch functionaliteit,...).

  • Wij zullen ons in die hoofdstuk bezighouden met Swing. De Swing bibliotheek herken je door het feit dat er veel klasses beginnen met de hoofdletter J (JButton, JFrame,...).

Grafische interfaces

  • Grafische gebruikersinterfaces zijn heel belangrijk in programma's. We weten dat een scherm en een beeld opgedeeld is in pixels (Engels: picture elements). Elke pixel is een heel klein gebied dat een kleur kan hebben.
  • De locatie van deze pixels wordt georganiseerd in een assenstelsel. Elk punt in Java kan men voorstellen via een (x,y) paar van waarden. In Java ligt de oorsprong (het punt met (0,0) van het assenstelsel in de linkerbovenhoek.

De klasse JFrame

  • In een modern besturingssysteem wordt een programma geopend in een grafisch venster. Dit venster heeft enkele eigenschappen zoals breedte, hoogte en locatie. Een dergelijk venster kan vergroot of verkleind worden... In Java heten deze vensters frames en kunnen in Swing worden voorgesteld door de klasse JFrame.
  • Om een verster op het scherm weer te geven, moeten we een frame maken en tonen. Dit doen we door een instantie te creëren van JFrame. We kunnen via methodeaanroepen het object tonen op het scherm.
  • Hieronder vind je een voorbeeld hoe je de klasse JFrame gebruikt:

EenvoudigeGUI.java

import javax.swing.*; public class EenvoudigeGUI{ public static void main(String[] args){ JFrame frame = new JFrame(); frame.setTitle("Een eenvoudige grafische interface"); frame.setSize(300,200); frame.setDefaultCloseOperation(EXIT_ON_CLOSE); // als je het venster sluit, dan zal ook de applicatie stoppen frame.setVisible(true); } }
  • Een frame bestaat uit 3 onderdelen: de titelbalk, een optionele menubalk en het inhoudspaneel. De vorm van de titelbalk is afhankelijk van het onderliggend besturingssysteem. De menubalk en inhoudspaneel wordt gecontroleerd door de applicatie zelf. We kunnen aan het inhoudspaneel eenvoudige componenten toevoegen.

Eenvoudige componenten toevoegen

  • Standaard staat een object van JFrame op onzichtbaar. We kunnen wel componenten toevoegen, bv een tekstvak (Eng. label) waarin we een stuk tekst plaatsen.
Container inhoudspaneel = frame.getContentPane(); JLabel label = new JLabel("Ik ben een label."); inhoudspaneel.add(label);
  • Eerst wordt een verwijzing naar het inhoudspaneel opgehaald van het frame. Die wordt dan gebruikt om een label toe te voegen aan het inhoudspaneel, via de methode add().
  • Het inhoudspaneel is van de klasse Container, dat is een klasse dat een verzameling van componenten bevat.
  • Wanneer we dit gedaan hebben, moeten we nog twee regels toevoegen:
frame.pack(); frame.setVisible(true);
  • via de methode pack() worden alle componenten netjes in het frame geschikt. De methode setVisible() verandert de zichtbaarheid van het frame, dat standaard op onzichtbaar is.

  • Een andere component is een knop. In de Swing bibliotheek kunnen we dit aanmaken via JButton.

Container inhoudspaneel = frame.getContentPane(); JButton knop = new JButton("Ik ben een knop."); inhoudspaneel.add(knop);

Alternatieve structuur

  • In het vorige programma hebben we ervoor gekozen om het maken van de GUI te doen in een main-methode. Als alternatief kunnen we onze klasse EenvoudigeGUI als een subklasse van JFrame beschouwen. Het is een programmeerstijl die heel vaak voorkomt. De creatie van het frame wordt dan gedaan in de constructor.

EenvoudigeGUI.java

import javax.swing.*; public class EenvoudigeGUI extends JFrame{ /** * Maak een eenvoudige GUI via een constructor */ public EenvoudigeGUI(){ super("Eenvoudige GUI"); makeFrame(); this.setVisible(true); } /** * Maak het inhoudspaneel */ private void makeFrame(){ Container inhoudspaneel = this.getContentPane(); JLabel label = new JLabel("Ik ben een label"); inhoudspaneel.add(label); this.pack(); } }

EenvoudigeApp.java

public class EenvoudigeApp{ public static void main(String[] args){ EenvoudigeGUI gui = new EenvoudigeGUI(); } }

De klasse JPanel

  • Een object van het type JPanel is een container. Dit object kan niet getoond worden op zichzelf, maar moet in een andere container geplaatst worden. Meestal wordt een JPanel aan een JFrame toegevoegd via methode setContentPane() van de klasse JFrame:
JFrame frame = new JFrame();
JPanel paneel = new JPanel();
frame.setContentPane(paneel);

LabelenKnopGUI.java

import javax.swing.*; public class LabelenKnopGUI extends JFrame{ private JPanel paneel; private JButton knop; private JLabel label; /** * Maak een eenvoudige GUI via een constructor */ public LabelenKnopGUI(){ super("GUI met label en knop"); makeFrame(); } /** * Maak het inhoudspaneel */ private void makeFrame(){ paneel = new JPanel(); knop = new JButton("klik"); label = new JLabel("Hartelijke Welkom!"); paneel.add(knop); paneel.add(label); // maak het paneel als inhoudspaneel van de JFrame this.setContentPane(paneel); } }

LabelenKnopApp.java

public class LabelenKnopApp{ public static void main(String[] args){ JFrame frame = new LabelenKnopGUI(); frame.setSize(400,200); frame.setVisible(true); } }
  • Via het JPanel kunnen we ook een layout meegeven. In Swing wordt de layout verzorgt door zogenaamde layoutmanagers, die gaan automatisch de plaatsing doen van onze componenten. Daarover straks meer.

    Gebeurtenisafhandeling

    • Als je op een knop klikt of als je een tekst invult in een vak, dan kan dit een gebeurtenis genereren. Het javaprogramma moet dan de gebeurtenis afhandelen en duidelijk zeggen wat er dient te gebeuren. Als er een gebeurtenis plaatsvindt moet het programma daarop reageren. Dat noemt men gebeurtenisafhandeling of in het Engels event handling.
    • In een objectgeoriënteerd programma verrichten objecten allerlei taken. De makers van Java weten echter niet wat er dient te gebeuren in jouw applicatie als de gebruiker op een knop drukt. Daarvoor hebben de makers van java een aparte klasse gemaakt die de afhandeling voor zijn rekening neemt. Een dergelijke klasse noemt ook wel een event handler. Er zijn verschillende manieren hoe we zo'n event handler kunnen gebruiken, een daarvan is via een inwendige klasse. Dit is een klasse defenitie binnen een bestaande klasse.
    • Gebruik maken van gebeurtenisafhandeling gaat volgens 4 stappen
      1. Importeren van de nodige bibliotheken
      2. Maken van een event handler als een inwendige klasse
      3. Instantiëren van de event handler in de GUI
      4. Koppelen van het event-handler-object aan de component (knop, tekstvak,...)

Importeren van de nodige bibliotheken

  • Om de gebeurtenissen op te vangen en af te handelen, hebben we bibliotheken nodig die voor ons die afhandeling doen. Deze bibliotheken zitten in de bibliotheek java.awt.event.*.
    import java.awt.event.*;

Maken van een event handler als een inwendige klasse

  • De inwendige klasse is een klasse die de afhandeling zal doen van de gebeurtenis. In Java bibliotheek is er een klasse die "luistert" naar gebeurtenissen en als er een gebeurtenis wordt afgevuurd, dan moet er iets gebeuren. De klasse heet ActionListener. De makers van Java weten natuurlijk niet wat er precies moet gebeuren in je applicatie, dus hebben ze een methode in de klasse ActionListener gemaakt die nog niet ingevuld is. De klasse ActionListener heeft zogenaamde interfaces die je als programmeur zelf een invulling kan geven. De in te vullen methode noemt actionPerformed. Deze method handelt de gebeurtenis af en in de body geeft je aan wat er moet gebeuren bij het klikken op de knop.
  • De inwendige definitie van de klasse is dan:
  class KnopHandler implements ActionListener{
    public void actionPerformed(ActionEvent e){
      //Vanaf hier geef je zelf de invulling van de methode actionPerformed
      label.setText("Bedankt!");
    }
  }

Instantiëren van de event handler in de GUI

  • Wanneer we de klasse gemaakt hebben, moeten we natuurlijk ook een object van de klasse maken. Het is immers het object dat de afhandelingstaak zal uitvoeren naar wat er in de klasse beschreven staat.
  • In de creatie van de GUI kunnen we een instantie van de event handler maken op de tradionele manier:
    KnopHandler kh = new KnopHandler();

Koppelen van het event-handlerobject aan de component

  • Als het object gemaakt is, moeten we aan de component (knop of tekstvak) duidelijk maken dat hij zijn gebeurtenissen moet doorgeven aan de afhandelaar. Dit gebeurt door het volgende:
    knop.addActionListener(kh);
  • Je kan de vorige stap en deze stap combineren door gebruik te maken van een naamloze instantie van de klasse en deze door te geven aan de knop. Een naamloze instantie heet ook wel een anoniem object.
    knop.addActionListener(new KnopHandler());
  • Hieronder vind je een voorbeeld van de volledige code:
import java.awt.event.*; // stap 1 import javax.swing.*; class EventApplicatie extends JFrame{ private JPanel paneel; private JLabel label; private JKnop knop; public EventApplicatie(){ paneel = new JPanel(); knop = new JButton("klik"); label = new JLabel("Hartelijk Welkom!"); KnopHandler kh = new KnopHandler(); // stap 3 knop.addActionListener(kh); // stap 4 paneel.add(knop); paneel.add(label); this.setContentPane(paneel); } //Inwendige klasse class KnopHandler implements ActionListener{ // stap 2 public void actionPerformed(ActionEvent e){ label.setText("Bedankt!"); } } public static void main(String args[]){ JFrame frame = new EventApplicatie(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400,200); frame.setTitle("EventApplicatie"); frame.setVisible(true); } }

Paneel als een aparte klasse

  • De voorgaande voorbeelden zijn relatief eenvoudig en beschouwen slechts één frame. In de praktijk behandelen we ingewikkeldere applicaties. Het is dan ook nuttig om een paneel te beschouwen als een aparte klasse. Een paneel is dan een subklasse van de klasse JPanel. Toegepast op het vorige kunnen we onze code scheiden in een klasse die verantwoordelijk is voor het maken van de GUI en een klasse die de applicatie aanroept.

WelkomstPaneel.java

import java.awt.event.*; import javax.swing.*; class WelkomstPaneel extends JPanel{ private JLabel label; private JButton knop; public WelkomstPaneel(){ label = new JLabel("Hartelijk welkom!"); knop = new JButton("klik"); KnopHandler kh = new KnopHandler(); knop.addActionListener(kh); this.add(knop); this.add(label); } //Inwendige klasse voor event handling class KnopHandler implements ActionListener{ // stap 2 public void actionPerformed(ActionEvent e){ label.setText("Bedankt!"); } } }

WelkomstApp.java

import javax.swing.*; class WelkomstApp extends JFrame{ private JPanel paneel; public WelkomstApp(){ paneel = new WelkomstPaneel(); this.setContentPane(paneel); } public static void main(String[] args){ JFrame frame = new WelkomstApp(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400,200); frame.setTitle("WelkomstApplicatie"); frame.setVisible(true); } }
  • Het leuke is dat je via de klasse een abstractie gemaakt hebt van het paneel. Binnen code WelkomstApp.java heb je niet gezegd dat het paneel uit een knop en een label bestaat. De WelkomstApp hoeft dit ook niet te weten. Ook de afhandeling wordt volledige geabstraheerd door de klasse WelkomstPaneel.

De klasse JTextField

  • Via de klasse JTextField kan je een tekstvakje in je GUI steken waarin je tekst kan intypen. Een JTextField-object maakt van alles wat je intypt een string.
  • De code die nodig is om een tekstvak te maken zijn:
    private JTextField tekstvak;
    tekstvak = new JTextField(20);
    tekstvak.setText("Dit is een tekstvak");

    paneel.add(tekstvak);
  • Zo kan je een tekstvak bijvoegen in je applicatie. Stel dat we een applicatie willen maken met 2 knoppen en een tekstvak. De bedoeling is dat de ene knop zal de inhoud veranderen naar "Bedankt!" terwijl de andere knop zal de inhoud leeg ("") maken.
import java.awt.event.*; import javax.swing.*; public class TweeKnoppenPaneel extends JPanel { private JTextField tekstvak; private JButton knop; private JButton herstelknop; public TweeKnoppenPaneel() { tekstvak = new JTextField( 20 ); tekstvak.setText( "Dit is een tekstvak" ); knop = new JButton( "Klik hier" ); KnopHandler kh = new KnopHandler(); knop.addActionListener( kh ); herstelknop = new JButton( "Reset!"); ResetHandler rh = new ResetHandler(); herstelknop.addActionListener( rh); add( tekstvak ); add( knop ); add(herstelknop); } // inwendige klassen voor event-afhandeling eerste knop class KnopHandler implements ActionListener { public void actionPerformed( ActionEvent e ) { if(e.getSource() == knop){ tekstvak.setText( "Bedankt!" ); } } } // inwendige klasse voor event-handling van reset knop class ResetHandler implements ActionListener { public void actionPerformed( ActionEvent e ) { if(e.getSource() == knop){ tekstvak.setText( "" ); } } } }
  • Het gebruik van twee inwendige klassen is een mogelijkheid, maar het kan ook efficienter door slechts één handler te gebruiken. Het argument ActionEvent e van de methode actionPerformed kan aangeroepen worden om informatie te vragen over de bron van de gebeurtenis. Met de methode getSource() kun je opvragen wat de bron van het event is.
  • De event handler bestaat uit één klasse en ziet er dan als volgt uit:
if(e.getSource() == knop){ tekstvak.setText("Bedankt!"); } if(e.getSource() == herstelknop){ tekstvak.setText(" "); }

results matching ""

    No results matching ""