上一篇完成了 XTree 核心組件(繼承 JTree,JAXP 解析 XML 構建樹模型)。本篇構建雙分欄編輯器框架:左側樹視圖、右側文本編輯區,並增強 XTree 魯棒性(默認樹、異常處理)。
JFrame (BorderLayout)
├─ NORTH: JButton "Refresh" (刷新樹視圖)
├─ CENTER: JSplitPane (HORIZONTAL_SPLIT)
│ ├─ LEFT: JScrollPane -> XTree
│ └─ RIGHT: JScrollPane -> JTextArea
└─ SOUTH: (可選狀態欄)
public class XmlEditor extends JFrame implements ActionListener {
private XTree xTree;
private JTextArea textArea;
private JSplitPane splitPane;
private JButton refreshButton;
public XmlEditor(String title) throws ParserConfigurationException {
super(title);
initComponents();
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
}
private void initComponents() {
// 刷新按鈕
refreshButton = new JButton("Refresh");
refreshButton.addActionListener(this);
add(refreshButton, BorderLayout.NORTH);
// 左側:樹視圖
xTree = new XTree(); // 使用無參構造器,顯示默認歡迎樹
JScrollPane leftScroll = new JScrollPane(xTree);
// 右側:文本編輯區
textArea = new JTextArea(200, 150);
textArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
JScrollPane rightScroll = new JScrollPane(textArea);
// 分欄面板
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScroll, rightScroll);
splitPane.setOneTouchExpandable(true); // 單鍵展開/摺疊
splitPane.setDividerLocation(300);
Dimension minSize = new Dimension(200, 150);
leftScroll.setMinimumSize(minSize);
rightScroll.setMinimumSize(minSize);
splitPane.setPreferredSize(new Dimension(800, 600));
add(splitPane, BorderLayout.CENTER);
}
// 刷新按鈕事件:將文本區內容解析並更新樹
public void actionPerformed(ActionEvent e) {
if ("Refresh".equals(e.getActionCommand())) {
xTree.refresh(textArea.getText());
}
}
// ... main 方法與命令行參數處理
}
buildTree(String)private DefaultTreeModel buildTree(String xmlText) {
try {
Node root = parseXml(xmlText);
if (root != null) {
DefaultMutableTreeNode treeRoot = createTreeNode(root);
return new DefaultTreeModel(treeRoot);
}
} catch (Exception ex) {
ex.printStackTrace(); // 後續改為彈窗提示
}
return null; // 解析失敗返回 null
}
buildWelcomeTree()無命令行參數啟動時顯示使用指引,而非空白界面:
private DefaultTreeModel buildWelcomeTree() {
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Welcome to XML View 1.0");
DefaultMutableTreeNode instructions = new DefaultMutableTreeNode("Instructions");
instructions.add(new DefaultMutableTreeNode("Opening XML Documents: java XmlEditor file.xml"));
instructions.add(new DefaultMutableTreeNode("Editing: Modify text in right pane, click Refresh to update tree."));
instructions.add(new DefaultMutableTreeNode("Saving: Not yet implemented (coming in Part 3)."));
root.add(instructions);
return new DefaultTreeModel(root);
}
// 無參構造器:顯示歡迎樹
public XTree() throws ParserConfigurationException {
super();
getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
setShowsRootHandles(true);
setEditable(false);
initParser();
setModel(buildWelcomeTree());
}
// 帶參構造器:解析指定 XML
public XTree(String xmlText) throws ParserConfigurationException {
this(); // 復用初始化邏輯
refresh(xmlText);
}
// 公開刷新方法:供外部調用(如刷新按鈕)
public void refresh(String xmlText) {
DefaultTreeModel model = buildTree(xmlText);
if (model != null) {
setModel(model);
} else {
// 保持原模型不變,或顯示錯誤節點
JOptionPane.showMessageDialog(this, "Invalid XML: " + lastParseError, "Parse Error", JOptionPane.ERROR_MESSAGE);
}
}
System.exit() 到優雅降級原 parseXml() 拋出異常直接 System.exit(0),現改為捕獲異常、記錄錯誤信息、返回 null,由調用者決定如何提示用戶,避免整個應用崩潰。
public static void main(String[] args) {
String fileName = "";
ArrayList xmlLines = new ArrayList<>();
if (args.length > 0) {
fileName = args[0];
if (fileName.toLowerCase().endsWith(".xml")) {
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
String line;
while ((line = reader.readLine()) != null) {
xmlLines.add(line);
}
new XmlEditor("XmlEditor 1.0", xmlLines).setVisible(true);
} catch (IOException | ParserConfigurationException ex) {
System.err.println("Error: " + ex.getMessage());
showHelp();
}
} else {
showHelp();
}
} else {
// 無參啟動:顯示歡迎界面
try {
new XmlEditor("XmlEditor 1.0").setVisible(true);
} catch (ParserConfigurationException ex) {
ex.printStackTrace();
}
}
}
本篇確立了雙視圖編輯器骨架,XTree 變身為健壯的可複用組件(默認樹、錯誤容錯、公開刷新 API)。下一篇將補齊文件操作(打開/保存)、菜單系統與退出確認,交付一個真正可用的桌面工具。