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
2 Answers 2
- 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 itBallPanel
or so. But it would be more flexible if you had aDrawPanel
, 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.
-
\$\begingroup\$ help me with this, im an old procedural programmer... \$\endgroup\$jamesTheProgrammer– jamesTheProgrammer2011年10月18日 17:19:51 +00:00Commented 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\$jamesTheProgrammer– jamesTheProgrammer2011年10月18日 17:35:04 +00:00Commented Oct 18, 2011 at 17:35
@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