展示可動態增刪的列表組件:
SINGLE_SELECTION),默認選中首項DefaultListModel 作為可變數據模型,支援 addElement/insertElementAt/remove 等操作,JList 自動監聽模型變化並刷新視圖。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class ListDemo extends JPanel implements ListSelectionListener {
private JList list;
private DefaultListModel model;
private JButton addBtn, removeBtn;
private JTextField inputField;
public ListDemo() {
super(new BorderLayout());
// 1. 數據模型與初始數據
model = new DefaultListModel<>();
for (String name : new String[]{
"Alan Sommerer", "Alison Huml", "Kathy Walrath",
"Lisa Friendly", "Mary Campione", "Sharon Zakhour"
}) model.addElement(name);
// 2. JList 配置
list = new JList<>(model);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setSelectedIndex(0);
list.addListSelectionListener(this);
list.setVisibleRowCount(5);
JScrollPane scrollPane = new JScrollPane(list);
// 3. 底部操作區
addBtn = new JButton("添加");
removeBtn = new JButton("刪除");
inputField = new JTextField(10);
// 添加邏輯:內部類 HireListener 實現 ActionListener + DocumentListener
HireListener hireListener = new HireListener(addBtn);
addBtn.addActionListener(hireListener);
addBtn.setActionCommand("add");
addBtn.setEnabled(false);
inputField.addActionListener(hireListener);
inputField.getDocument().addDocumentListener(hireListener);
// 刪除邏輯
removeBtn.addActionListener(new FireListener());
removeBtn.setActionCommand("remove");
removeBtn.setEnabled(false); // 初始有選中項,稍後在 valueChanged 中啟用
// 底部面板佈局
JPanel btnPanel = new JPanel();
btnPanel.setLayout(new BoxLayout(btnPanel, BoxLayout.LINE_AXIS));
btnPanel.add(removeBtn);
btnPanel.add(Box.createHorizontalStrut(5));
btnPanel.add(new JSeparator(SwingConstants.VERTICAL));
btnPanel.add(Box.createHorizontalStrut(5));
btnPanel.add(inputField);
btnPanel.add(addBtn);
btnPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
add(scrollPane, BorderLayout.CENTER);
add(btnPanel, BorderLayout.PAGE_END);
}
// --- 刪除監聽 ---
class FireListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int idx = list.getSelectedIndex();
model.remove(idx);
int size = model.getSize();
if (size == 0) {
removeBtn.setEnabled(false);
} else {
if (idx >= size) idx = size - 1;
list.setSelectedIndex(idx);
list.ensureIndexIsVisible(idx);
}
}
}
// --- 添加監聽(含文本框狀態聯動) ---
class HireListener implements ActionListener, DocumentListener {
private JButton btn;
private boolean alreadyEnabled = false;
HireListener(JButton btn) { this.btn = btn; }
@Override public void actionPerformed(ActionEvent e) {
String name = inputField.getText().trim();
if (name.isEmpty() || model.contains(name)) {
Toolkit.getDefaultToolkit().beep();
inputField.requestFocusInWindow();
inputField.selectAll();
return;
}
int idx = list.getSelectedIndex();
if (idx == -1) idx = 0; else idx++;
model.insertElementAt(name, idx);
inputField.setText("");
inputField.requestFocusInWindow();
list.setSelectedIndex(idx);
list.ensureIndexIsVisible(idx);
}
// DocumentListener:輸入框非空→啟用添加按鈕
@Override public void insertUpdate(DocumentEvent e) { enableButton(); }
@Override public void removeUpdate(DocumentEvent e) { handleEmpty(e); }
@Override public void changedUpdate(DocumentEvent e) { if (!handleEmpty(e)) enableButton(); }
private void enableButton() {
if (!alreadyEnabled) { btn.setEnabled(true); alreadyEnabled = true; }
}
private boolean handleEmpty(DocumentEvent e) {
if (e.getDocument().getLength() <= 0) {
btn.setEnabled(false);
alreadyEnabled = false;
return true;
}
return false;
}
}
// --- 列表選中狀態聯動刪除按鈕 ---
@Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
removeBtn.setEnabled(list.getSelectedIndex() != -1);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame("ListDemo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(new ListDemo());
f.pack();
f.setVisible(true);
});
}
}
| 模式 | 應用場景 |
|---|---|
| Model-View 分離 | DefaultListModel 獨立管理數據,JList 專注渲染 |
| 事件委派 | ListSelectionListener、ActionListener、DocumentListener 分工明確 |
| 狀態機 | 按鈕啟用/禁用由「有選中項」「輸入框非空」「無重名」三條件共同決定 |
| 即時反饋 | 重名/空輸入 → 蜂鳴 + 全選 + 焦點回籠,引導用戶修正 |
ListCellEditor)DragSource/DropTarget)ListView:綁定 ObservableList,聲明式綁定更簡潔