JaVa
   

Using Our New GUI System

Now that we have created the foundation for the GUI and a custom button component, let's look at an example app that implements this GUI system. Let's first look at the complete code listing, and then we will focus on the key parts where the GUI is implemented.

Code Listing 13-8: Using the custom GUI system
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
import javax.imageio.*;
 public class ExampleApp extends JFrame implements Runnable,
 MouseListener,
 MouseMotionListener,
 GUIButtonListener
<>
 public ExampleApp()
 <>
 setTitle("Custom GUI System");
 getContentPane().setLayout(null);
 setResizable(false);
 setIgnoreRepaint(true);
 addWindowListener(new WindowAdapter()
 <>
 public void windowClosing(WindowEvent e)
 <>
 exitProgram();
 <>
 <>);
 backBuffer = new BufferedImage(DISPLAY_WIDTH, DISPLAY_HEIGHT,
 BufferedImage.TYPE_INT_RGB);
 bbGraphics = (Graphics2D)backBuffer.getGraphics();
 // add the mouse listeners
 getContentPane().addMouseListener(this);
 getContentPane().addMouseMotionListener(this);
 // initialize the GUI system...
 guiSystem = new GUISystem();
 guiSystem.setBounds(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
 GUIContainer gc = new GUIContainer();
 gc.setBounds(10, 10, 150, 250);
 gc.setBackground(Color.red);
 GUIContainer gc2 = new GUIContainer();
 gc2.setBounds(250, 10, 150, 200);
 gc2.setBackground(Color.yellow);
 // load the button images...
 BufferedImage up = null;
 BufferedImage down = null;
 BufferedImage over = null;
 try
 <>
 up = ImageIO.read(new File("up.jpg"));
 down = ImageIO.read(new File("down.jpg"));
 over = ImageIO.read(new File("over.jpg"));
 <>
 catch(IOException e)
 <>
 System.out.println(e);
 <>
 globalButton1 = new GUIButton(up, down, over);
 globalButton1.setLocation(21, 10);
 globalButton1.setSize(103, 38);
 globalButton1.setButtonListener(this);
 gc.add(globalButton1);
 GUIButton button2 = new GUIButton(up, down, over)
 <>
 public void onClick()
 <>
 System.out.println("Button 2 was clicked!");
 globalButton1.visible = !globalButton1.visible;
 <>
 <>;
 button2.setLocation(21, 110);
 button2.setSize(103, 38);
 gc.add(button2);
 globalButton2 = new GUIButton(up, down, over);
 globalButton2.setLocation(400, 300);
 globalButton2.setSize(103, 38);
 globalButton2.setButtonListener(this);
 guiSystem.add(globalButton2);
 guiSystem.add(gc);
 guiSystem.add(gc2);
 setVisible(true);
 Insets insets = getInsets();
 DISPLAY_X = insets.left;
 DISPLAY_Y = insets.top;
 resizeToInternalSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
 <>
 public void resizeToInternalSize(int internalWidth, int
 internalHeight)
 <>
 Insets insets = getInsets();
 final int newWidth = internalWidth + insets.left + insets.right;
 final int newHeight = internalHeight + insets.top + insets.bottom;
 Runnable resize = new Runnable()
 <>
 public void run()
 <>
 setSize(newWidth, newHeight);
 validate();
 <>
 <>;
 if(!SwingUtilities.isEventDispatchThread())
 <>
 try
 <>
 SwingUtilities.invokeAndWait(resize);
 <>
 catch(Exception e) <><>
 <>
 else
 resize.run();
 <>
 public void run()
 <>
 Thread thisThread = Thread.currentThread();
 while(loop==thisThread)
 <>
 render(bbGraphics);
 Graphics g = getGraphics();
 g.drawImage(backBuffer, DISPLAY_X, DISPLAY_Y, null);
 g.dispose();
 try
 <>
 Thread.sleep(20);
 <>
 catch(InterruptedException e) <>/* ignore */<>
 <>
 System.out.println("Program Exited");
 dispose();
 System.exit(0);
 <>
 public void render(Graphics g)
 <>
 g.clearRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);
 guiSystem.render(g);
 <>
 public void exitProgram()
 <>
 loop = null;
 <>
 // mouse handling...
 public void mouseDragged(MouseEvent e) <> guiSystem.handleMouse(e); <>
 public void mouseMoved(MouseEvent e) <> guiSystem.handleMouse(e); <>
 public void mouseClicked(MouseEvent e) <> guiSystem.handleMouse(e); <>
 public void mousePressed(MouseEvent e) <> guiSystem.handleMouse(e); <>
 public void mouseReleased(MouseEvent e) <> guiSystem.handleMouse(e); <>
 public void mouseEntered(MouseEvent e) <> guiSystem.handleMouse(e); <>
 public void mouseExited(MouseEvent e) <> guiSystem.handleMouse(e); <>
 // end of mouse handling
 public void buttonClicked(GUIButton source)
 <>
 if(source == globalButton1)
 <>
 System.out.println("The globalButton 1 was clicked");
 <>
 else if(source == globalButton2)
 <>
 System.out.println("The globalButton 2 was clicked");
 <>
 <>
 public static void main(String args[])
 <>
 ExampleApp app = new ExampleApp();
 app.loop = new Thread(app);
 app.loop.start();
 <>
 private Thread loop;
 private BufferedImage backBuffer;
 private Graphics2D bbGraphics;
 private final int DISPLAY_X; // value assigned in constructor
 private final int DISPLAY_Y; // value assigned in constructor
 private static final int DISPLAY_WIDTH = 640;
 private static final int DISPLAY_HEIGHT = 480;
 private GUIButton globalButton1;
 private GUIButton globalButton2;
 private GUISystem guiSystem;
<>



Java End example

Here is a screen shot of it in action:

Java Click To expand
Screenshot-6: Custom GUI example

If you want to play with the example, try holding the mouse down over a button, moving the mouse around, and then releasing it back on the originally pressed down button (cool eh?). First we define our GUISystem and two GUIButtons as members of our main class using the following code:

private GUIButton globalButton1;
private GUIButton globalButton2;
private GUISystem guiSystem;


Also note that our main class implements the GUIButtonListener interface so we have to declare the buttonClicked method in the class, which we will look at soon. The first important thing to do is actually create our GUISystem object, so we just call the default constructor and set the width and height (bounds) to the same as our app window, which can be seen in the following line of code:

guiSystem = new GUISystem();
guiSystem.setBounds(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT);


Next, create two GUIContainers and set the first to red and the second to yellow, just so we can determine which is which when we run the example. This can be seen here:

GUIContainer gc = new GUIContainer();
gc.setBounds(10, 10, 150, 250);
gc.setBackground(Color.red);
GUIContainer gc2 = new GUIContainer();
gc2.setBounds(250, 10, 150, 200);
gc2.setBackground(Color.yellow);


We then load in three JPEG images to represent the three possible states of the buttons (up, over, and down). This is done simply by using ImageIO, which we learned about in . The code to load the images can be seen here:

BufferedImage up = null;
BufferedImage down = null;
BufferedImage over = null;
try
<>
 up = ImageIO.read(new File("up.jpg"));
 down = ImageIO.read(new File("down.jpg"));
 over = ImageIO.read(new File("over.jpg"));
<>
catch(IOException e)
<>
 System.out.println(e);
<>


Once we have the images loaded in, we can then create our first button (called globalButton1) by calling the constructor with the three images as parameters.

globalButton1 = new GUIButton(up, down, over);


Then, as the GUIButton extends the GUIComponent class, we can call the setLocation and setSize methods to position it (remembering the position will be relative to the container that we place it inside). This can be seen here:

globalButton1.setLocation(21, 10);
globalButton1.setSize(103, 38);


For this button, it will be placed 21 pixels from the left of the container and 10 pixels down from the top of the container. Next we call the setButtonListener method to state that this class will implement the buttonClicked method (declared in the GUIButtonListener interface) and hence handle any events that should occur when the globalButton1 is clicked. This can be seen here:

globalButton1.setButtonListener(this);


Finally, we call the add method of the first (red) container, passing our globalButton1 object as a parameter. Hence, our button will then become a child of this container. This can be seen here:

gc.add(globalButton1);


We then add another button called button2 to the same container in a different location; however, we implement the handling code for the clicking of this button slightly differently. Instead of using the setButtonListener method, as we did with the last button, this time we override the GUIButton onClick method on the fly when we create the instance of the GUIButton object, placing the click-handling code directly inside the onClick method. This can be seen here:

GUIButton button2 = new GUIButton(up, down, over)
 <>
 public void onClick()
 <>
 System.out.println("Button 2 was clicked!");
 globalButton1.visible = !globalButton1.visible;
 <>
 <>;


When button2 is clicked, it will toggle the visibility of globalButton1. Neat, eh? We then set the location and size for button2 and add it to the first container, as we did with the previous button. Next we declare another button called globalButton2 and set the listener to be this class also, so we will have to handle two different buttons in the same class. Also, instead of adding this new button into one of the two containers, we add it directly to our GUISystem object guiSystem, as it is a container (the top-level container). After this, we then add the two containers and that's all there is to the setup part. For rendering the GUI, all we need to do is place a call to the GUISystem render method in our main game loop, passing in the Graphics object that we wish to use to render it.

guiSystem.render(g);


As you know from before, the GUISystem class needs to also handle the mouse events for the GUI, so we need to pass this information into our guiSystem object every time an event occurs. To do this, we simply implement the MouseListener and MouseMotionListener interfaces into our main class, and then for each of the methods that we have to declare, we call the handleMouse method of the guiSystem object, passing in the MouseEvent object. Note, however, that these mouse events are not synchronized with the main loop, and hence they are not synchronized with the GUI system, which is being rendered from the main loop. This is easy to change, however, if you require it to be synchronized by using the Event Processor that we discussed in detail in . Remember also that we need to add the MouseListener and MouseMotionListener to the content pane (not the JFrame), as this gives us mouse coordinates relative to the internal area inside the window's borders, which is seen in the following two lines of code:

getContentPane().addMouseListener(this);
getContentPane().addMouseMotionListener(this);


Finally, let's look at how we deal with the GUIButtonListener method buttonClicked, which we have to implement. As you know, when a button is clicked and it has an associated listener, it will pass itself as a parameter to the listener's buttonClicked method in the form of a GUIComponent, so we can use this to test which button the click has come from by simply comparing the references for equality. The entire buttonClicked method can be seen here for reference:

public void buttonClicked(GUIButton source)
<>
 if(source == globalButton1)
 <>
 System.out.println("The globalButton 1 was clicked");
 <>
 else if(source == globalButton2)
 <>
 System.out.println("The globalButton 2 was clicked");
 <>
<>


In the previous chapter, we defined a screen system with the base class TemplateScreen, which was used to handle many screens from the game framework from one currentScreen reference. In reality, the GUISystem we have just developed is an advanced version of the screen system defined in the previous chapter, incorporating a solid component-container hierarchy. This could be used even for the structure of your actual game screens by overriding the render methods and supplying your in-game rendering code yourself, all being called through the original top-level GUISystem object when added to it.

JaVa
   
Comments