Simple user interfaces Liang, Chapter 10 (some parts) Liang Chapter 11 (some parts) Jframes : user interfaces Next we’re going to learn how to get java to create more complicated JFrames for input and output. These can contain larger text areas, multiple buttons, choice buttons, graphics, etc. These are a type of GUI (Graphical User Interface). In describing Jframes I’m going to use sections of Liang chapter 10 and chapter 11. I’ll skip bits of these chapters, though. Parts of a JFrame This is a Jframe. It is a window with various user interface components. Test frame Every JFrame contains a contentPane, which is a Container holding all of the contents of the JFrame _ _ The contentPane can hold JComponents such as JMenu, JTextArea, JCheckBox, JRadioButton, JButton etc.. This is a text area with multiple lines in it submit clear male A Jpanel containing two JButtons and two JCheckBoxs female The contentPane may contain JPanels, with each panel containing a subgroup of JComponents. Example Jframe (see Liang 10.3.1) import javax.swing.*; // JFrame is a swing class public class MyFrame{ public static void main(String[] args){ JFrame frame = new JFrame("my first JFrame"); frame.setSize(400,300); // size of frame in pixels frame.setVisible(true); // make the frame appear // on the screen frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } There’s no System.exit() here. Instead, this line tells the MyFrame program to exit when the frame window is closed by the user. You just have to include this to get the program to end properly. This program does nothing but display an empty Jframe. Screen coordinates go from upper left corner. X (0,0) (600,0) Y (0,400) (600,400) Placing a JFrame at a location import javax.swing.*; // JFrame is a swing class public class MyFrame{ public static void main(String[] args){ JFrame frame = new JFrame("my first JFrame"); frame.setSize(400,300); // size of frame in pixels frame.setLocation(300,300); // location of upper-left corner // of frame on screen (in pixels) frame.setVisible(true); // make the frame appear frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } Liang 10.3.1 gives code for getting the size of the screen and using setLocation to place the frame in the center of the screen. (Click here to download this program) JFrame with a component (10.3.3) To add an interface component to a frame (like a JRadioButton, a JButton, a JTextArea,etc), we have to get the ContentPane of the frame and add the component to that ContentPane. A ContentPane is a Container. We can add a component using the Container class, like this: Jbutton myButton = new JButton("hello!"); Container content = frame.getContentPane(); content.add(myButton); Or, more compressed: Container content = frame.getContentPane(); content.add(new JButton("hello!")); Or even more compressed: frame.getContentPane().add(new JButton.("hello!")); Container is a class from the package java.awt.*. We have to import java.awt.* to use Container . JFrame with a component (10.3.3) import javax.swing.*; // JFrame is a swing class import java.awt.*; // awt to use the Container class // it’s ‘java’ not ‘javax’! public class MyFrame{ public static void main(String[] args){ JFrame frame = new JFrame("my first JFrame"); frame.setSize(400,300); Container content = frame.getContentPane(); content.add(new JButton("hello!")); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } This creates a frame with a single big button in it, saying “hello!” A JCheckBox component (download here) import javax.swing.*; // JFrame is a swing class import java.awt.*; // to use the Container class // it’s ‘java’ not ‘javax’! public class MyJCheckBoxFrame{ public static void main(String[] args){ JFrame frame = new JFrame("my JCheckBox JFrame"); frame.setSize(400,300); Container content = frame.getContentPane(); content.add(new JCheckBox("check this", true)); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame. } } EXIT_ON_CLOSE); “new JCheckBox(“check this”, true));” creates a JCheckBox with the box already checked (that’s what the ‘true’ means) A JTextArea component (liang 11.6) import javax.swing.*; // JFrame is a swing class import java.awt.*; // to use the Container class // it’s ‘java’ not ‘javax’! public class MyJTextAreaFrame{ public static void main(String[] args){ JFrame frame = new JFrame("my JTextArea JFrame"); frame.setSize(400,300); Container content = frame.getContentPane(); content.add(new JTextArea(4,25)); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame. EXIT_ON_CLOSE); } } “new JTextArea(4,25));” creates a JTextArea object that is 4 characters (or lines) high and 25 characters across (for the user to type in). More than one component in a JFrame. • The programs we’ve seen so far just put one component into a JFrame. We usually want more than one component. • If we have more than one component in a JFrame, we have to organise their layout. • We set a layout manager object for the contentPane. • There are different predefined layout managers: – flowLayout() places components left to right in the order they were added – gridLayout(int rows, int columns) defines a grid of rows*columns locations – borderLayout() has five locations, north south east west center. This is the default layout manager in contentPane objects. • When we add multiple components to the contentPane, they are placed in position by the layout manager we have placed in the contentPane. If we put different layout manager objects in, we will get different layouts. Creating and setting a LayoutManager import javax.swing.*; import java.awt.*; public class MyFlowFrame{ public static void main(String[] args){ JFrame frame = new JFrame("my flowLayout JFrame"); frame.setSize(400,300); Container content = frame.getContentPane(); content.setLayout(new FlowLayout()); for(int i=0; i<20; i++){ content.add(new JCheckBox("check " + i)); } frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } We could use new flowLayout(5,5) , which would put 5 pixel spaces between components, new flowLayout(FlowLayout.LEFT) which would align components to the left, or new flowLayout(FlowLayout.LEFT,5,5) to do both Setting a gridLayout manager (download here) import javax.swing.*; import java.awt.*; public class MyGridFrame{ public static void main(String[] args){ JFrame frame = new JFrame("my gridLayout JFrame"); frame.setSize(400,300); Container content = frame.getContentPane(); content.setLayout(new GridLayout(10,2)); for(int i=0; i<20; i++){ content.add(new JCheckBox("check " + i)); } frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } new GridLayout(10,2)creates a layout manager object that puts components in a grid 10 rows down, 2 columns across. We could use new GridLayout(10,2,5,5), which would create a 10 by 2 grid with 5 pixel spaces between components Playing with LayoutManagers Notice that things are placed in the layout according to the order in which they were added to the contentPane. In these examples, I added things using a loop. This is just for demonstration purposes! I also added the same thing each time. Again, this is just for demonstration: you can add any type of JComponent. • • • • • Experiment with these 3 Layout Managers For each, observe effects of resizing the window For each, explore arguments to the constructor To avoid surprises, always explicitly set a LayoutManager Expect surprises and trial and error 1 JTextArea and 4 JButtons (download here) import javax.swing.*; import java.awt.*; public class MyFourButtonFrame{ public static void main(String[] args){ JFrame frame = new JFrame("my first JFrame"); frame.setSize(400,300); Container content = frame.getContentPane(); content.setLayout(new BorderLayout()); content.add(new JTextArea(4,25),BorderLayout.CENTER); content.add(new JButton("North"), BorderLayout.NORTH); content.add(new JButton("South"), BorderLayout.SOUTH); content.add(new JButton("West"), BorderLayout.WEST); content.add(new JButton("East"), BorderLayout.EAST); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame. EXIT_ON_CLOSE); } A BorderLayout Jframe has 5 specified locations: } BorderLayout.CENTER,NORTH,SOUTH EAST WEST MyGridFrame extends Jframe (download here) import javax.swing.*; import java.awt.*; class MyGridFrame extends JFrame{ MyGridFrame(){ // constructor super("my gridLayout JFrame"); // superclass constructor Container content = this.getContentPane(); content.setLayout(new GridLayout(10,2)); for(int i=0; i<20; i++) { content.add(new JCheckBox("check " + i)); } this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } class testGridFrame{ public static void main(String[] args) { MyGridFrame frame = new MyGridFrame(); // make a MyGridFrame frame.setSize(400,300); // set the size frame.setVisible(true); // make it visible } } Notice that the MyGridFrame object has methods setSize and setVisible: it inherits them from JFrame Liangs FlowLayout example (page 386) import javax.swing.*; import java.awt.*; public class ShowFlowLayout extends Jframe{ public ShowFlowLayout(){ Container container = getContentFrame(); container.setLayout(new FlowLayout(FlowLayout.LEFT,10,20); for (int i = 1; i <= 10; i++){ container.add(new Jbutton("Component "+i)); } } public static void main(String[] args){ ShowFlowLayout frame = new ShowFlowLayout(); frame.setTitle("show flow layout"); frame.setDefaultCloseOperation(Jframe.EXIT_ON_CLOSE); frame.setSize(400,300); frame.setVisible(true); } } In this example, the main method is inside the ShowFlowLayout class, rather than there being a separate test class. Class JPanel • Most interfaces combine several kinds of component: – – – – – – text boxes messages check buttons clickable buttons menus graphics • Often we want to group some of these together, for layout reasons. • Interface components are grouped within JPanels • JPanel comes from javax.swing Example using JPanel This is a Jframe. It is a window with various user interface components. Test frame Every JFrame contains a contentPane, which is a Container holding all of the contents of the JFrame _ _ The contentPane contains JComponents such as JMenu, JTextArea, JCheckBox, JRadioButton, JButton etc.. This is a text area with multiple lines in it submit clear male A Jpanel containing two JButtons and two JCheckBoxs female The contentPane may contain JPanels, with each panel containing a subgroup of JComponents. import javax.swing.*; // (download here) import java.awt.*; class MyFrame extends JFrame{ MyFrame(){ super("JFrame with button and checkbox panel"); JPanel myPanel = new JPanel(); // create panel myPanel.setLayout(new GridLayout(0,4)); // set layout of panel myPanel.add(new JButton("submit")); // add button to panel myPanel.add(new JButton("clear")); // add another button myPanel.add(new JCheckBox("male", true)); // add checkbox to panel myPanel.add(new JCheckBox("female", false)); // another checkbox Container content = this.getContentPane(); content.setLayout(new BorderLayout()); content.add(new JTextArea(16,25),BorderLayout.CENTER); content.add(myPanel,BorderLayout.SOUTH); // add panel to frame this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } class testMyFrame{ public static void main(String[] args) { MyFrame frame= new MyFrame(); frame.setSize(300,200); frame.setVisible(true); } In this example, Jframe has two components: a JTextArea, and the JPanel, which itself contains JButtons and JCheckBoxes Putting messages in JPanels (liang 10.6) The things we’ve put in JPanels so far are interface components like JButtons and JCheckBoxes. How do those things actually get onto the screen? Jpanel has a method called public void paintComponent(Graphics g) which is called whenever a JPanel object is to be “painted” onto the screen. This is given a Graphics object g by the Java system, which represents the bit of screen where the JPanel is to be drawn. Every JPanel paints its interface components onto the screen using this Graphics object g. We don’t care what goes on inside the paintComponent method. However, if we want to put messages, or draw pictures, into our JPanels, we need to override the paintComponent method (redefine the method so that it draws what we want onto the screen). We use methods in Graphic object g for this: g.drawString(“Hello”,x,y); // draws string “hello” at location x, y in the bit of // screen represented by Graphics object g Extending JPanel and overriding paintComponent() to draw a message on a panel import javax.swing.*; import java.awt.*; class MyMessageJPanel extends JPanel{ String message; // message contents for panel int xlocation,ylocation; // message location in panel MyMessageJPanel(String msg, int x, int y){ // constructor super(); // call constructor for superclass JPanel first message = msg; xlocation = x; This paintComponent method is called whenever ylocation = y; a Jpanel is painted on the screen. We’ve changed it } so it draws our message whenever it’s called. public void paintComponent(Graphics g){ // override paintComponent super.paintComponent(g); // first do whatever paintComponent in JPanel always does g.drawString(message,xlocation,ylocation); } // next use the drawString method to draw our message in the JPanel } Using our extended JMessagePanel class MyMessageJFrame extends JFrame{ MyMessageJFrame(){ // constructor for MyMessageJFrame super("JFrame with MyMessageJPanel"); MyMessageJPanel messagePanel; messagePanel = new MyMessageJPanel("Hello world!",50,50); // create a MyMessageJPanel object that // draws "hello world!" at location 50, 50 Container content = this.getContentPane(); content.setLayout(new BorderLayout(15,15)); content.add(messagePanel,BorderLayout.CENTER); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } class testMyFrame{ // class to test out these classes public static void main(String[] args) { MyMessageJFrame frame= new MyMessageJFrame(); frame.setSize(300,300); frame.setVisible(true); } } When the MyMessageJPanel object is painted on the screen, our overridden paintComponent method will draw the message we gave to the constructor. Noticing events: A Button Click • A JFrame contains a JButton • To respond when the JButton is clicked, we create an actionListener object to listen for the action of that JButton being clicked. • That actionListener object must contain a method called • public void ActionPerformed(ActionEvent e) • The actionListener object registers with the Jbutton. • Thereafter, whenever the JButton is clicked, it calls the actionPerformed(ActionEvent e) method in the listener. • The actionPerformed (ActionEvent e) method does whatever we want to happen when the button is clicked. Listener registers with JButton count event e: Click myActionListener { actionPerformed(ActionEvent e){ JOptionPane.showMessageDialog(“Button click!”); } JOptionPane } Button Clicked! Registering a listener object with a source (a Jbutton) JButton myCountButton = new JButton(“count"); myCountListener listener = new myCountListener(); myCountButton.addListener(listener); This snippet of code creates a JButton object, then creates a myCountListener object, the registers that listener with that button (adds the listener object as a listener to the button). This registration step creates the link between the button and the listener: after this step, when the button is clicked, the actionPerformed() method in the listener object will be executed. Next: putting it all together. Making the JFrame class implement ActionListener • Any class at all can implement ActionListener, if it provides the actionPerformed() method. • Its most useful to make the class that draws our windows (our JFrames) also implement ActionListener, so that objects from that clss not only draw buttons but can respond to events on those buttons. • To do this we change our myButtonJFrame class so that it proves an actionPerformed() method as well as constructing the JFrame object. • I’ll call this new class EventButtonJFrame import javax.swing.*; import java.awt.*; import java.awt.event.*; // we need event classes class EventButtonJFrame extends Jframe implements ActionListener{ JTextArea myTextArea = new JTextArea(6,30), JButton myCountButton = new JButton("Count"); // make button JPanel buttonPanel = new JPanel(); EventButtonJFrame (){ super(“myButtonJFrame"); myCountButton.addActionListener(this); // constructor // register this class as the listener // for myCountButton buttonPanel.setLayout(new GridLayout(4,0)); buttonPanel.add(myCountButton); // add button to panel Container content = this.getContentPane(); content.setLayout(new BorderLayout()); content.add(myTextArea,BorderLayout.CENTER); content.add(buttonPanel, BorderLayout.EAST); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null,”Count button clicked!"); } } Doing something useful The actionPerformed method in the previous programs doesn’t really do anything useful. To get that method to do something useful, we could, for example, give it access to the JTextArea object created in our JFrame window. To do this, we will 1) Make the JTextArea object a generally accessible variable in the Jframe object. To do this, we put JTextArea myTextArea = new JTextArea("this is a text area",6,30); at the top of the EventButtonJFrame class definition. 2) Refer to the text in that JtextArea object in the actionPerformed method, so that when our count button is clicked, we can return some useful information. We can get the text inside the JTextArea object by saying myTextArea.getText(); (we can change that text by saying something like: myTextArea.setText(“this is the new text”); import javax.swing.*; import java.awt.*; import java.awt.event.*; Download from here // we need event classes class EventButtonJFrame extends JFrame implements ActionListener{ JTextArea myTextArea = new JTextArea("this is a text area",6,30); JButton myCountButton = new JButton("count"); // create button JPanel buttonPanel = new JPanel(); EventButtonJFrame (){ super("myButtonJFrame"); myCountButton.addActionListener(this); // add listener buttonPanel.setLayout(new GridLayout(4,0)); buttonPanel.add(myCountButton); // add button Container content = this.getContentPane(); content.setLayout(new BorderLayout()); content.add(myTextArea,BorderLayout.CENTER); // add text area content.add(buttonPanel, BorderLayout.EAST); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null, “The number of characters in JTextArea is "+myTextArea.getText().length() ); } } Whenever the count button is clicked, we get a message telling us how many characters are currently in myTextArea. // continued… class testEventButtonJFrame { public static void main(String[] args) { EventButtonJFrame frame = new EventButtonJFrame(); frame.setSize(300,200); frame.setLocation(300,330); frame.setVisible(true); } } This program will respond to a click on the count button by popping up a text message telling the user how many characters are currently entered in the JTextArea of the EventButtonJFrame object. Note that for this program to compile and run properly, it must be in a file called testEventButtonJFrame.java. This is because the class with the main method (the one that will actually run) is the testEventButtonJFrame class. A listener with two buttons (download here) If we want our actionListener to respond to more than 1 button, we have to change the actionPerformed method so that it checks which button was source of the current event. We do this as follows: Declare the two buttons as class-level variables; at top of the class, put: JButton myCountButton = new JButton("count"); JButton myClearButton = new JButton("clear"); In the constructor for the class, register both buttons with the listener (and add each button to the JPanel as required): myCountButton.addActionListener(this); myClearButton.addActionListener(this); In the actionPerformed method, check which button was the source of the current ActionEvent e, and respond accordingly: if (e.getSource().equals(myCountButton)) {…do whatever} if (e.getSource().equals(myClearButton)) {…do whatever} Colors, fonts, and drawing graphics There are lots of other things we can do with JPanels: change colors, set font sizes, etc. See Liang 10.7 and 10.5, and example 10.5, for details of these. I’ll leave them to you to read over. We have extended JPanel to MyMessageJPanel and used g.drawString() to draw a string (a message) in the panel. We can use a similar approach to draw graphics in a panel, using the predefined graphics methods g.drawline(), g.drawRect(), g.drawOval() and so on. To use these we extend JPanel to JRectPanel, for example, and in JRectPanel override paintComponent() as follows: public void paintComponent(Graphics g){ // override paintComponent super.paintComponent(g); // first do whatever paintComponent in JPanel always does g.drawRect(x,y,width,height); } // draw a rectangle at x,y with the given width and height Using the API There are a lot of details in the javax.swing and java.awt classes I’ve discussed here. I haven’t mentioned most of the abilities of these classes. You should look these classes up in the API: http://java.sun.com/j2se/ (click on ‘API specifications’ under ‘reference’, and then pick the edition of J2SE you are using: currently probably 1.4.2). You should look up javax.swing and within that look up the classes JFrame, JPanel, JComponent, JDialog and see the methods that are listed. In JComponent you will see subclasses such as JTextField, JTextArea, Jbutton, JRadioButton, JCheckBox and so on. The API explains all of the abilites of objects in these classes and how they are used. Within javax.awt you will find the layout managers and various other useful classes of objects. Conclusion • GUI construction and control is not complicated, but there are loads of details involved. • You don’t need to know all these details: my aim has been to give a general idea of what goes on in GUI construction and in event-driven programming. • If you’re interested, you can look up the various classes discussed here in the Java API: • http://java.sun.com/j2se/ (click on ‘API specifications’ under ‘reference’, and then pick the edition of J2SE you are using: currently probably 1.5.0 ). • There are very useful tutorials on JFrame use, event-driven programming and other aspects of Java programming in the API.