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
finnw
7771 gold badge5 silver badges9 bronze badges
asked Oct 13, 2011 at 18:42
\$\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.

answered Oct 15, 2011 at 15:23
\$\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
answered Oct 18, 2011 at 17:51
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.