// SampleGUI.java // David Kosbie, 15-111/AB, Spring 2007 // Last modified: 8-Feb-07 /* Standard disclaimer: As usual with sample code designed (often in class!) to demonstrate specific issues, the style may not be perfect (it is especially lacking comments and some top-down design), and there may even be a bug or two lurking in the code! Note: you are not responsible for the code in the bottom half of this file (there is a comment clearly indicating where this occurs). This code lets you create Java GUI applications! It does so by providing you with two helper classes: IntroCsApplication and IntroCsComponent Typically, you would create one subclass of each -- your main would typically just create a single instance of your application subclass, and that would create one or more instances of your component subclass. The sample code below demonstrates how to do this. Here are the API's. The dot-dot-dot's indicate methods that you should OVERRIDE, the semicolons are methods you can call but typically would not override. ////////////////////////////////////////////// // IntroCsApplication API ////////////////////////////////////////////// public class IntroCsApplication { public void init() { ... } public Component add(Component c); public void addButtons(String... buttonLabels) { ... } public void onButtonPressed(String label) { ... } public void startTimer(int timerDelay); public void onTimer() { ... } public void beep(); } ////////////////////////////////////////////// // IntroCsComponent API ////////////////////////////////////////////// class IntroCsComponent { public void paintComponent(Graphics page) { ... } public void onMousePressed(int x, int y) { ... } public void onMouseDragged(int x, int y) { ... } public void onMouseReleased(int x, int y) { ... } public void startTimer(int timerDelay); public void onTimer() { ... } public void onResized() { ... } public void beep(); } Note that the helper classes definitely abstract away some of the complexities of writing Swing applications (that's why they are there, after all!). If you want to see what's going on, well, by all means: the code is at the bottom of this file, so go take a look! */ import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; public class SampleGUI extends IntroCsApplication { public static void main(String[] args) { new SampleGUI(); } private CustomComponent circlePainter; public void init() { add(circlePainter = new CustomComponent()); addButtons("change color", "beep"); circlePainter.startTimer(100); } public void onButtonPressed(String label) { if (label.equals("change color")) circlePainter.changeColor(); else if (label.equals("beep")) beep(); } } class CustomComponent extends IntroCsComponent { private int circleRadius = 50; private Point circleCenter = new Point(circleRadius,circleRadius); private String message = "Press a key!"; private String sizeMessage = ""; // set in onResized() private Color circleColor = Color.blue; public void paintComponent(Graphics page) { // paint the circle int left = circleCenter.x - circleRadius; int top = circleCenter.y - circleRadius; page.setColor(circleColor); page.fillOval(left,top,2*circleRadius,2*circleRadius); // paint the message page.setColor(Color.red); page.setFont(new Font("Arial",Font.BOLD,36)); page.drawString(message,100,100); // paint the sizeMessage page.setColor(Color.black); page.setFont(new Font("Times",Font.ITALIC,12)); page.drawString(sizeMessage,20,20); } // if circleCenter's x or y are off the board, this wraps // them around to the other side private void wrapCircleCenter() { int width = this.getWidth(); int height = this.getHeight(); if (circleCenter.x < -circleRadius) circleCenter.x += width; else if (circleCenter.x > width+circleRadius) circleCenter.x -= width; if (circleCenter.y < -circleRadius) circleCenter.y += height; else if (circleCenter.y > height+circleRadius) circleCenter.y -= height; } // Sets the circle's location to (x,y) and redraws the component public void moveCircle(int x, int y) { circleCenter.x = x; circleCenter.y = y; wrapCircleCenter(); } // Moves the circle's location according to the keystroke (up,down // left, right) and redraws the component public void moveCircle(int keyCode) { int speed = 5; // # of pixels to move on each arrow press switch (keyCode) { case KeyEvent.VK_UP: circleCenter.y -= speed; break; case KeyEvent.VK_DOWN: circleCenter.y += speed; break; case KeyEvent.VK_LEFT: circleCenter.x -= speed; break; case KeyEvent.VK_RIGHT: circleCenter.x += speed; break; default: beep(); } wrapCircleCenter(); } public void changeColor() { circleColor = (circleColor == Color.blue) ? Color.green : Color.blue; } //////////////////////////////////////////// // Event Handlers //////////////////////////////////////////// public void onMousePressed(int x, int y) { moveCircle(x,y); } public void onMouseDragged(int x, int y) { moveCircle(x,y); } public void onMouseReleased(int x, int y) { moveCircle(x,y); } public void onKeyPressed(KeyEvent e, int keyCode, char keyChar) { message = KeyEvent.getKeyText(keyCode) + " (" + keyChar + ")"; moveCircle(keyCode); } public void onTimer() { moveCircle(KeyEvent.VK_RIGHT); } public void onResized() { this.sizeMessage = "Component size = " + this.getWidth() + " x " + this.getHeight(); } } //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // You are not (yet) responsible for the code below here! //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// class IntroCsComponent extends JComponent { public IntroCsComponent() { this.addEventListeners(); this.setPreferredSize(new Dimension(400,400)); } // override these methods to respond to events public void onTimer() { } public void onMousePressed(int x, int y) { } public void onMouseDragged(int x, int y) { } public void onMouseReleased(int x, int y) { } public void onKeyPressed(KeyEvent e, int keyCode, char keyChar) { } public void onResized() { } public void beep() { Toolkit.getDefaultToolkit().beep(); } public void startTimer(final int timerDelay) { // use invokeLater to allow window to become visible before starting // to be initialized in subclasses before calling init() method javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { new javax.swing.Timer(timerDelay, new ActionListener() { public void actionPerformed(ActionEvent evt) { onTimer(); repaint(); } }).start(); } }); } protected void addEventListeners() { // add actions in response to mouse events addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { onMousePressed(e.getX(),e.getY()); repaint(); } public void mouseReleased(MouseEvent e) { onMouseReleased(e.getX(),e.getY()); repaint(); } }); // and for mouse motion events addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { onMouseDragged(e.getX(),e.getY()); repaint(); } }); // and for keyboard events addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { onKeyPressed(e,e.getKeyCode(),e.getKeyChar()); repaint(); } }); // and for component events addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { onResized(); repaint(); } }); } } class IntroCsApplication extends JFrame { public void init() { // override this method to add your buttons, objects, etc } public void onTimer() { } // This method lives outside the anonymous inner class Runnable // so that getClass() returns this class (in fact, the overridden subclass) protected String makeTitle() { return this.getClass().getName(); } public IntroCsApplication() { super(); Container cp = this.getContentPane(); cp.setBackground(Color.white); cp.setLayout(new BoxLayout(cp,BoxLayout.Y_AXIS)); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // use invokeLater to allow instance variables (like buttonLabels[]) // to be initialized in subclasses before calling init() method javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { init(); // override this method! pack(); if (focusComponent != null) // send key events to this component! focusComponent.requestFocusInWindow(); setTitle(makeTitle()); setVisible(true); } }); } private IntroCsComponent focusComponent = null; public Component add(Component c) { Component result = super.add(c); if (c instanceof IntroCsComponent) { // the first one added gets the keyboard by default if (focusComponent == null) { focusComponent = (IntroCsComponent) c; // send key events to this component! focusComponent.requestFocusInWindow(); } } return result; } public void onButtonPressed(String label) { System.out.printf("onButtonPressed(%s)\n",label); } public void addButtons(String... buttonLabels) { JPanel buttons = new JPanel(); buttons.setLayout(new BoxLayout(buttons,BoxLayout.X_AXIS)); for (int i=0; i