JFrame下NameFrame类开发需求:绘图、蜂鸣提示及名称列表存储扩展
Alright, let's walk through implementing your NameFrame class step by step, covering all the required features including the missing persistence for your displayed names list. I'll break this down into core functionality first, then dive into the persistence options that fit best for this use case.
NameFrame Implementation First, we'll build the basic Swing UI, wire up the "Graph" button logic, and handle the name search + feedback flow. We'll use an ArrayList<String> to track the displayed names as specified.
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; public class NameFrame extends JFrame { private JTextField nameInput; private JButton graphButton; private NameGraph nameGraph; private ArrayList<String> displayedNames; public NameFrame() { // Frame setup setTitle("Name Data Visualizer"); setSize(800, 600); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // We'll handle close for persistence setLayout(new BorderLayout()); // Initialize displayed names storage displayedNames = new ArrayList<>(); // Input panel (top) JPanel inputPanel = new JPanel(); nameInput = new JTextField(25); graphButton = new JButton("Graph"); inputPanel.add(new JLabel("Enter Name:")); inputPanel.add(nameInput); inputPanel.add(graphButton); add(inputPanel, BorderLayout.NORTH); // Graph rendering panel (center) nameGraph = new NameGraph(displayedNames); add(nameGraph, BorderLayout.CENTER); // Load persisted names on startup loadDisplayedNames(); // Wire up button action graphButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String targetName = nameInput.getText().trim(); if (targetName.isEmpty()) { JOptionPane.showMessageDialog(NameFrame.this, "Please enter a valid name!"); return; } // Check if name exists in NameRecord boolean nameExists = NameRecord.hasDataForName(targetName); // Assume this method exists if (nameExists) { // Avoid duplicate entries if (!displayedNames.contains(targetName)) { displayedNames.add(targetName); nameGraph.repaint(); // Trigger graph update with new name } } else { // Trigger system beep and notify user Toolkit.getDefaultToolkit().beep(); JOptionPane.showMessageDialog(NameFrame.this, "No data found for that name!"); } } }); // Handle window close to save data addWindowListener(new java.awt.event.WindowAdapter() { @Override public void windowClosing(java.awt.event.WindowEvent windowEvent) { saveDisplayedNames(); System.exit(0); } }); } public static void main(String[] args) { // Run UI on Swing's Event Dispatch Thread (EDT) for safety SwingUtilities.invokeLater(() -> { new NameFrame().setVisible(true); }); } // Persistence methods will go here }
Notes on Core Logic:
- We set
DO_NOTHING_ON_CLOSEso we can save the displayed names before exiting. - The
NameRecord.hasDataForName()is a placeholder — replace this with your actual method to check if the name exists in your dataset. NameGraphshould accept thedisplayedNameslist in its constructor and use it to render data in itspaintComponent()method. Callingrepaint()triggers a redraw when a new name is added.
Now let's add the persistence layer. We'll cover two common, simple options for saving/loading the ArrayList<String>:
Option 1: Plain Text/Properties File (Simple, Human-Readable)
This is great if you want the saved data to be editable manually. We'll use a properties file to store the names as a comma-separated string.
Add these methods to your NameFrame class:
import java.io.*; import java.util.Properties; private void loadDisplayedNames() { Properties props = new Properties(); // Store file in user's home directory to avoid permission issues File saveFile = new File(System.getProperty("user.home") + File.separator + ".name_visualizer" + File.separator + "saved_names.properties"); // Create directory if it doesn't exist saveFile.getParentFile().mkdirs(); try (FileInputStream fis = new FileInputStream(saveFile)) { props.load(fis); String namesString = props.getProperty("displayed_names", ""); if (!namesString.isEmpty()) { String[] namesArray = namesString.split(","); for (String name : namesArray) { displayedNames.add(name.trim()); } nameGraph.repaint(); // Update graph with loaded names } } catch (IOException e) { // Ignore on first run (file won't exist yet) — or log the error System.out.println("No saved names found, starting fresh."); } } private void saveDisplayedNames() { Properties props = new Properties(); File saveFile = new File(System.getProperty("user.home") + File.separator + ".name_visualizer" + File.separator + "saved_names.properties"); saveFile.getParentFile().mkdirs(); String namesString = String.join(",", displayedNames); props.setProperty("displayed_names", namesString); try (FileOutputStream fos = new FileOutputStream(saveFile)) { props.store(fos, "Saved names for Name Visualizer"); } catch (IOException e) { JOptionPane.showMessageDialog(this, "Failed to save displayed names!"); e.printStackTrace(); } }
Option 2: Object Serialization (Flexible for Future Changes)
If you might expand the data stored beyond just strings later, serialization is a better fit — it lets you save the entire ArrayList directly.
Replace the persistence methods above with these:
import java.io.*; private void loadDisplayedNames() { File saveFile = new File(System.getProperty("user.home") + File.separator + ".name_visualizer" + File.separator + "saved_names.ser"); saveFile.getParentFile().mkdirs(); try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(saveFile))) { displayedNames = (ArrayList<String>) ois.readObject(); nameGraph.repaint(); } catch (IOException | ClassNotFoundException e) { System.out.println("No saved names found, starting fresh."); } } private void saveDisplayedNames() { File saveFile = new File(System.getProperty("user.home") + File.separator + ".name_visualizer" + File.separator + "saved_names.ser"); saveFile.getParentFile().mkdirs(); try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(saveFile))) { oos.writeObject(displayedNames); } catch (IOException e) { JOptionPane.showMessageDialog(this, "Failed to save displayed names!"); e.printStackTrace(); } }
Persistence Best Practices:
- We save the file in the user's home directory (with a hidden folder) to avoid permission problems that can occur when writing to the program's installation directory.
- Always handle exceptions gracefully — show user feedback instead of just printing stack traces in production code.
- Swing Thread Safety: We use
SwingUtilities.invokeLater()to launch the UI, which ensures all UI operations run on the EDT (critical for avoiding race conditions). - Duplicate Prevention: The code checks if a name is already in the list before adding it, so you won't have duplicate entries cluttering the graph.
- Cross-Platform Beep:
Toolkit.getDefaultToolkit().beep()works on Windows, macOS, and Linux, making it a reliable choice for the error feedback.
内容的提问来源于stack exchange,提问作者Yuhe Zhu

