Informaticasite van het Lauwers College te Buitenpost                 © R.J. van der Beek
 

Hoofdstuk 18 Java

18.5 Een listbox

Deze keer gaan we een programma maken waarin een lijst van namen met cijfers wordt bijgehouden. En aan die lijst kunnen namen met cijfers worden toegevoegd, verwijderd, enz. (zie de figuur hier onder)



  18.5.1. De klasse List

We beginnen met de interface, en we bespreken eerst hoe je een listbox in het applet plaatst.
De listbox met namen noemen we lstNamen, en die moet je eerst declareren.
Dat doe je zo: List lstNamen;

In de methode init zetten we de opdrachten om de listbox echt te maken:
lstNamen = new List();
lstNamen.setBounds(20,20,170,270);
lstNamen.setFont(new Font("Dialog", Font.BOLD, 16));
lstNamen.setBackground(new Color(255,255,255));
this.add(lstNamen);


We kunnen er voor zorgen dat er bij de start direkt een paar namen met cijfers in de lijst staan, door de volgende opdrachten in de methode init te zetten:
lstNamen.add("Jan 7.8");
lstNamen.add("Piet 6.4");
lstNamen.add("Ellie 7.0");


Als je dan het project start dan zie je de namen in de lijst verschijnen, zoals in bovenstaande figuur.

Verder zetten we een label met de naam lblNaam en met het opschrift naam op het applet met daarachter een textbox met de naam txtNaam.
Daar onder een label met de naam lblCijfer en met het opschrift cijfer, met daarachter een textbox met de naam txtCijfer.
Daar weer onder een button met de naam btnVoegtoe en met het opschrift Voeg toe.
En dan nog vier buttons met de namen btnVerwijder, btnOmhoog, btnSorteer en btnZoek.
De opdrachten om die interactiecomponenten te maken zie je bij de volledige code.

Als je op een button klikt moet er iets gebeuren, daarom moet je ActionListeners toevoegen en aan de buttons koppelen. Dat gaat m.b.v. de volgende opdrachten:
btnVoegtoe.addActionListener(this);
btnVerwijder.addActionListener(this);
btnOmhoog.addActionListener(this);
btnSorteer.addActionListener(this);
btnZoek.addActionListener(this);


  18.5.2. Een item toevoegen

Nu moet je er voor zorgen dat, als je op de Voegtoe-button klikt, de naam met het cijfer uit de tekstboxen in de lijst terecht komt.
Dat doe je door als event-handler aan die knop toe te voegen:

	void bVoegtoe_Action()
	{
	    String naam = txtNaam.getText();
	    String cijfer = txtCijfer.getText();
	    lstNamen.add(naam + "  " + cijfer);
	    txtNaam.setText("");
	    txtCijfer.setText("");
	}

Uitleg

Door de opdracht lstNamen.add(naam+ " "+cijfer); wordt het ingevulde onder aan de lijst toegevoegd.
Door naam + " " + cijfer worden de strings aan elkaar vast geplakt, eerst de naam, dan twee spaties en dan het cijfer. Dat noem je wel concatenatie.
Wil je dat het bovenaan wordt toegevoegd aan de lijst dan moet je de opdracht veranderen in: lstNamen.add(naam+ " "+cijfer,0);
De plaatsen op de lijst worden met nummers aangegeven (dat wordt de index genoemd), en daarbij beginnen ze bij 0 te tellen. Dus de bovenste naam van de lijst heeft index 0, de tweede heeft index 1, enz.

Als je per ongeluk op de voegtoe-knop klikt en er is geen naam en/of cijfer ingevuld, dan wordt er een leeg veld aan de lijst toegevoegd.
Het zou mooier zijn als er dan geen item aan de lijst werd toegevoegd.
Ik kan me voorstellen dat je denkt dat de oplossing is in plaats van lstNamen.add(naam+ " "+cijfer); de volgende code in te voeren:
if (( naam != "" ) && ( cijfer != "" ))
{ lstNamen.add(naam+ " "+cijfer); }


!= betekent ongelijk
! betekent niet
&& staat voor and
|| staat voor of
en "" staat voor de lege string.

Als je dit probeert dan wordt er geen foutmelding gegeven, maar er wordt toch een leeg veld aan de lijst toegevoegd als je zonder iets in te vullen op de voegtoe-knop klikt.

Het moet op een andere manier, namelijk zo:
if (( !(naam.equals("")) || (cijfer.equals("")))
{ lstNamen.add(naam+ " "+cijfer);}

  18.5.3. Een item verwijderen

Je moet er voor zorgen dat, als je op de verwijder-button klikt, de naam uit de lijst die geselecteerd is verwijderd wordt. Je gaat er dan van uit dat er eerst een naam geselecteerd is.
Dat doe je door als event-handler aan die knop toe te voegen:

	void bVerwijder_Action()
	{
	    int i=lstNamen.getSelectedIndex();
	    if (i != -1) lstNamen.remove(i);
	}

De eerste opdracht zorgt er voor dat i de waarde van de index krijgt van het geselecteerde item.
Daarbij moet je bedenken dat de index bij 0 begint te tellen. Dus als het eerste item is geselecteerd krijgt i de waarde 0, als het tweede item is geselecteerd krijgt i de waarde 2, enz. En als er geen item geselecteerd is krijgt i de waarde -1
En de tweede opdracht zorgt er voor dat, als er een item geselecteerd is (dus als i ongelijk aan -1 is) het item met index i verwijderd wordt.

  18.5.4. Een item omhoog schuiven

Je moet er voor zorgen dat, als je op de omhoog-button klikt, de naam uit de lijst die geselecteerd is één omhoog geschoven wordt (en die er boven stond moet dan dus één omlaag). Je gaat er dan van uit dat er eerst een naam geselecteerd is.
Dat doe je door als event-handler aan die knop toe te voegen:

	void bOmhoog_Action()
	{
	    int i = lstNamen.getSelectedIndex();
	    if (i > 0)
	    {
	        String naam=lstNamen.getItem(i);
	        lstNamen.remove(i);
	        lstNamen.add(naam,i-1);
	        lstNamen.select(i-1);
	    }
	}

De eerste opdracht zorgt er voor dat i de waarde van de index krijgt van het geselecteerde item.
En als er geen item geselecteerd is krijgt i de waarde -1
Als de bovenste naam is geselecteerd dan kan die niet omhoog geschoven, dus als i=0 dan hoeft er niets te gebeuren.
Ook als er niets geselecteerd is, dus als i = -1, dan hoeft er niets te gebeuren.
Daarom hoeft het volgende alleen te gebeuren als i > 0, en daarom staat er in de tweede regel: if i>0
Dan moet nummer i één omhoog. Dat komt er op neer dat nummer i en i-1 omgewisseld moeten worden.
Eerst wordt de naam van nummer i in de string naam vastgelegd.
Dan wordt nummer i weggehaald.
En dan wordt er op plaats i-1 een nieuwe tussengevoegd, en wel de naam die eerst op plaats i stond.
Tenslotte wordt er nog even voor gezorgd dat nummer i-1 nu geselecteerd wordt.
En dan is het klaar.

  18.5.5. Herhalingsopdrachten

In de volgende paragraaf moet steeds dezelfde handeling worden herhaald.
Er zijn in Java een paar manieren om iets te laten herhalen.

for-lus
Als je iets tien keer wilt herhalen, dan kan dat met behulp van de opdracht:

for (int i=1; i<=10; i++)
{
..............
}

Daarbij is i een variabele waarvan de beginwaarde 1 is.
De eindwaarde van i is 10, dus i is steeds kleiner of gelijk aan 10.
En de betekenis van het derde wat tussen de haakjes staat, i++, is dat i steeds één groter wordt.
Dus i neemt de waarde 1, 2, 3 t/m 10 aan. De variabele i wordt daarom ook wel de teller genoemd, hij houdt bij tot hoever de herhaling gevorderd is.
Wat er precies herhaald wordt moet tussen accolades staan.

Eigenlijk kun je net zo goed de opdracht als volgt schrijven:

for (int i=0; i<=9; i++)
{
..............
}

Dan neemt i de waarden 0, 1, 2 t/m 9 aan, maar dan wordt het ook 10 keer herhaald.
En je kunt ook een andere variabelenaam gebruiken.

while-lus
Een while-lus herhaalt opdrachten zolang de voorwaarde na het woord while waar is.
De voorwaarde wordt geëvalueerd voordat de opdrachten worden uitgevoerd.
public void paint(Graphics g)  
     { 
      Dimension d; 
      d = this.getSize(); 
      int breedte = d.width; int hoogte = d.height; 
      int straal = hoogte/2; 
      if (breedte<hoogte) { straal=breedte/2;} else {straal=hoogte/2;} 
      int x=breedte/2-straal; int y=hoogte/2-straal; 
      while (straal > 0)
         {
            g.setColor(255 - i, 255 - i, 255 - i); 
            g.fillOval (x+10, y+10, 2*straal, 2*straal); 
            straal = straal - 10; 
         } 
      }

do-while-lus
Met een do-while lus wordt de voorwaarde om te lussen pas gecontroleerd nadat de lus werd doorlopen.
Dit betekent dat bij een do...while-lus de opdrachten binnen de lus minstens één keer worden uitgevoerd.
public void paint(Graphics g)  
     { 
      Dimension d; 
      d = this.getSize(); 
      int breedte = d.width; int hoogte = d.height; 
      int straal = hoogte/2; 
      if (breedte<hoogte) {straal=breedte/2;} else {straal=hoogte/2;} 
      int x=breedte/2-straal; int y=hoogte/2-straal; 
      do
         {  g.setColor(255 - i, 255 - i, 255 - i); 
            g.fillOval (x+10, y+10, 2*straal, 2*straal); 
            straal = straal - 10; 
         } while (straal > 0)
      }

  18.5.6. Sorteren

Je moet er voor zorgen dat, als je op de Sorteer-button klikt, de namen uit de lijst gesorteerd worden, dus in alfabetische volgorde worden gezet
Het sorteerprobleem bestaat uit een aantal stappen, dat zijn deelproblemen.
We gaan niet direkt tot in de finesses verfijnen, we doen het weer in etappes.
Eerst verfijnen we het als volgt:



Het deelprobleem: Maak een doorloop enz..... gaan we nu verder verfijnen.
In een structuurdiagram wordt dat het volgende:



Het deelprobleem: Verwissel (teller-1, teller) moeten we ook nog in stapjes verdelen.
Als je de inhoud van twee variabelen a en b wilt verwisselen, dan kan dat niet met de opdrachten:
a := b;
b := a;
Want stel je voor dat a het woord mies bevat, en b het woord aap.
Dan zal na de opdracht a := b de variabele a het woord aap bevatten, en b bevat ook nog steeds het woord aap.
En dan ben je het woord mies kwijt!
En na de volgende opdracht b := a zal de variabele b het woord aap bevatten, en a bevat ook het woord aap.
Dan zijn ze dus niet verwisseld.
Je hebt een extra hulpvariabele nodig, waarin het eerste woord wordt bewaard.
Het moet als volgt:

hulp := b;
b := a;
a := hulp;

De java-code voor de event-handler van de sorteer-knop is dan als volgt:

void bSorteer_Action()
	{
	    boolean verwisseld=true;
	    
	    while (verwisseld==true)
	    {
	        verwisseld=false;
	        txtNaam.setText("false" );
    	    for (int i=1;i<lstNamen.getItemCount();i++)
    	    {   
    	        String naam1=lstNamen.getItem(i);
    	        String naam2=lstNamen.getItem(i-1);
    	        if (naam1.compareTo(naam2) < 0)
    	        {
    	            lstNamen.remove(i);
    	            lstNamen.add(naam1,i-1);
    	            verwisseld=true;
    	            txtNaam.setText("true" );
    	        }
    	    }   
    	}
   }

Dit algoritme wordt bubble sort genoemd.
We bespreken eerst het blokje:

1    	    for (int i=1;i<lstNamen.getItemCount();i++)
    	    {   
2    	        String naam1=lstNamen.getItem(i);
3    	        String naam2=lstNamen.getItem(i-1);
4    	        if (naam1.compareTo(naam2) < 0)
    	        {
5    	            lstNamen.remove(i);
6    	            lstNamen.add(naam1,i-1);
7    	            verwisseld=true;
8    	            txtNaam.setText("true" );
    	        };  

In regel 1 zie je dat regel 2 t/m 8 herhaald wordt.
En wel net zo vaak als er items in de lijst zijn, want lstNamen.getItemCount() geeft het aantal items uit de lijst.
De eerste keer heeft i de waarde 1
naam1 is dan de naam van item nr. 1 en naam2 is dan de naam van item nr. 0.
Als naam1 alfabetisch voor naam2 komt, dan wordt naam1.compareTo(naam2) < 0
Als naam1 alfabetisch na naam2 komt, dan wordt naam1.compareTo(naam2) > 0
Als naam1 gelijk is aan naam2, dan wordt naam1.compareTo(naam2) gelijk aan 0

In regel 4 zie je dus dat als naam1 alfabetisch voor naam2 komt dan worden de volgende opdrachten uitgevoerd, en dat heeft tot gevolg dat item nr. 1 en nr. 0 worden omgewisseld.
Dan krijgt i de waarde 2, en dan worden item nr. 2 en nr. 1 omgewisseld als dat alfabetisch nodig is.
Daarna krijgt i de waarde 3, en dan worden item nr. 3 en nr. 2 omgewisseld als dat alfabetisch nodig is.
En zo gaat dat verder, tot de laatste items.

Nu gaan we terug naar de complete event-handler.
In de eerste regel staat: boolean verwisseld=true;
Dat heeft tot gevolg dat er een boolese variabele wordt gedeclareerd, die wordt verwisseld genoemd, en die krijgt de waarde true
(een boolese variabele is een variabele die alleen maar de waarden true en false kan hebben, dus waar en niet-waar)
In de volgende opdracht staat: while (verwisseld==true)
Dat heeft tot gevolg dat het opdrachten-blok er na steeds herhaald wordt, en pas als verwisseld de waarde false heeft wordt er mee gestopt (er wordt herhaald zolang verwisseld de waarde true heeft)
Dat betekent dat het blauwe blokje hierboven net zo lang herhaald wordt tot er niets meer te verwisselen is, want als er wel iets verwisseld is dan krijgt verwisseld de waarde true (zie regel 7).
En als er niets meer te verwisselen is dan staan de items in alfabetische volgorde.

  18.5.7. Zoeken

Je moet er voor zorgen dat, als je op de zoek-button klikt, en je hebt een naam en een cijfer in de tekstboxen ingevuld, de computer op zoek gaat in de lijst of die naam met dat cijfer voorkomt. En als dat voorkomt wordt die naam geselecteerd, zodat hij opvalt.
Dat doe je door als event-handler aan die knop toe te voegen:

	void bZoek_Action()
	{
1	    String naam=txtNaam.getText();
2	    boolean gevonden=false;
3	    int index=0;
4	    while (gevonden==false && index < lstNamen.getItemCount())
5	        if (lstNamen.getItem(index).indexOf(naam)>-1) 
	            {
6	                gevonden=true;
7	                lstNamen.select(index);}
	        else
8	            {index++;};
	}

Uitleg:
Eerst wordt de ingetypte naam in een string vastgelegd (regel 1).
Er moet worden bijgehouden of de naam wel of niet gevonden is, en dat gebeurt met de variabele gevonden, die alleen de waarde true en false kan hebben. In het begin krijgt die de waarde false, want dan is de naam nog niet gevonden (regel 2)
Dan moeten alle items uit de lijst bekeken worden of ze overeenkomen met de ingetypte naam. Het nummer van het item dat aan de beurt is wordt met index aangegeven, en de eerste die aan de beurt is is nummer 0 (de bovenste, zie regel 3)
Er moet worden doorgegaan met zoeken zolang de naam nog niet gevonden is en zolang nog niet alle nummers aan de beurt zijn geweest.
De methode lstNamen.getItemCount() wordt gebruikt om er achter te komen wat het aantal items in de lijst is.
Zolang geldt index < lstNamen.getItemCount() zijn nog niet alle items aan de beurt geweest.
(&& betekent en)
Als de naam voorkomt in het item dat aan de beurt is (regel 5) dan is de naam gevonden (regel 6) en het item moet worden geselecteerd (regel 7)
En anders moet er naar het volgende item worden gekeken, dus de index moet met 1 worden opgehoogd (regel 8)

  18.5.8. De volledige code.

Tot slot nog de volledige code:

import java.awt.*; 
import java.applet.*; 
import java.awt.event.*; 

public class Oefening6 extends Applet implements ActionListener 
{ 
   Label lblNaam;
   Label lblCijfer;
   TextField txtNaam;
   TextField txtCijfer;
   Button btnVoegtoe;
   Button btnVerwijder;
   Button btnOmhoog;
   Button btnSorteer;
   Button btnZoek;
   List lstNamen;

   public void init()
   {
      setLayout(null);
      setSize(400,300);
      setBackground(new Color(255,255,255));
      lblNaam = new Label("naam:",Label.CENTER);
      lblNaam.setBounds(200,20,50,30);
      lblNaam.setFont(new Font("Dialog", Font.BOLD, 16));
      lblNaam.setBackground(new Color(255,255,0));
      add(lblNaam);
      
      txtNaam = new TextField();
      txtNaam.setText("");
      txtNaam.setBounds(260,20,100,30);
      txtNaam.setFont(new Font("Dialog", Font.BOLD, 16));
      add(txtNaam);
      
      lblCijfer = new Label("cijfer:",Label.CENTER);
      lblCijfer.setBounds(200,60,50,30);
      lblCijfer.setFont(new Font("Dialog", Font.BOLD, 16));
      lblCijfer.setBackground(new Color(255,255,0));
      add(lblCijfer);
      
      txtCijfer = new TextField();
      txtCijfer.setText("");
      txtCijfer.setBounds(260,60,100,30);
      txtCijfer.setFont(new Font("Dialog", Font.BOLD, 16));
      add(txtCijfer);
      
      btnVoegtoe = new Button("Voegtoe");
      btnVoegtoe.setBounds(200,100,160,30);
      btnVoegtoe.setFont(new Font("Dialog", Font.BOLD, 16));
      btnVoegtoe.setBackground(new Color(150,150,150));
      add(btnVoegtoe);

      btnVerwijder = new Button("Verwijder");
      btnVerwijder.setBounds(200,140,160,30);
      btnVerwijder.setFont(new Font("Dialog", Font.BOLD, 16));
      btnVerwijder.setBackground(new Color(150,150,150));
      add(btnVerwijder);

      btnOmhoog = new Button("Omhoog");
      btnOmhoog.setBounds(200,180,160,30);
      btnOmhoog.setFont(new Font("Dialog", Font.BOLD, 16));
      btnOmhoog.setBackground(new Color(150,150,150));
      add(btnOmhoog);

      btnSorteer = new Button("Sorteer");
      btnSorteer.setBounds(200,220,160,30);
      btnSorteer.setFont(new Font("Dialog", Font.BOLD, 16));
      btnSorteer.setBackground(new Color(150,150,150));
      add(btnSorteer);

      btnZoek = new Button("Zoek");
      btnZoek.setBounds(200,260,160,30);
      btnZoek.setFont(new Font("Dialog", Font.BOLD, 16));
      btnZoek.setBackground(new Color(150,150,150));
      add(btnZoek);

      lstNamen = new List();
      lstNamen.setBounds(20,20,170,270);
      lstNamen.setFont(new Font("Dialog", Font.BOLD, 16));
      lstNamen.setBackground(new Color(255,255,255));
      add(lstNamen);

      btnVoegtoe.addActionListener(this);
      btnVerwijder.addActionListener(this);
      btnOmhoog.addActionListener(this);
      btnSorteer.addActionListener(this);
      btnZoek.addActionListener(this);
      
      lstNamen.add("Jan 7.8");
      lstNamen.add("Piet 6.4");
      lstNamen.add("Ellie 7.0");
   }

   public void actionPerformed(ActionEvent e)
      {
          if (e.getSource() == btnVoegtoe)
            btnVoegtoe_Action();
          else if (e.getSource() == btnVerwijder)
            btnVerwijder_Action();
          else if (e.getSource() == btnOmhoog)
            btnOmhoog_Action();
          else if (e.getSource() == btnSorteer)
            btnSorteer_Action();
          else if (e.getSource() == btnZoek)
            btnZoek_Action();
      }
   

   public void btnVoegtoe_Action()
   {
       String naam=txtNaam.getText();
       String cijfer=txtCijfer.getText();
       lstNamen.add(naam + "  " + cijfer);
       txtNaam.setText("");
       txtCijfer.setText("");
   }

   public void btnVerwijder_Action()
   {
       int i=lstNamen.getSelectedIndex();
       if (i!=-1) lstNamen.remove(i);
   }

   public void btnOmhoog_Action()
   {
       int i=lstNamen.getSelectedIndex();
       if (i>0)
       {
           String naam=lstNamen.getItem(i);
           lstNamen.remove(i);
           lstNamen.add(naam,i-1);
           lstNamen.select(i-1);
       }
   }

   public void btnSorteer_Action()
   {
       boolean verwisseld=true;
       
       while (verwisseld==true)
       {
           verwisseld=false;
          for (int i=1;i<lstNamen.getItemCount();i++)
          {   
              String naam1=lstNamen.getItem(i);
              String naam2=lstNamen.getItem(i-1);
              if (naam1.compareTo(naam2) < 0)
              {
                  lstNamen.remove(i);
                  lstNamen.add(naam1,i-1);
                  verwisseld=true;
              }
          }
        }
   }

   public void btnZoek_Action()
   {
      String naam = txtNaam.getText();
      boolean gevonden=false;
      int index=0;
      while (gevonden==false && index < lstNamen.getItemCount())
          if (lstNamen.getItem(index).indexOf(naam)>-1) 
              {
                  gevonden=true;
                  lstNamen.select(index);}
           else
             {index++;}
   }
}

Hieronder zie je het applet, probeer maar eens!



Opgaven.
Maak nu opgave 5 van hoofdstuk 18 (Java)