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; } }