注:本文所有素材均來源於How to Use Lists,本文為該文章的學習筆記。
JList是Swing中的列表控件,形狀如下所示:

實際上,JList有3種不同的樣式:

分別為HORIZONTAL_WRAP、VERTICAL_WRAP、VERTICAL,通過setLayoutOrientation 進行設置。
構造model
JList每行只有一列,每一列稱為一個element。根據MVC架構,其內部的model維護着一個數組,存放了所有的成員元素。因此,對於JList內部元素的處理,都應調用其model的相關方法。
JList共有3種model:
DefaultListModel:對內部成員的操作均已實現,你只需要使用相應的方法即可。
AbstractListModel:手動管理數據並調用相關操作,必須繼承AbstractListModel並自己實現其中的getSize和getElementAt方法。
ListModel:手動管理所有事情。
一般來說,我們使用第一種即可。
初始化JList
建議使用DefaultListModel對象作為參數來初始化JList。如果你使用一個數組或vector來初始化JList,或者調用JList.setListData方法來填充它,JList會隱式地生成一個不可修改的默認model,這意味着你無法再對JList進行增刪改等操作。
選擇元素
JList中的元素選擇有三種模式,可通過setSelectionMode來設置:

添加刪除元素
調用model的add/remove/insert等方法來實現元素的添加和刪除。這里沒什么要特別注意的。
定制單元格渲染器
JList使用“單元格渲染器”來顯示所有的元素。默認的渲染器只會顯示string和圖標,對於其它對象,一律調用其toString方法來處理。如果你有特殊的需求,可以使用以下步驟來定制渲染器:
(1)寫一個實現了 ListCellRenderer接口的類;
(2)調用setCellRenderer方法,將你的定制渲染器作為參數使用;
Oracle並沒有給出Jlist使用定制渲染器的例子,只有一個是關於Combo Box的,其實是一樣的啦。Providing a Custom Renderer
選擇事件
在《Swing-JList選擇事件監聽器ListSelectionListener-入門》中專門介紹
數據事件
如果想要對JList中數據的變更進行監聽,那么你需要使用addListDataListener添加一個監聽器,該監聽器必須實現ListDataListener接口。How to Write a List Data Listener進行了詳細的介紹並提供了demo。
Demo
下面這個demo包含了JList的常用方法,修改自官網源程序ListDemo.java
/* * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle or the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package GUI.components; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; /* ListDemo.java requires no other files. */ public class ListDemo extends JPanel implements ListSelectionListener { private JList list; private DefaultListModel listModel; private static final String hireString = "Hire"; private static final String fireString = "Fire"; private JButton fireButton; private JTextField employeeName; public ListDemo() { super(new BorderLayout()); listModel = new DefaultListModel(); listModel.addElement("Jane Doe"); listModel.addElement("John Smith"); listModel.addElement("Kathy Green"); //Create the list and put it in a scroll pane. list = new JList(listModel); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setSelectedIndex(0); list.addListSelectionListener(this); list.setVisibleRowCount(5); JScrollPane listScrollPane = new JScrollPane(list); JButton hireButton = new JButton(hireString); HireListener hireListener = new HireListener(hireButton); hireButton.setActionCommand(hireString); hireButton.addActionListener(hireListener); hireButton.setEnabled(false); fireButton = new JButton(fireString); fireButton.setActionCommand(fireString); fireButton.addActionListener(new FireListener()); employeeName = new JTextField(10); employeeName.addActionListener(hireListener); employeeName.getDocument().addDocumentListener(hireListener); String name = listModel.getElementAt( list.getSelectedIndex()).toString(); //Create a panel that uses BoxLayout. JPanel buttonPane = new JPanel(); buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); buttonPane.add(fireButton); buttonPane.add(Box.createHorizontalStrut(5)); buttonPane.add(new JSeparator(SwingConstants.VERTICAL)); buttonPane.add(Box.createHorizontalStrut(5)); buttonPane.add(employeeName); buttonPane.add(hireButton); buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); add(listScrollPane, BorderLayout.CENTER); add(buttonPane, BorderLayout.SOUTH); } class FireListener implements ActionListener { public void actionPerformed(ActionEvent e) { //This method can be called only if //there's a valid selection //so go ahead and remove whatever's selected. int index = list.getSelectedIndex(); listModel.remove(index); int size = listModel.getSize(); if (size == 0) { //Nobody's left, disable firing. fireButton.setEnabled(false); } else { //Select an index. if (index == listModel.getSize()) { //removed item in last position index--; } list.setSelectedIndex(index); list.ensureIndexIsVisible(index); } } } //This listener is shared by the text field and the hire button. class HireListener implements ActionListener, DocumentListener { private boolean alreadyEnabled = false; private JButton button; public HireListener(JButton button) { this.button = button; } //Required by ActionListener. public void actionPerformed(ActionEvent e) { String name = employeeName.getText(); //User didn't type in a unique name... if (name.equals("") || alreadyInList(name)) { Toolkit.getDefaultToolkit().beep(); employeeName.requestFocusInWindow(); employeeName.selectAll(); return; } int index = list.getSelectedIndex(); //get selected index if (index == -1) { //no selection, so insert at beginning index = 0; } else { //add after the selected item index++; } listModel.insertElementAt(employeeName.getText(), index); //If we just wanted to add to the end, we'd do this: //listModel.addElement(employeeName.getText()); //Reset the text field. employeeName.requestFocusInWindow(); employeeName.setText(""); //Select the new item and make it visible. list.setSelectedIndex(index); list.ensureIndexIsVisible(index); } //This method tests for string equality. You could certainly //get more sophisticated about the algorithm. For example, //you might want to ignore white space and capitalization. protected boolean alreadyInList(String name) { return listModel.contains(name); } //Required by DocumentListener. public void insertUpdate(DocumentEvent e) { enableButton(); } //Required by DocumentListener. public void removeUpdate(DocumentEvent e) { handleEmptyTextField(e); } //Required by DocumentListener. public void changedUpdate(DocumentEvent e) { if (!handleEmptyTextField(e)) { enableButton(); } } private void enableButton() { if (!alreadyEnabled) { button.setEnabled(true); } } private boolean handleEmptyTextField(DocumentEvent e) { if (e.getDocument().getLength() <= 0) { button.setEnabled(false); alreadyEnabled = false; return true; } return false; } } //This method is required by ListSelectionListener. public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) { if (list.getSelectedIndex() == -1) { //No selection, disable fire button. fireButton.setEnabled(false); } else { //Selection, enable the fire button. fireButton.setEnabled(true); } } } /** * Create the GUI and show it. For thread safety, * this method should be invoked from the * event-dispatching thread. */ private static void createAndShowGUI() { //Create and set up the window. JFrame frame = new JFrame("ListDemo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Create and set up the content pane. JComponent newContentPane = new ListDemo(); newContentPane.setOpaque(false); //content panes must be opaque frame.setContentPane(newContentPane); //Display the window. frame.pack(); frame.setVisible(true); } public static void main(String[] args) { //Schedule a job for the event-dispatching thread: //creating and showing this application's GUI. javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGUI(); } }); } }

