注:本文所有素材均来源于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();
}
});
}
}