Chris Pollett>Old Classes>PIC 20, Winter 2000>Hw6>Hw6 Solutions
// Program Name: AqFrame.java
//
// Purpose: This applications displays an Aquarium. A menu bar is
// used to add fish, seaweed to the auqarium. There is
// also an option to clear the aquarium and to play sound.
//
// Known Bugs: The factorization of the classes AqComponent, Fish, Seaweed
// should be improved.
//
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
public class AqFrame extends JFrame
{
static final int FISH=0,SEAWEED =1; //internal constants
// used to keep track of type of
// an AqComponent. Would be better
// to use instanceof but forgot to
// mention in class.
Container c;
AudioClip fishMusic;
JMenuBar bar;
JMenu optionsMenu, newMenu;
JMenuItem fishItem,seaweedItem,clearItem;
JCheckBoxMenuItem soundItem;
Aquarium aq;
MyTimer redrawTimer;
ImageIcon fish[],seaweed[];
JApplet applet;
// Method Name: AqFrame
//
// Purpose: creates an instance of AqFrame, loads in sounds,
// sets up menus, creates the Aquarium,
// starts movement timer for things in the Aquarium
// loads in the different fish and seaweed images.
//
public AqFrame()
{
super("AqFrame");
try{
fishMusic =
Applet.newAudioClip(
new
URL("http://www.math.ucla.edu/~cpollett/20.1.00w/sparcle.wav"));
}
catch(MalformedURLException e)
{
System.err.println("no music");
}
setUpMenus();
c=getContentPane();
c.setLayout(new BorderLayout());
aq = new Aquarium();
c.add(aq);
redrawTimer = new MyTimer(100,new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
aq.move();
c.repaint();
}
}
);
redrawTimer.start();
setUpFishSeaweed();
setSize(aq.getPreferredSize());
show();
}
// Method Name: setUpMenus
//
// Purpose: set up the menubar, menus, and their items
// for the AqFrame. Also add their Listeners
//
void setUpMenus()
{
fishItem =new JMenuItem("Fish");
fishItem.setMnemonic('F');
fishItem.addActionListener(new AqListener(FISH));
seaweedItem =new JMenuItem("Seaweed");
seaweedItem.setMnemonic('S');
seaweedItem.addActionListener(new AqListener(SEAWEED));
soundItem =new JCheckBoxMenuItem("Sound");
soundItem.setMnemonic('d');
soundItem.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent e)
{
if(soundItem.isSelected())
fishMusic.loop();
else fishMusic.stop();
}
}
);
clearItem =new JMenuItem("Clear");
clearItem.setMnemonic('C');
clearItem.addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent e)
{
aq.clear();
}
}
);
newMenu =new JMenu("New");
newMenu.setMnemonic('N');
newMenu.add(fishItem);
newMenu.add(seaweedItem);
optionsMenu =new JMenu("Options");
optionsMenu.setMnemonic('O');
optionsMenu.add(newMenu);
optionsMenu.addSeparator();
optionsMenu.add(clearItem);
optionsMenu.add(soundItem);
bar= new JMenuBar();
bar.add(optionsMenu);
setJMenuBar(bar);
}
// Method Name: setUpFishSeaweed
//
// Purpose: instantiates the ImageIcons for the
// kinds of fish and seaweed available in the aquarium
//
public void setUpFishSeaweed()
{
fish = new ImageIcon[3];
seaweed = new ImageIcon[3];
fish[0] =new ImageIcon("fish0.gif");
fish[1] =new ImageIcon("fish1.gif");
fish[2] =new ImageIcon("fish2.gif");
seaweed[0] =new ImageIcon("seaweed0.gif");
seaweed[1] =new ImageIcon("seaweed1.gif");
seaweed[2] =new ImageIcon("seaweed2.gif");
}
// Method Name: main
//
// Purpose: create an instance of AqFrame and add a
// window listener. Might be better to put
// this in a wrapper class.
//
public static void main (String args[])
{
AqFrame app = new AqFrame();
app.addWindowListener(
new WindowAdapter(){
public void windowClosing ( WindowEvent e)
{
System.exit(0);
}
}
);
}
// Class Name: AqListener
//
// Purpose: listener attached to Fish and Seaweed MenuItems
// It creates a AqDialog window to allow the
// user to add this AqComponent
//
class AqListener implements ActionListener
{
int type;
public AqListener(int t)
{type =t;}
public void actionPerformed(ActionEvent e)
{
AqDialog ad =new AqDialog(type);
}
}
// Class Name: AqDialog
//
// Purpose: class used to create dialog window used to add
// AqComponents to aquarium
//
class AqDialog extends JFrame
{
Container c;
String names[]; //names of images of AqComponent for JList
JList imList; //images for AqComponent
JSlider xSlider,ySlider,scaleSlider,xVelSlider;
JSlider yVelSlider;
int type;
// Class Name: AqDialog
//
// Purpose: create an instance of AqDialog window
// sets up the sliders for fish or seaweed subcases
// and set up JList of available images. Also
// set up Create and Cancel buttons and their listeners
//
public AqDialog(int t)
{
super("AqComponent Dialog");
type=t;
c = getContentPane();
switch(type)
{
case FISH:
c.setLayout(new GridLayout(6,1,5,5));
names =new String[fish.length];
for(int i=0; i<fish.length; i++)
{names[i]="fish"+i;}
break;
case SEAWEED:
c.setLayout(new GridLayout(5,1,5,5));
names =new String[seaweed.length];
for(int i=0; i<seaweed.length; i++)
{names[i]="seaweed"+i;}
break;
default:
names = new String[1];
names[0] = "default";
}
imList =new JList(names);
imList.setVisibleRowCount(2);
imList.setSelectedIndex(0);
imList.setSelectionMode(
ListSelectionModel.SINGLE_SELECTION);
JPanel listPanel = new JPanel();
listPanel.setLayout(new FlowLayout
(FlowLayout.CENTER));
listPanel.add(new JScrollPane(imList));
c.add(listPanel);
xSlider=addSlider("X Location:");
switch(type)
{
case FISH:
ySlider=addSlider("Y Location: ");
xVelSlider=addSlider("X Velocity:" );
yVelSlider=addSlider("Y Velocity:");
break;
case SEAWEED:
scaleSlider=addSlider("Seaweed Size:");
break;
}
JButton cancel = new JButton("Cancel");
cancel.addActionListener( new ActionListener()
{ public void actionPerformed(ActionEvent e)
{
dispose();
}
}
);
JButton create = new JButton("Create");
create.addActionListener( new ActionListener()
{ public void actionPerformed(ActionEvent e)
{
switch(type)
{
case AqFrame.FISH:
aq.add( new Fish(
fish[imList.getSelectedIndex()],
(float)(xSlider.getValue()/100.0),
(float)(ySlider.getValue()/100.0),
(float)(xVelSlider.getValue()/10000.0),
(float)(yVelSlider.getValue()/10000.0)));
break;
case AqFrame.SEAWEED:
aq.add( new Seaweed(
seaweed[imList.getSelectedIndex()],
(float)(xSlider.getValue()/100.0),
(float)1.0,
(float)(scaleSlider.getValue()/100.)));
break;
}
AqFrame.this.c.repaint();
dispose();
}
}
);
JPanel buttonPanel =new JPanel();
buttonPanel.setLayout(new FlowLayout());
buttonPanel.add(cancel);
buttonPanel.add(create);
c.add(buttonPanel);
switch(type)
{
case FISH:
setSize(250,300);
break;
case SEAWEED:
setSize(250,250);
break;
}
show();
}
// Class Name: addSlider
//
// Purpose: sets up a single slider to add to window
//
JSlider addSlider(String s)
{
JPanel sliderPanel = new JPanel();
sliderPanel.setLayout(new FlowLayout());
JLabel label = new JLabel(s);
sliderPanel.add(label);
JSlider slider =new JSlider(SwingConstants.HORIZONTAL,
0,100,10);
slider.setValue(50);
sliderPanel.add(slider);
c.add(sliderPanel);
return slider;
}
}//end AqDialog inner class
} //end AqFrame
// Program Name: Aquarium.java
//
// Purpose: This is the JPanel where the actual aquarium is drawn
// it also contains a Vector of the AqComponents
// on the aquarium
//
// Known Bugs: None
//
//
import java.awt.*;
import javax.swing.*;
import java.util.*;
public class Aquarium extends JPanel
{
Vector marineLife;
Color lightBlue = new Color(130,220,255);
Color sand =new Color(255,255,170);
BasicStroke aqStroke = new BasicStroke(20.0f);
// Method Name: Aquarium
//
// Purpose: set-up up panel and Vector of AqComponents
//
public Aquarium()
{
super();
marineLife = new Vector();
}
// Method Name: paintComponent
//
// Purpose: draws the aquarium and the fish on it
//
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
Dimension d= getSize();
int height = d.height;
int width = d.width;
int topX,topY,bottomX, bottomY;
int skyHeight = (int)(height*.3);
int sandHeight = (int)(height*.8);
int imWidth,imHeight;
Image im;
AqComponent tmp;
Rectangle screen = new Rectangle(width,height);
g2d.setPaint(Color.blue);
g2d.fill(screen);
g2d.setPaint(sand);
g2d.fill(new Rectangle(0,sandHeight,width,height));
g2d.setPaint(Color.black);
g2d.setStroke(aqStroke);
g2d.draw(screen);
g2d.setPaint(lightBlue);
g2d.fill(new Rectangle(0,0,width,skyHeight));
g2d.setPaint(Color.black);
g2d.fill(new Rectangle(0,skyHeight-10,10,skyHeight));
g2d.fill(new Rectangle(width-10,skyHeight-10,width,skyHeight));
for(int i=0; i < marineLife.size(); i++)
{
tmp = (AqComponent)marineLife.get(i);
im = tmp.icon.getImage();
imWidth = (int)(width*.10*tmp.scale);
imHeight = (int)(height*.20*tmp.scale);
topX = (int)((width-20-imWidth)*tmp.xLoc+10);
bottomX = imWidth;
topY = skyHeight+
(int)((sandHeight-skyHeight-imHeight)*tmp.yLoc);
bottomY = imHeight;
g2d.drawImage(im,topX,topY,bottomX,bottomY,this); }
}
// Method Name: getPreferredSize()
//
// Purpose: returns standard size of aquarium
//
public Dimension getPreferredSize()
{
return new Dimension(500,300);
}
// Method Name: getMinimumSize()
//
// Purpose: returns minimum size to draw aquarium
//
public Dimension getMinimumSize()
{
return new Dimension(100,100);
}
// Method Name: add
//
// Purpose: add a new AqComponent to aquarium
//
public void add(AqComponent aq)
{
marineLife.add(aq);
}
// Method Name: clear
//
// Purpose: deletes all the AqComponents from the aquarium
//
public void clear()
{
marineLife.clear();
}
// Method Name: move
//
// Purpose: move all the AqComponents on the aquarium one step
// accroding to their respective move methods
//
public void move()
{
AqComponent lifeForm;
for(int i=0; i < marineLife.size();i++ )
{
lifeForm = (AqComponent)marineLife.get(i);
lifeForm.move();
}
}
}
// Program Name: MyTimer.java
//
// Purpose: this class acts like the usual java class Timer. We
// have written this class by extending Thread although
// it might be better to try to implement the Runnable interface
// instead so we could have a stop() method which doesn't give
// deprecated stop method
//
//
// Known Bugs: None
//
//
import java.awt.event.*;
public class MyTimer extends Thread
{
ActionListener act;
int sleepTime;
boolean running;
// Method Name: MyTimer
//
// Purpose:
//
public MyTimer(int sleep, ActionListener action)
{
sleepTime =sleep;
act =action;
running=true;
}
// Method Name: isRunning
//
// Purpose: returns if the timer is currently calling
// tact.actionPreformed every sleepTime milliseconds.
//
public boolean isRunning()
{ return running;}
// Method Name: run
//
// Purpose: The run method just loops and does the
// following two things: (1)sleeps the time specified in the
// the constructor.
// (2) call act.actionPerformed
//
public void run()
{
try{
while(true)
{
running =true;
while(running)
{
act.actionPerformed(
new ActionEvent(this,
ActionEvent.ACTION_PERFORMED,
"timer")
);
Thread.sleep( sleepTime );
}
while(!running)
{ Thread.sleep(sleepTime);}
}//end while true
}
catch (InterruptedException e)
{
System.err.println(e.toString());
running =false;
}
}
// Method Name: restart
//
// Purpose: start the timer timer again
//
public void restart()
{
running =true;
}
// Method Name: myStop
//
// Purpose: stop the timer
//
public void myStop()
{
running=false;
}
}
// Program Name: AqComponent.java
//
// Purpose: Contains the base class for object that can appear in the aquarium
// such object have an associated ImageIcon, an x and y location
// and a scale (which is not set in the constructor, but is settable
// in constructors of subclasses)
//
//
// Known Bugs: might be better to make this class abstract
//
//
import java.awt.*;
import javax.swing.*;
public class AqComponent
{
public ImageIcon icon;
public float xLoc, yLoc;
public float scale=(float)0.5;
// Method Name: Aqcomponent
//
// Purpose: createa an instance of AqComponent. Sets
// the image and x and y location
//
public AqComponent(ImageIcon i, float x, float y)
{
icon=i;
xLoc=x;
yLoc=y;
}
// Method Name: move
//
// Purpose: stub method used by subclasses
//
public void move()
{}
}
// Program Name: Seaweed.java
//
// Purpose: This class is a subclass of AqComponent which
// is used for object on the seabed, also unlike fish
// size can be rescaled
//
//
// Known Bugs: None
//
//
import java.awt.*;
import javax.swing.*;
public class Seaweed extends AqComponent
{
// Method Name: Seaweed
//
// Purpose: create an instance of Seaweed. Unlike
// AqComponent can set scale in constructor
//
public Seaweed(ImageIcon i, float x, float y, float s)
{
super(i,x,y);
scale=s;
}
}
// Program Name: Fish.java
//
// Purpose: this subclass of AqComponent is used to create
// moving objects in the aquarium
//
// Known Bugs: None
//
//
import java.awt.*;
import javax.swing.*;
public class Fish extends AqComponent
{
public float xVel, yVel;
// Method Name: Fish
//
// Purpose: creates an instance of Fish. Unlike AqComponent
// specify x and y velocity in the constructor
//
public Fish(ImageIcon i, float x, float y, float xV, float yV)
{
super(i,x,y);
xVel=xV;
yVel=yV;
}
// Method Name: move()
//
// Purpose: changes the fishes location by xVel and yVel
// and if bumps into side of tank reverses direction of motion
public void move()
{
xLoc +=xVel;
yLoc +=yVel;
if (xLoc > 1 || xLoc<0) //handles horizontal boundaries
xVel = - xVel;
if(yLoc > 1 || yLoc <0) //handles vertical boundaries
yVel = - yVel;
}
}