In: Computer Science
Object-Oriented Design and Patterns in Java (the 3rd Edition Chapter 5)
- Create a directory named as the question number and save the required solutions in the directory.
- Each problem comes with an expected tester name. In the directory for a given problem, including the tester and all java classes successfully run this tester.
Exercise 5.2
ObserverTester.java - Improve Exercise 1 by making the graph view editable. Attach a mouse listener to the panel that paints the graph. When the user clicks on a point, move the nearest data point to the mouse click. Then update the model and ensure that both the number view and the graph view are notified of the change so that they can refresh their contents. Hint: Look up the API documentation for the MouseListener interface type. In your listener, you need to take action in the mousePressed method. Implement the remaining methods of the interface type to do nothing.
Exercise 1 is below.
-----------------------------------------------------------------------------------
BarFrame.java
import java.awt.*; import java.awt.geom.*; import javax.swing.*; import javax.swing.event.*; import java.util.*; /** A class that implements an Observer object that displays a barchart view of a data model. */ public class BarFrame extends JFrame implements ChangeListener { /** Constructs a BarFrame object @param dataModel the data that is displayed in the barchart */ public BarFrame(DataModel dataModel) { this.dataModel = dataModel; a = dataModel.getData(); setLocation(0,200); setLayout(new BorderLayout()); Icon barIcon = new Icon() { public int getIconWidth() { return ICON_WIDTH; } public int getIconHeight() { return ICON_HEIGHT; } public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.red); double max = (a.get(0)).doubleValue(); for (Double v : a) { double val = v.doubleValue(); if (val > max) max = val; } double barHeight = getIconHeight() / a.size(); int i = 0; for (Double v : a) { double value = v.doubleValue(); double barLength = getIconWidth() * value / max; Rectangle2D.Double rectangle = new Rectangle2D.Double (0, barHeight * i, barLength, barHeight); i++; g2.fill(rectangle); } } }; add(new JLabel(barIcon)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setVisible(true); } /** Called when the data in the model is changed. @param e the event representing the change */ public void stateChanged(ChangeEvent e) { a = dataModel.getData(); repaint(); } private ArrayList<Double> a; private DataModel dataModel; private static final int ICON_WIDTH = 200; private static final int ICON_HEIGHT = 200; }
-----------------------------------------------------------------------------------
DataModel.java
import java.util.ArrayList; import javax.swing.event.*; /** A Subject class for the observer pattern. */ public class DataModel { /** Constructs a DataModel object @param d the data to model */ public DataModel(ArrayList<Double> d) { data = d; listeners = new ArrayList<ChangeListener>(); } /** Constructs a DataModel object @return the data in an ArrayList */ public ArrayList<Double> getData() { return (ArrayList<Double>) (data.clone()); } /** Attach a listener to the Model @param c the listener */ public void attach(ChangeListener c) { listeners.add(c); } /** Change the data in the model at a particular location @param location the index of the field to change @param value the new value */ public void update(int location, double value) { data.set(location, new Double(value)); for (ChangeListener l : listeners) { l.stateChanged(new ChangeEvent(this)); } } ArrayList<Double> data; ArrayList<ChangeListener> listeners; }
-----------------------------------------------------------------------------------
TextFrame.java
import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.ArrayList; /** A class for displaying the model as a column of textfields in a frame. */ public class TextFrame extends JFrame { /** Constructs a JFrame that contains the textfields containing the data in the model. @param d the model to display */ public TextFrame(DataModel d) { dataModel = d; final Container contentPane = this.getContentPane(); setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); ArrayList<Double> a = dataModel.getData(); fieldList = new JTextField[a.size()]; // A listener for action events in the text fields ActionListener l = new ActionListener() { public void actionPerformed(ActionEvent e) { // Figure out which field generated the event JTextField c = (JTextField) e.getSource(); int i = 0; int count = fieldList.length; while (i < count && fieldList[i] != c) i++; String text = c.getText().trim(); try { double value = Double.parseDouble(text); dataModel.update(i, value); } catch (Exception exc) { c.setText("Error. No update"); } } }; final int FIELD_WIDTH = 11; for (int i = 0; i < a.size(); i++) { JTextField textField = new JTextField(a.get(i).toString(),FIELD_WIDTH); textField.addActionListener(l); add(textField); fieldList[i] = textField; } setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setVisible(true); } DataModel dataModel; JTextField[] fieldList; }
-----------------------------------------------------------------------------------
ObserverTester.java
import java.util.ArrayList; /** A class for testing an implementation of the Observer pattern. */ public class ObserverTester { /** Creates a DataModel and attaches barchart and textfield listeners @param args unused */ public static void main(String[] args) { ArrayList<Double> data = new ArrayList<Double>(); data.add(new Double(33.0)); data.add(new Double(44.0)); data.add(new Double(22.0)); data.add(new Double(22.0)); DataModel model = new DataModel(data); TextFrame frame = new TextFrame(model); BarFrame barFrame = new BarFrame(model); model.attach(barFrame); } }
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import static java.lang.Math.abs;
public class BarGraphics extends JFrame implements MouseListener{
private JPanel contentPane;
private JTextField tf3;
private JTextField tf1;
private JTextField tf2;
private boolean b=false;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
BarGraphics frame = new BarGraphics();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public BarGraphics() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 800, 600);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel lblNum1 = new JLabel("Number 1:");
lblNum1.setBounds(46, 39, 61, 14);
contentPane.add(lblNum1);
JLabel lblNum2 = new JLabel("Number 2:");
lblNum2.setBounds(46, 69, 61, 14);
contentPane.add(lblNum2);
JLabel lblNum3 = new JLabel("Number 3:");
lblNum3.setBounds(46, 103, 61, 14);
contentPane.add(lblNum3);
tf1 = new JTextField();
tf1.setBounds(117, 36, 86, 20);
contentPane.add(tf1);
tf1.setColumns(10);
tf2 = new JTextField();
tf2.setBounds(117, 66, 86, 20);
contentPane.add(tf2);
tf2.setColumns(10);
tf3 = new JTextField();
tf3.setBounds(117, 97, 86, 20);
contentPane.add(tf3);
tf3.setColumns(10);
JButton btnGraphics = new JButton("Graphing");
btnGraphics.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
b=true;
repaint();
}
});
btnGraphics.setBounds(45, 138, 107, 37);
contentPane.add(btnGraphics);
addMouseListener(this);
}
public void paint(Graphics g)
{
super.paint(g);
if (b==true)
{
String s1=tf1.getText();
String s2=tf2.getText();
String s3=tf3.getText();
try{
int v1=Integer.parseInt(s1);
int v2=Integer.parseInt(s2);
int v3=Integer.parseInt(s3);
int higher=returnHigher(v1,v2,v3);
int largo1=v1*400/higher;
int largo2=v2*400/higher;
int largo3=v3*400/higher;
g.setColor(new Color(255,0,0));
g.fillRect(100,250,largo1,40);
g.drawString("Number 1", 10, 270);
g.setColor(new Color(0,128,0));
g.fillRect(100,300,largo2,40);
g.drawString("Number 2", 10, 320);
g.setColor(new Color(0,0,255));
g.fillRect(100,350,largo3,40);
g.drawString("Number 3", 10, 370);
}catch(Exception e){
return;
}
}
}
private int returnHigher(int v1,int v2,int v3)
{
if (v1>v2 && v1>v3)
return v1;
else
if (v2>v3)
return v2;
else
return v3;
}
private void ClickGraphics(int x){
String s1=tf1.getText();
String s2=tf2.getText();
String s3=tf3.getText();
int v1=Integer.parseInt(s1);
int v2=Integer.parseInt(s2);
int v3=Integer.parseInt(s3);
int higher=returnHigher(v1,v2,v3);
if (higher > x){
int d = abs(higher-x-100);
v1 -= d;
v2 -= d;
v3 -= d;
}else{
int d = abs(higher+x+100);
v1 += d;
v2 += d;
v3 += d;
}
tf1.setText(String.valueOf(v1));
tf2.setText(String.valueOf(v2));
tf3.setText(String.valueOf(v3));
repaint();
}
@Override
public void mouseClicked(MouseEvent e) {
//System.out.println("Click " + e.getX());
//ClickGraphics(e.getX());
}
@Override
public void mousePressed(MouseEvent e) {
System.out.println("Pressed " + e.getX());
if (b)
ClickGraphics(e.getX());
}
@Override
public void mouseReleased(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
public void mouseEntered(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
pubvoid mouseExited(MouseEvent e) {
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
output: