Forrige avsnitt   Start på kapittel om grafiske brukergrensesnitt

Tekstkomponenter

Forskjellige typer tekstkomponenter

Av tekstkomponenter har vi blant annet tekstfelter og tekstområder. Men det finnes også andre tekstkomponenter med mer spesialiserte egenskaper. All felles funksjonalitet for tekstkomponenter finnes i den abstrakte klassen JTextComponent som er direkte subklasse til JComponent. I denne finner vi blant annet metodene getText og getSelectedText, som altså kan brukes på alle tekstkomponenter. Den første returnerer all tekst som er lagt inn i tekstkomponenten, mens den andre returnerer den teksten som er merket.

Klassene JTextField og JTextArea er direkte subklasser til JTextComponent. En tredje subklasse er JEditorPane som kan brukes til å vise html-filer. En liten orientering om bruken av denne finner du i notatet Bruk av JEditorPane. Som spesialtype til JTextField finner vi subklassen JPasswordField som er beregnet for sikker innlesing av passord. Vi finner også subklassen JFormattedTextField. En orientering om bruken av denne finner du i notatet Bruk av formatterte tekstfelter.

Nærmere om tekstområder

Vi vet at en JTextArea til forskjell fra JTextField kan inneholde flere linjer tekst. Den vanligste bruken av JTextArea er for utskrift av tekst, men det kan også brukes til innlesing av tekst som går over flere linjer. Til forskjell fra JTextField blir det imidlertid ikke generert noen ActionEvent når vi trykker retur-tasten mens markøren står i en JTextArea. Innlesing må derfor foretas på en mer indirekte måte, for eksempel ved å klikke på en knapp utenfor tekstområdet. På denne måten blir det gjort i eksemplet nedenfor. Vanligvis pleier vi å utstyre tekstområder med skrollefelter ved at vi plasserer dem i en JScrollPane. Dersom vi på et tekstområde bruker metoden

  public void setLineWrap( boolean linjeskift )

med parameter lik true, så vil det bli automatisk linjeskifte hver gang en linje er for lang til å få plass innenfor tekstområdets bredde. Et eventuelt horisontalt skrollefelt vil da bli fjernet. Men for å unngå at ord blir delt ved linjeskiftene, bør vi da også for tekstområdet gjøre et kall på metoden

  public void setWrapStyleWord( boolean heleOrd )

med parameter lik true.

Eksempel

Programmet TextAreaFrame.java som er gjengitt nedenfor plasserer to tekstområder i et vindu som vist på følgende bilde.

Programmet er som det du kan finne i Fig. 14.47 i 9. utgave av læreboka til Deitel & Deitel. De tre skjermkomponentene blir plassert i en container av type Box. Den har layout av type BoxLayout. Det innebærer at komponentene plasseres i én rad, enten horisontalt eller vertikalt. I eksemplet er det horisontal plassering. Du kan finne nærmere beskrivelse av Box-containere og BoxLayout i notatet BoxLayout.

Programmet virker på den måten at når det blir klikket på knappen mellom de to tekstområdene, så blir eventuell merket tekst i det første tekstområdet lest inn og satt som tekst i det andre tekstområdet. Driverklasse for programmet finnes i fila TextAreaDemo.java.

 1 import java.awt.event.ActionListener;
 2 import java.awt.event.ActionEvent;
 3 import javax.swing.Box;
 4 import javax.swing.JFrame;
 5 import javax.swing.JTextArea;
 6 import javax.swing.JButton;
 7 import javax.swing.JScrollPane;
 8 import java.awt.BorderLayout;
 9
10 //Copying selected text from one textarea to another.
11 public class TextAreaFrame extends JFrame
12 {
13   private JTextArea textArea1; // displays demo string
14   private JTextArea textArea2; // highlighted text is copied here
15   private JButton copyJButton; // initiates copying of text
16
17   public TextAreaFrame()
18   {
19     super( "TextArea Demo" );
20     Box box = Box.createHorizontalBox(); // create box
21     String demo = "This is a demo string to\n" +
22          "illustrate copying text\nfrom one textarea to \n" +
23          "another textarea using an\nexternal event\n";
24
25     textArea1 = new JTextArea( demo, 10, 15 );
26     box.add( new JScrollPane( textArea1 ) );
27
28     copyJButton = new JButton( "Copy >>>" );
29     box.add( copyJButton );
30     copyJButton.addActionListener(
31
32        new ActionListener() // anonymous inner class
33        {
34          // set text in textArea2 to selected text from textArea1
35          public void actionPerformed( ActionEvent event )
36          {
37            textArea2.setText( textArea1.getSelectedText() );
38          }
39        } // end anonymous inner class
40       ); // end call to addActionListener
41
42     textArea2 = new JTextArea( 10, 15 ); // create second textarea
43     textArea2.setEditable( false ); // disable editing
44     box.add( new JScrollPane( textArea2 ) );
45
46     getContentPane().add(box, BorderLayout.CENTER); //add box to frame
47   } // end TextAreaFrame constructor
48 } // end class TextAreaFrame

Bruk av passordfelt

Klassen JPasswordField er subklasse til JTextField og er spesielt laget for sikker innlesing av passord. Av sikkerhetsgrunner viser den ikke hvilke tegn brukeren skriver i skrivefeltet, men viser isteden et ekkotegn, slik som en stjerne '*'. (Hvilket tegn det skal være kan vi selv bestemme ved bruk av metoden

  setEchoChar(char c)

Programeksemplet nedenfor viser det tegnet som er forhåndssatt.) Som et annet sikkerhetstiltak lagrer et passordfelt sin input i en char-array istedenfor i en String. Metoden getText er for et passordfelt markert som foreldet. Isteden skal metoden getPassword brukes for innlesing. Den returnerer char-arrayen med tegnene som brukeren har skrevet i passordfeltet (men som altså bare vises med et ekkotegn på skjermen). Som et ytterligere sikkerhetstiltak anbefales det at så snart som innlesing av passord og testing på gyldigheten av dette er unnagjort, så fylles den array som er brukt ved innlesingen med nulltegn. Programeksemplet nedenfor viser hvordan dette kan gjøres.

Programeksempel

Eksemplet er hentet fra The Java Tutorials. Hele programmet ligger i fila PasswordDemo.java. Når det blir kjørt, får vi fram følgende programvindu:

I vinduet er det skrevet inn et passord. Vinduet viser hva som er forhåndssatt som ekkotegn. For å få lest inn og testet passordet for gyldighet, kan man enten trykke Enter-tast eller klikke på OK-knappen. Er det galt passord, vises følgende meldingsboks:

Ved riktig passord vises isteden denne meldingsboksen:

For dette eksemplets vedkommende kan man få opplysninger om riktig passord ved å klikke på Help-knappen. Da blir følgende meldingsboks vist:

Riktig passord kan man for øvrig finne ved å lese koden nedenfor.

Vi skal se litt nærmere på noe av koden for programmet. Aller først ser vi på hvordan innlesing og testing på gyldighet av passord er utført. Selve innlesingen skjer i actionPerformed-metoden:

 65       char[] input = passwordField.getPassword();

Innlest array blir sendt til metoden isPasswordCorrect. Denne inneholder det korrekte passordet og sjekker det mot det den får tilsendt. Til dette brukes klassemetoden equals fra Arrays-klassen. Merk at dette er ikke det samme som equals-metoden til String-klassen! Det som gjøres, er å sjekke om to arrayer har samme innhold. Klassen Arrays i pakken java.util inneholder et stort antall klassemetoder for å utføre standardoperasjoner på arrayer. I dette programmet brukes også dens fill-metode for å fylle med 0-tegn de arrayene som ble brukt ved innlesingen. Det skjer uavhengig av om riktig eller galt passord er lest inn.

Programmet inneholder enkelte andre detaljer som du kanskje ikke er kjent med fra tidligere. Vi skal se på noen av dem. Først ser vi på hvordan både passordfeltet og OK-knappen er tilordnet samme reaksjonsmåte for det lytteobjektet som de to komponentene har felles med Help-knappen (og som for øvrig er det panelet som alt sammen ligger i, og som er tilordnet som contentPane for vinduet). Dette er gjort ved at både passordfeltet og OK-knappen er tilordnet samme identifiserende String-konstant OK:

 13   private static String OK = "ok";
      ...
      ...
 25     passwordField.setActionCommand(OK);
      ...
 48     okButton.setActionCommand(OK);

Det ActionEvent-objekt som er parameter til actionPerformed-metoden vil inneholde den identifiserende streng som er tilordnet komponenten der hendelsen ble generert. I actionPerformed-metoden blir identifiserende streng hentet ut og sammenliknet med den som er tilordnet passordfelt og OK-knapp:

 61     String cmd = e.getActionCommand();
 62
 63     if (OK.equals(cmd)) //Kilde til hendelsen er passordfeltet eller OK-knappen

Etter at passord er lest inn og sjekket for gyldighet, gjøres det kall på passordfeltets metode selectAll, samt på programmets metode resetFocus:

 82       passwordField.selectAll();
 83       resetFocus();

selectAll er en fellesmetode for alle tekstkomponenter. Den har som virkning at hele teksten som komponenten inneholder blir valgt og dermed markert, slik vi i dette tilfelle ser på følgende bilde:

Programmets metode resetFocus, som også blir kalt opp når programmet starter opp, gjør kall på passordfeltets metode requestFocusInWindow. Den sørger for at passordfeltet får fokus på den måte at markøren plasserer seg i passordfeltet, slik at det er klart til å skrive noe i det.

  1 /*
  2  * Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
  3  */
  4
  5 import javax.swing.*;
  6 import java.awt.*;
  7 import java.awt.event.*;
  8 import java.util.Arrays;
  9
 10 /* PasswordDemo.java requires no other files. */
 11 public class PasswordDemo extends JPanel implements ActionListener
 12 {
 13   private static String OK = "ok";
 14   private static String HELP = "help";
 15   private JFrame controllingFrame; //needed for dialogs
 16   private JPasswordField passwordField;
 17
 18   public PasswordDemo(JFrame f)
 19   {
 20     //Use the default FlowLayout.
 21     controllingFrame = f;
 22
 23     //Create everything.
 24     passwordField = new JPasswordField(10);
 25     passwordField.setActionCommand(OK);
 26     passwordField.addActionListener(this);
 27
 28     JLabel label = new JLabel("Enter the password: ");
 29     label.setLabelFor(passwordField);
 30
 31     JComponent buttonPane = createButtonPanel();
 32
 33     //Lay out everything.
 34     JPanel textPane = new JPanel(new FlowLayout(FlowLayout.TRAILING));
 35     textPane.add(label);
 36     textPane.add(passwordField);
 37
 38     add(textPane);
 39     add(buttonPane);
 40   }
 41
 42   protected JComponent createButtonPanel()
 43   {
 44     JPanel p = new JPanel(new GridLayout(0, 1));
 45     JButton okButton = new JButton("OK");
 46     JButton helpButton = new JButton("Help");
 47
 48     okButton.setActionCommand(OK);
 49     helpButton.setActionCommand(HELP);
 50     okButton.addActionListener(this);
 51     helpButton.addActionListener(this);
 52
 53     p.add(okButton);
 54     p.add(helpButton);
 55
 56     return p;
 57   }
 58
 59   public void actionPerformed(ActionEvent e)
 60   {
 61     String cmd = e.getActionCommand();
 62
 63     if (OK.equals(cmd))
 64     { //Process the password.
 65       char[] input = passwordField.getPassword();
 66       if (isPasswordCorrect(input))
 67       {
 68         JOptionPane.showMessageDialog(controllingFrame,
 69                 "Success! You typed the right password.");
 70       }
 71       else
 72       {
 73         JOptionPane.showMessageDialog(controllingFrame,
 74                 "Invalid password. Try again.",
 75                 "Error Message",
 76                 JOptionPane.ERROR_MESSAGE);
 77       }
 78
 79       //Zero out the possible password, for security.
 80       Arrays.fill(input, '0');
 81
 82       passwordField.selectAll();
 83       resetFocus();
 84     }
 85     else
 86     { //The user has asked for help.
 87       JOptionPane.showMessageDialog(controllingFrame,
 88               "You can get the password by searching this example's\n"
 89               + "source code for the string \"correctPassword\".\n"
 90               + "Or look at the section How to Use Password Fields in\n"
 91               + "the components section of The Java Tutorial.");
 92     }
 93   }
 94
 95   /**
 96    * Checks the passed-in array against the correct password.
 97    * After this method returns, you should invoke eraseArray
 98    * on the passed-in array.
 99    */
100   private static boolean isPasswordCorrect(char[] input)
101   {
102     boolean isCorrect = true;
103     char[] correctPassword = {'b', 'u', 'g', 'a', 'b', 'o', 'o'};
104
105     if (input.length != correctPassword.length)
106     {
107       isCorrect = false;
108     }
109     else
110     {
111       isCorrect = Arrays.equals(input, correctPassword);
112     }
113
114     //Zero out the password.
115     Arrays.fill(correctPassword, '0');
116
117     return isCorrect;
118   }
119
120   //Must be called from the event dispatch thread.
121   protected void resetFocus()
122   {
123     passwordField.requestFocusInWindow();
124   }
125
126   /**
127    * Create the GUI and show it.  For thread safety,
128    * this method should be invoked from the
129    * event dispatch thread.
130    */
131   private static void createAndShowGUI()
132   {
133     //Create and set up the window.
134     JFrame frame = new JFrame("PasswordDemo");
135     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
136
137     //Create and set up the content pane.
138     final PasswordDemo newContentPane = new PasswordDemo(frame);
139     newContentPane.setOpaque(true); //content panes must be opaque
140     frame.setContentPane(newContentPane);
141
142     //Make sure the focus goes to the right component
143     //whenever the frame is initially given the focus.
144     frame.addWindowListener(new WindowAdapter()
145     {
146
147       public void windowActivated(WindowEvent e)
148       {
149         newContentPane.resetFocus();
150       }
151     });
152
153     //Display the window.
154     frame.pack();
155     frame.setVisible(true);
156   }
157
158   public static void main(String[] args)
159   {
160     //Schedule a job for the event dispatch thread:
161     //creating and showing this application's GUI.
162     SwingUtilities.invokeLater(new Runnable() {
163
164       public void run() {
165         //Turn off metal's use of bold fonts
166         UIManager.put("swing.boldMetal", Boolean.FALSE);
167         createAndShowGUI();
168       }
169     });
170   }
171 }

Copyright © Kjetil Grønning, revidert av Eva Hadler Vihovde 2014

Forrige avsnitt   Start på kapittel om grafiske brukergrensesnitt