Lab 10: ArrayLists and Files in a GUI Application For this lab, you will work on
ID: 3728244 • Letter: L
Question
Lab 10: ArrayLists and Files in a GUI Application
For this lab, you will work on a simple GUI application. The starting point for your work consists of four files (TextCollage, DrawTextItem, DrawTextPanel, and SimpleFileChooser) in the code directory. These files are supposed to be in package named "textcollage". Start an Eclipse project, create a package named textcollage in that project, and copy the four files into the package. To run the program, you should run the file TextCollage.java, which contains the main routine. You will need to look through the DrawTextItem.java and SimpleFileChooser.java, but the only file that you have to edit is DrawTextPanel.java.
Part 1: ArrayLists
You can run TextCollage to see what it does. As it stands, you can click the drawing area to place a string on the canvas. The text of the string comes from the input box at the bottom of the window. The program only has support for one string. If you click the mouse again, the previous string disappears, and the string appears at the location of the new mouse click.
A string in the program is represented by a variable of type DrawTextItem. In addition to the string itself, a DrawTextItem holds information about the appearance of the string, such as its color, font, background color, rotation, and whether it has a border. (The doMousePress method, where strings are created, has some lines that you can uncomment to experiment with these options.)
Your first job is to replace the variable theString, which is a single DrawTextItem, with a variable of type ArrayList<DrawTextItem> that can hold any number of text items.
You will then have to modify the program wherever theString was used. For example, in the doMousePress method, you should add the newly created DrawTextItem variable to the arraylist, instead of assigning it to theString.
When you have finished, you should be able to add multiple strings to the picture, and the "Undo" and "Clear" commands in the "Edit" menu should work. (The "Save Image" command in the "File" menu and the commands in the "Options" menu also work, but they were already working before you made any changes.)
Part 2: Files
The program is already able to save the contents of the drawing area as an image. But when you do this, there is no way to go back and edit the strings in the picture. To be able to do that, you should save all the information that defines the strings, not just the image where the strings are displayed. That's what the "Save" and "Open" commands are for in the "File" menu. The "Save" command should create a text file that describes the contents of the image (that is, the background color and the arraylist of DrawTextItems). The "Open" command should be able to read a file created by the "Save" command and restore the state of the program. You can look at the implementation of the "Save Image" command for some hints. Note in particular the use of try..catch to catch any exceptions that occur during the file manipulation; you should always do something similar when you work with files, and you should report any errors to the user.
First, implement the "Save" command. Use the method fileChooser.getOutputFile to get a File from the user. (fileChooser is a varialble of type SimpleFileChooser, which is already defined. See the implementation of "Save Image" for an example of using it.) Create a PrintWriter to write to the file. Output a text representation of the background color of the image, then write at least the text and color of each DrawTextItem in the arraylist. Don't forget to close the PrintWriter. Note that you can represent a color c as the three integers c.getRed(), c.getGreen(), and c.getBlue(). Design the format for your output file so that it will be easy to read the data in later. Putting every output value on a separate line is the easiest approach. Test your work by saving a file and looking at its contents.
Next, you can implement the "Open" command. Use the method fileChooser.getInputFile to let the user select a file for input. Create a Scanner to read from the file. You should use the scanner to read the contents of the file back into the program and reconstruct the picture represented by the contents of the file. Of course, this will only succeed if the file is one that was previously saved from the program using the Save command (or if it follows exactly the same syntax as such a file). Note: As long as each item is on its own line in the file, you can use scanner.nextLine() to read items from the file. If you are trying to read an integer, use Integer.parseInt(scanner.nextLine()). You should know the order in which the data items were written, and you should read them in the same order. If any error occurs, it means that the file is not of the correct form. Don't forget to call canvas.repaint() at the end, to make the new image visible. Ideally, if an error does occur, you should not change the current contents of the image.
Part 3: Improve the program!
To complete your program, you should design at least one additional feature and add it to the program. You should consult with your course advisor about what would be appropriate and how to do it. Grading will be based in part on creativity, ambition, and degree of consultation. You will probably need to extend your file format to accommodate the new features.
For example, you could add new options to control the appearance of strings. The DrawTextItem class, which is used to represent the strings that are drawn in the picture, has a variety of properties that can be applied to the strings. Some examples can be found, commented out, in the doMousePressed() method in the DrawTextPanel class. Another possibility would be to allow the user to drag text items around on the screen, after they have been placed. Or, you could add the ability to create a random text collage using a bunch of strings selected at random from a text file specified by the user.
+++++++++++++++++++++++++++++++++++++++++++++++
Please find link below to view DirectoryList.java, DrawTextItem.java, DrawTextPanel.java, SimpleFileChooser.java, TextCollage
https://drive.google.com/drive/folders/1oDWZFjme09ZOtdN3Lxkz2eAvYMEdKMlZ?usp=sharing
Explanation / Answer
/**
* Modified File DrawTextPanel.java
*/
package textcollage;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Scanner;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JColorChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
/**
* A panel that contains a large drawing area where strings
* can be drawn. The strings are represented by objects of
* type DrawTextItem. An input box under the panel allows
* the user to specify what string will be drawn when the
* user clicks on the drawing area.
*/
public class DrawTextPanel extends JPanel {
// As it now stands, this class can only show one string at at
// a time! The data for that string is in the DrawTextItem object
// named theString. (If it's null, nothing is shown. This
// variable should be replaced by a variable of type
// ArrayList<DrawStringItem> that can store multiple items.
private ArrayList<DrawTextItem> theString = new ArrayList<DrawTextItem>(); // change to an ArrayList<DrawTextItem> !
private Color currentTextColor = Color.BLACK; // Color applied to new strings.
private Canvas canvas; // the drawing area.
private JTextField input; // where the user inputs the string that will be added to the canvas
private SimpleFileChooser fileChooser; // for letting the user select files
private JMenuBar menuBar; // a menu bar with command that affect this panel
private MenuHandler menuHandler; // a listener that responds whenever the user selects a menu command
private JMenuItem undoMenuItem; // the "Remove Item" command from the edit menu
/**
* An object of type Canvas is used for the drawing area.
* The canvas simply displays all the DrawTextItems that
* are stored in the ArrayList, strings.
*/
private class Canvas extends JPanel {
Canvas() {
setPreferredSize( new Dimension(800,600) );
setBackground(Color.LIGHT_GRAY);
setFont( new Font( "Serif", Font.BOLD, 24 ));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (theString != null) {
for (DrawTextItem thisString: theString) {
thisString.draw(g);
}
}
}
}
/**
* An object of type MenuHandler is registered as the ActionListener
* for all the commands in the menu bar. The MenuHandler object
* simply calls doMenuCommand() when the user selects a command
* from the menu.
*/
private class MenuHandler implements ActionListener {
public void actionPerformed(ActionEvent evt) {
doMenuCommand( evt.getActionCommand());
}
}
/**
* Creates a DrawTextPanel. The panel has a large drawing area and
* a text input box where the user can specify a string. When the
* user clicks the drawing area, the string is added to the drawing
* area at the point where the user clicked.
*/
public DrawTextPanel() {
fileChooser = new SimpleFileChooser();
undoMenuItem = new JMenuItem("Remove Item");
undoMenuItem.setEnabled(false);
menuHandler = new MenuHandler();
setLayout(new BorderLayout(3,3));
setBackground(Color.BLACK);
setBorder(BorderFactory.createLineBorder(Color.BLACK, 2));
canvas = new Canvas();
add(canvas, BorderLayout.CENTER);
JPanel bottom = new JPanel();
bottom.add(new JLabel("Text to add: "));
input = new JTextField("Hello World!", 40);
bottom.add(input);
add(bottom, BorderLayout.SOUTH);
canvas.addMouseListener( new MouseAdapter() {
public void mousePressed(MouseEvent e) {
doMousePress( e );
}
} );
}
/**
* This method is called when the user clicks the drawing area.
* A new string is added to the drawing area. The center of
* the string is at the point where the user clicked.
* @param e the mouse event that was generated when the user clicked
*/
public void doMousePress( MouseEvent e ) {
String text = input.getText().trim();
if (text.length() == 0) {
input.setText("Hello World!");
text = "Hello World!";
}
DrawTextItem s = new DrawTextItem( text, e.getX(), e.getY() );
s.setTextColor(currentTextColor); // Default is null, meaning default color of the canvas (black).
// SOME OTHER OPTIONS THAT CAN BE APPLIED TO TEXT ITEMS:
// s.setFont( new Font( "Serif", Font.ITALIC + Font.BOLD, 12 )); // Default is null, meaning font of canvas.
// s.setMagnification(3); // Default is 1, meaning no magnification.
// s.setBorder(true); // Default is false, meaning don't draw a border.
// s.setRotationAngle(25); // Default is 0, meaning no rotation.
// s.setTextTransparency(0.3); // Default is 0, meaning text is not at all transparent.
// s.setBackground(Color.BLUE); // Default is null, meaning don't draw a background area.
// s.setBackgroundTransparency(0.7); // Default is 0, meaning background is not transparent.
theString.add(s); // Set this string as the ONLY string to be drawn on the canvas!
undoMenuItem.setEnabled(true);
canvas.repaint();
}
/**
* Returns a menu bar containing commands that affect this panel. The menu
* bar is meant to appear in the same window that contains this panel.
*/
public JMenuBar getMenuBar() {
if (menuBar == null) {
menuBar = new JMenuBar();
String commandKey; // for making keyboard accelerators for menu commands
if (System.getProperty("mrj.version") == null)
commandKey = "control "; // command key for non-Mac OS
else
commandKey = "meta "; // command key for Mac OS
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
JMenuItem saveItem = new JMenuItem("Save...");
saveItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + "N"));
saveItem.addActionListener(menuHandler);
fileMenu.add(saveItem);
JMenuItem openItem = new JMenuItem("Open...");
openItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + "O"));
openItem.addActionListener(menuHandler);
fileMenu.add(openItem);
fileMenu.addSeparator();
JMenuItem saveImageItem = new JMenuItem("Save Image...");
saveImageItem.addActionListener(menuHandler);
fileMenu.add(saveImageItem);
JMenu editMenu = new JMenu("Edit");
menuBar.add(editMenu);
undoMenuItem.addActionListener(menuHandler); // undoItem was created in the constructor
undoMenuItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + "Z"));
editMenu.add(undoMenuItem);
editMenu.addSeparator();
JMenuItem clearItem = new JMenuItem("Clear");
clearItem.addActionListener(menuHandler);
editMenu.add(clearItem);
JMenu optionsMenu = new JMenu("Options");
menuBar.add(optionsMenu);
JMenuItem colorItem = new JMenuItem("Set Text Color...");
colorItem.setAccelerator(KeyStroke.getKeyStroke(commandKey + "T"));
colorItem.addActionListener(menuHandler);
optionsMenu.add(colorItem);
JMenuItem bgColorItem = new JMenuItem("Set Background Color...");
bgColorItem.addActionListener(menuHandler);
optionsMenu.add(bgColorItem);
}
return menuBar;
}
/**
* Carry out one of the commands from the menu bar.
* @param command the text of the menu command.
*/
private void doMenuCommand(String command) {
if (command.equals("Save...")) { // save all the string info to a file
//JOptionPane.showMessageDialog(this, "Sorry, the Save command is not implemented.");
File textFile = fileChooser.getOutputFile(this, "Select Text File Name", "text.txt");
if (textFile == null)
return;
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(textFile));
// write bg color
bw.write(canvas.getHeight() + "~"+ canvas.getWidth()+"~"+canvas.getBackground().getRGB());
for (DrawTextItem dti : theString) {
bw.write(System.lineSeparator());
bw.write(dti.getString()+"~"+dti.getX()+"~"+dti.getY()+"~"+dti.getTextColor().getRGB()+"~"+dti.getRotationAngle());
}
bw.close();
}
catch (Exception e) {
JOptionPane.showMessageDialog(this,
"Sorry, an error occurred while trying to save the image details: " + e);
}
}
else if (command.equals("Open...")) { // read a previously saved file, and reconstruct the list of strings
//JOptionPane.showMessageDialog(this, "Sorry, the Open command is not implemented.");
File textFile = fileChooser.getInputFile(this, "Select Text File Name");
if (textFile == null)
return;
try {
Scanner sc = new Scanner(textFile);
String meta = sc.nextLine();
canvas.setSize(Integer.parseInt(meta.split("~")[1]), Integer.parseInt(meta.split("~")[0]));
canvas.setBackground(new Color(Integer.parseInt(meta.split("~")[2])));
theString = new ArrayList<DrawTextItem>();
while(sc.hasNextLine()) {
String element = sc.nextLine();
DrawTextItem dti = new DrawTextItem(element.split("~")[0]);
dti.setX(Integer.parseInt(element.split("~")[1]));
dti.setY(Integer.parseInt(element.split("~")[2]));
dti.setTextColor(new Color(Integer.parseInt(element.split("~")[3])));
dti.setRotationAngle(Double.parseDouble(element.split("~")[4]));
theString.add(dti);
}
//canvas.repaint();
sc.close();
}
catch (Exception e) {
JOptionPane.showMessageDialog(this,
"Sorry, an error occurred while trying to save the image details: " + e);
}
canvas.repaint(); // (you'll need this to make the new list of strings take effect)
}
else if (command.equals("Clear")) { // remove all strings
theString = new ArrayList<DrawTextItem>(); // Remove the ONLY string from the canvas.
undoMenuItem.setEnabled(false);
canvas.repaint();
}
else if (command.equals("Remove Item")) { // remove the most recently added string
theString.remove(theString.size()-1); // Remove the ONLY string from the canvas.
if (theString.size()==0) {
undoMenuItem.setEnabled(false);
}
canvas.repaint();
}
else if (command.equals("Set Text Color...")) {
Color c = JColorChooser.showDialog(this, "Select Text Color", currentTextColor);
if (c != null)
currentTextColor = c;
}
else if (command.equals("Set Background Color...")) {
Color c = JColorChooser.showDialog(this, "Select Background Color", canvas.getBackground());
if (c != null) {
canvas.setBackground(c);
canvas.repaint();
}
}
else if (command.equals("Save Image...")) { // save a PNG image of the drawing area
File imageFile = fileChooser.getOutputFile(this, "Select Image File Name", "textimage.png");
if (imageFile == null)
return;
try {
// Because the image is not available, I will make a new BufferedImage and
// draw the same data to the BufferedImage as is shown in the panel.
// A BufferedImage is an image that is stored in memory, not on the screen.
// There is a convenient method for writing a BufferedImage to a file.
BufferedImage image = new BufferedImage(canvas.getWidth(),canvas.getHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setFont(canvas.getFont());
canvas.paintComponent(g); // draws the canvas onto the BufferedImage, not the screen!
boolean ok = ImageIO.write(image, "PNG", imageFile); // write to the file
if (ok == false)
throw new Exception("PNG format not supported (this shouldn't happen!).");
}
catch (Exception e) {
JOptionPane.showMessageDialog(this,
"Sorry, an error occurred while trying to save the image: " + e);
}
}
}
}
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.