2
\$\begingroup\$

This is a sample program that I intend to post as part of a series of beginner level Java tutorials. Please provide any feedback on improvements that would make example more clear or illustrate/emphasize best practices. The example drawns a ball object to a panel on a mouse click and then moves it randomly with another mouse click.

Class BouncingBall

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

/*
 * this class will add a red ball to a canvas or play area, when a button is clicked and 
 * then move about randomly when another button is clicked
 * 
 * jmergenthaler 10/1/2011
*/

public class BouncingBall {

private JFrame frame = new JFrame();
private JPanel actionPanel = new JPanel();
private JPanel playarea = new JPanel();
private JButton btnNew = new JButton("Add Red Ball");
private JButton movebtn = new JButton("Move it");

//constructor
BouncingBall(){
    buildTheGUI();  
}

public void buildTheGUI(){
    frame.setLayout( new BorderLayout());
    btnNew.addActionListener( new ButtonClickHandler() );
    movebtn.addActionListener( new MoveButtonClickHandler() );
    actionPanel.add(btnNew);
    actionPanel.add(movebtn);
    frame.add(BorderLayout.NORTH,actionPanel);
    frame.add(BorderLayout.SOUTH,playarea);
    frame.setSize(500, 500);
    frame.setVisible(true); 
}

public static void main(String args[]){
    new BouncingBall();
}

class ButtonClickHandler implements ActionListener{
    public void actionPerformed(ActionEvent e){ 

        //create initial Ball object add to the frame
        frame.add(new Ball() );
        //draw
        frame.validate();
    }
}

class MoveButtonClickHandler implements ActionListener{
    public void actionPerformed(ActionEvent e){

        //polymorphic behavior, calling the Ball constructor differently
        frame.add(new Ball(1) );
        //redraw
        frame.validate();
    }
}// end class MoveButtonClickHandler
}//end class BouncingBall

class 2 - the Ball class

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JPanel;

class Ball extends JPanel{

//private instance variables avail. only avail to methods in this class
private int x,y,w,h;

//constructor
Ball(){
    this.x = 200;
    this.y = 200;
    this.w = 100;
    this.h = 100;
}   

//constructor with different behavior
Ball(int a){
    Random rand = new Random();

    this.w = 100;
    this.h = 100;
    this.x = rand.nextInt(300);
    this.y = rand.nextInt(300);
}

//draw the ball
//@override
public void paintComponent(Graphics g){
    super.paintComponent(g);
    g.setColor(Color.RED);
    g.fillOval(x, y, h, w); 
}

}//end class Ball
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$
  • Swing GUIs should be built inside the EDT, e.g. using SwingWorker.invokeLater(). See http://leepoint.net/JavaBasics/gui/gui-commentary/guicom-main-thread.html for details.
  • I think calling a JPanel descendant "Ball" is just confusing. If you really want that a panel can handle only one Ball, call it BallPanel or so. But it would be more flexible if you had a DrawPanel, which accepts a number of objects to draw, and the objects themself. That makes it much easier to extend the system later:

.

public class DrawPanel {
   private List<Drawable> drawables = new ArrayList<Drawable>();
   ...
   public void addDrawable(Drawable d) { drawables.add(d); }

   public void paintComponent(Graphics g){
     super.paintComponent(g);
     for(Drawable d : drawables) {
        d.draw(g, getWidth(), getHeight()); 
     }  
   }
   ...
   //for animation a timer task, calling Drawable.update
}

interface Drawable {
   public void draw(Graphics g, int width, int height);

   //when you need animation  
   public void update(long ms);  
}

public class Ball implements Drawable {
  ...
}

This is only one possibility to split view and model, the "right" way depends on your needs. But keeping both model and view in one class is a receipt for trouble.

\$\endgroup\$
2
  • \$\begingroup\$ help me with this, im an old procedural programmer... \$\endgroup\$ Commented Oct 18, 2011 at 17:19
  • \$\begingroup\$ Class DrawPanel would be the "view", Class Ball and Interface Drawable would be the "model"? I am trying to implement the feedback provided, but i am struggling with parts of it \$\endgroup\$ Commented Oct 18, 2011 at 17:35
0
\$\begingroup\$

@Landei - this is what i have now. I am trying to understand and implement your ideas...

import java.awt.Graphics;

public interface Drawable {

   //this is an interface method - no body
   public void draw(Graphics g, int w, int h);  

   public void update(long ms);

}

Next class

import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

class Ball implements Drawable{

//private instance variables avail. only avail to methods in this class
private int x,y,w,h;

//constructor
Ball(){
    this.x = 200;
    this.y = 200;
    this.w = 100;
    this.h = 100;
}   

//constructor with different behavior
Ball(int a){
    Random rand = new Random();

    this.w = 100;
    this.h = 100;
    this.x = rand.nextInt(300);
    this.y = rand.nextInt(300);
}


@Override
public void draw(Graphics g, int w, int h) {
    //super.paintComponent(g);
    g.setColor(Color.red);
    g.fillOval(x, y, w, h);     
}

@Override
public void update(long ms) {
    // TODO Auto-generated method stub

}

}//end class Ball

3rd class

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class DrawPanel {

private ArrayList drawable = new ArrayList<Drawable>();
private JFrame frame = new JFrame();
private JPanel actionPanel = new JPanel();
private JPanel playarea = new JPanel();
private JButton btnNew = new JButton("Add Red Ball");
private JButton movebtn = new JButton("Move it");

//constructor
DrawPanel(){
    buildTheGUI();  
}

public void addDrawable(Drawable d){
    drawable.add(d);
}

public void buildTheGUI(){
    frame.setLayout( new BorderLayout());
    btnNew.addActionListener( new ButtonClickHandler() );
    movebtn.addActionListener( new MoveButtonClickHandler() );
    actionPanel.add(btnNew);
    actionPanel.add(movebtn);
    frame.add(BorderLayout.NORTH,actionPanel);
    frame.add(BorderLayout.SOUTH,playarea);
    frame.setSize(500, 500);
    frame.setVisible(true); 
}

public static void main(String args[]){
    //launch GUI on the EDT (event dispatch thread) - per best practice
    SwingUtilities.invokeLater(new Runnable(){
        public void run(){
            new DrawPanel();
        }
    }
);

}

class ButtonClickHandler implements ActionListener{
    public void actionPerformed(ActionEvent e){ 

        //create initial Ball object add to the frame
        addDrawable( new Ball() ) ;
        //draw
        frame.validate();
    }
}

class MoveButtonClickHandler implements ActionListener{
    public void actionPerformed(ActionEvent e){

        //polymorhic behavior, calling the Ball constructor differently
        addDrawable( new Ball(1) ) ;
        //redraw
        frame.validate();
    }
}// end class MoveButtonClickHandler
}//end class DrawPanel
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.