Java-GUI 编程之 JList、JComboBox实现列表框
JList、JComboBox实现列表框
无论从哪个角度来看, JList 和 JComboBox 都是极其相似的,它们都有一个列表框,只是 JComboBox的列表框需要 以下拉方式显示出来; JList 和 JComboBox 都可以通过调用 setRendererO方法来改变列表项的表现形式 。甚至维护这两个组件的 Model 都是相似的, JList 使用 ListModel, JComboBox 使用ComboBoxModel ,而 ComboBoxModel 是 ListModel 的子类 。
简单列表框
使用JList或JComboBox实现简单列表框的步骤:
- 创建JList或JComboBox对象
JList(final E[] listData):创建JList对象,把listData数组中的每项内容转换成一个列表项展示
JList(final Vector<? extends E> listData):创建JList对象,把listData数组中的每项内容转换成一个列表项展示
JComboBox(E[] items):
JComboBox(Vector<E> items):
- 设置JList或JComboBox的外观行为
---------------------------JList----------------------------------------------
addSelectionInterval(int anchor, int lead):在已经选中列表项的基础上,增加选中从anchor到lead索引范围内的所有列表项
setFixedCellHeight(int height)/setFixedCellWidth(int width):设置列表项的高度和宽度
setLayoutOrientation(int layoutOrientation):设置列表框的布局方向
setSelectedIndex(int index):设置默认选中项
setSelectedIndices(int[] indices):设置默认选中的多个列表项
setSelectedValue(Object anObject,boolean shouldScroll):设置默认选中项,并滚动到该项显示
setSelectionBackground(Color selectionBackground):设置选中项的背景颜色
setSelectionForeground(Color selectionForeground):设置选中项的前景色
setSelectionInterval(int anchor, int lead):设置从anchor到lead范围内的所有列表项被选中
setSelectionMode(int selectionMode):设置选中模式,默认没有限制,也可以设置为单选或者区域选中
setVisibleRowCount(int visibleRowCount):设置列表框的可是高度足以显示多少行列表项
---------------------------JComboBox----------------------------------------------
setEditable(boolean aFlag):设置是否可以直接修改列表文本框的值,默认为不可以
setMaximumRowCount(int count):设置列表框的可是高度足以显示多少行列表项
setSelectedIndex(int anIndex):设置默认选中项
setSelectedItem(Object anObject):根据列表项的值,设置默认选中项
- 设置监听器,监听列表项的变化,JList通过addListSelectionListener完成,JComboBox通过addItemListener完成
案例:
使用JList和JComboBox完成下图效果:
演示代码:
import javax.swing.*;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.List;
import java.util.Vector;
public class ListTest {
JFrame mainWin = new JFrame("列表框测试");
String[] books = {"java自学宝典","轻量级javaEE企业应用实战","Android基础教程","jQuery实战教程","SpringBoot企业级开发"};
//用一个字符串数组来创建一个JList对象
JList<String> bookList = new JList<>(books);
JComboBox<String> bookSelector;
//定义 布局选择按钮 所在的面板
JPanel layoutPanel = new JPanel();
ButtonGroup layoutGroup = new ButtonGroup();
//定义 选择模式按钮 所在面板
JPanel selectModePanel = new JPanel();
ButtonGroup selectModeGroup = new ButtonGroup();
JTextArea favorite = new JTextArea(4,40);
public void init(){
//设置JList的可视高度可以同时展示3个列表项
bookList.setVisibleRowCount(3);
//设置Jlist默认选中第三项到第五项
bookList.setSelectionInterval(2,4);
addLayoutButton("纵向滚动",JList.VERTICAL);
addLayoutButton("纵向换行",JList.VERTICAL_WRAP);
addLayoutButton("横向换行",JList.HORIZONTAL_WRAP);
addSelectModeButton("无限制", ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
addSelectModeButton("单选", ListSelectionModel.SINGLE_SELECTION);
addSelectModeButton("单范围", ListSelectionModel.SINGLE_INTERVAL_SELECTION);
Box listBox = Box.createVerticalBox();
//将JList组件放置到JScrollPane中,并将JScrollPane放置到box中
listBox.add(new JScrollPane(bookList));
listBox.add(layoutPanel);
listBox.add(selectModePanel);
//为JList添加事件监听器
bookList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
List<String> selectedValuesList = bookList.getSelectedValuesList();
favorite.setText("");
for (String s : selectedValuesList) {
favorite.append(s+"\n");
}
}
});
//定义一个Vector对象
Vector<String> bookCollection = new Vector<>();
List<String> books = List.of("java自学宝典","轻量级javaEE企业应用实战","Android基础教程","jQuery实战教程","SpringBoot企业级开发");
bookCollection.addAll(books);
//创建JComboBox对象
bookSelector = new JComboBox<>(bookCollection);
//为JComboBox添加事件监听器
bookSelector.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
Object selectedItem = bookSelector.getSelectedItem();
favorite.setText(selectedItem.toString());
}
});
//设置JComboBox的列表项可编辑
bookSelector.setEditable(true);
//设置下拉列表的可视高度最多显示4个列表项
bookSelector.setMaximumRowCount(4);
JPanel panel = new JPanel();
panel.add(bookSelector);
Box box = Box.createHorizontalBox();
box.add(listBox);
box.add(panel);
JPanel favoritePanel = new JPanel();
favoritePanel.setLayout(new BorderLayout());
favoritePanel.add(new JScrollPane(favorite));
favoritePanel.add(new JLabel("您最喜欢的图书:"),BorderLayout.NORTH);
mainWin.add(box);
mainWin.add(favoritePanel,BorderLayout.SOUTH);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}
public void addLayoutButton(String label,int orientation){
layoutPanel.setBorder(new TitledBorder(new EtchedBorder(),"确定选项布局"));
JRadioButton button = new JRadioButton(label);
layoutPanel.add(button);
//默认选中第一个按钮
if (layoutGroup.getButtonCount()==0){
button.setSelected(true);
}
layoutGroup.add(button);
button.addActionListener(e->{
//改变列表框里列表项的布局方向
bookList.setLayoutOrientation(orientation);
});
}
public void addSelectModeButton(String label,int selectMode){
selectModePanel.setBorder(new TitledBorder(new EtchedBorder(),"确定选择模式"));
JRadioButton button = new JRadioButton(label);
selectModePanel.add(button);
if (selectModeGroup.getButtonCount()==0){
button.setSelected(true);
}
selectModeGroup.add(button);
button.addActionListener(e->{
bookList.setSelectionMode(selectMode);
});
}
public static void main(String[] args) {
new ListTest().init();
}
}
不强制存储列表项的ListModel和ComboBoxModel
与JProgressBar一样,JList和JComboBox也采用了MVC的设计模式,JList和JComboBox只负责外观的显示,而组件底层的状态数据则由对应的Model来维护。JList对应的Model是ListModel接口,JComboBox对应的Model是ComboBox接口,其代码如下:
public interface ListModel<E>{
int getSize();
E getElementAt(int index);
void addListDataListener(ListDataListener l);
void removeListDataListener(ListDataListener l);
}
public interface ComboBoxModel<E> extends ListModel<E> {
void setSelectedItem(Object anItem);
Object getSelectedItem();
}
从上面接口来看,这个 ListMode l 不管 JList 里的所有列表项的存储形式,它甚至不强制存储所有的列表项,只要 ListModel的实现类提供了getSize()和 getElementAt()两个方法 , JList 就可以根据该ListModel 对象来生成列表框 。ComboBoxModel 继承了 ListModel ,它添加了"选择项"的概念,选择项代表 JComboBox 显示区域内可见的列表项 。
在使用JList和JComboBox时,除了可以使用jdk提供的Model实现类,程序员自己也可以根据需求,自己定义Model的实现类,实现对应的方法使用。
案例:
自定义NumberListModel和NumberComboBoxModel实现类,允许使用数值范围来创建JList和JComboBox
演示代码:
import javax.swing.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class NumberListModel extends AbstractListModel<BigDecimal> {
protected BigDecimal start;
protected BigDecimal end;
protected BigDecimal step;
public NumberListModel(double start,double end,double step) {
this.start = new BigDecimal(start);
this.end = new BigDecimal(end);
this.step = new BigDecimal(step);
}
@Override
public int getSize() {
int floor = (int) Math.floor(end.subtract(start).divide(step,2, RoundingMode.HALF_DOWN).doubleValue());
return floor+1;
}
@Override
public BigDecimal getElementAt(int index) {
return BigDecimal.valueOf(index).multiply(step).add(start).setScale(1,RoundingMode.HALF_DOWN);
}
}
import javax.swing.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class NumberComboBoxModel extends NumberListModel implements ComboBoxModel<BigDecimal> {
//用于保存用户选中项的索引
private int selectId = 0;
public NumberComboBoxModel(double start, double end, double step) {
super(start, end, step);
}
//设置选择项
@Override
public void setSelectedItem(Object anItem) {
if (anItem instanceof BigDecimal){
BigDecimal target = (BigDecimal) anItem;
selectId = target.subtract(super.start).divide(super.step,2, RoundingMode.HALF_DOWN).intValue();
}
}
//获取选中项的索引
@Override
public BigDecimal getSelectedItem() {
return BigDecimal.valueOf(selectId).multiply(step).add(start).setScale(1,RoundingMode.HALF_DOWN);
}
}
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.math.BigDecimal;
import java.util.List;
public class ListModelTest {
JFrame mainWin = new JFrame("测试ListModel");
//根据NumberListModel对象创建一个JList
JList<BigDecimal> numScopeList = new JList<>(new NumberListModel(1,21,2));
//根据NumberComboBoxModel对象创建一个JComboBox
JComboBox<BigDecimal> numScopeSelector = new JComboBox<>(new NumberComboBoxModel(0.1,1.2,0.1));
JTextField showVal = new JTextField(10);
public void init(){
//JList可视高度可同时显示四个列表项
numScopeList.setVisibleRowCount(4);
//默认选中第三项到第五项
numScopeList.setSelectionInterval(2,4);
//设置每个列表项具有指定高度和宽度
numScopeList.setFixedCellHeight(30);
numScopeList.setFixedCellWidth(90);
//为numScopeList添加监听器
numScopeList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
//获取用户选中的所有数字
List<BigDecimal> selectedValuesList = numScopeList.getSelectedValuesList();
showVal.setText("");
for (BigDecimal bigDecimal : selectedValuesList) {
showVal.setText(showVal.getText()+bigDecimal.toString()+", ");
}
}
});
//设置下拉列表的可视高度可显示5个列表项
numScopeSelector.setMaximumRowCount(5);
Box box = Box.createHorizontalBox();
box.add(new JScrollPane(numScopeList));
JPanel p = new JPanel();
p.add(numScopeSelector);
box.add(p);
//为numberScopeSelector添加监听器
numScopeSelector.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
Object value = numScopeSelector.getSelectedItem();
showVal.setText(value.toString());
}
});
JPanel bottom = new JPanel();
bottom.add(new JLabel("您选择的值是:"));
bottom.add(showVal);
mainWin.add(box);
mainWin.add(bottom, BorderLayout.SOUTH);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}
public static void main(String[] args) {
new ListModelTest().init();
}
}
强制存储列表项的DefaultListModel和DefaultComboBoxModel
前面只是介绍了如何创建 JList 、 JComboBox 对象, 当 调用 JList 和 JComboBox构造方法时时传入数组或 Vector 作为参数,这些数组元素或集合元素将会作为列表项。当使用JList 或 JComboBox 时 常常还需要动态地增加、删除列表项,例如JCombox提供了下列方法完成增删操作:
addItem(E item):添加一个列表项
insertItemAt(E item, int index):向指定索引处插入一个列表项
removeAllItems():删除所有列表项
removeItem(Object anObject):删除指定列表项
removeItemAt(int anIndex):删除指定索引处的列表项
JList 并没有提供这些类似的方法。如果需要创建一个可以增加、删除列表项的 JList 对象,则应该在创建 JLi st 时显式使用 DefaultListModel作为构造参数 。因为 DefaultListModel 作为 JList 的 Model,它负责维护 JList 组件的所有列表数据,所以可以通过向 DefaultListModel 中添加、删除元素来实现向 JList 对象中增加 、删除列表项 。DefaultListModel 提供了如下几个方法来添加、删除元素:
add(int index, E element): 在该 ListModel 的指定位置处插入指定元素 。
addElement(E obj): 将指定元素添加到该 ListModel 的末尾 。
insertElementAt(E obj, int index): 在该 ListModel 的指定位置处插入指定元素 。
Object remove(int index): 删除该 ListModel 中指定位置处的元素
removeAllElements(): 删 除该 ListModel 中的所有元素,并将其的大小设置为零 。
removeElement(E obj): 删除该 ListModel 中第一个与参数匹配的元素。
removeElementAt(int index): 删除该 ListModel 中指定索引处的元素 。
removeRange(int 企omIndex , int toIndex): 删除该 ListModel 中指定范围内的所有元素。
set(int index, E element) : 将该 ListModel 指定索引处的元素替换成指定元素。
setElementAt(E obj, int index): 将该 ListModel 指定索引处的元素替换成指定元素。
案例:
使用DefaultListModel完成下图效果:
演示代码:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class DefaultListModelTest {
JFrame mainWin = new JFrame("测试DefaultListModel");
//定义一个JList对象
JList<String> bookList;
//定义一个DefaultListModel对象
DefaultListModel<String> bookModel = new DefaultListModel<>();
JTextField bookName = new JTextField(20);
JButton removeBtn = new JButton("删除选中图书");
JButton addBtn = new JButton("添加指定图书");
public void init(){
//向bookModel中添加元素
bookModel.addElement("java自学宝典");
bookModel.addElement("轻量级javaEE企业应用实战");
bookModel.addElement("Android基础教程");
bookModel.addElement("jQuery实战教程");
bookModel.addElement("SpringBoot企业级开发");
//根据DefaultListModel创建一个JList对象
bookList = new JList<>(bookModel);
//设置最大可视高度
bookList.setVisibleRowCount(4);
//设置只能单选
bookList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
//为addBtn添加事件监听器
addBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//当bookName文本框内容不为空时添加列表项
if (!bookName.getText().trim().equals("")){
bookModel.addElement(bookName.getText());
}
}
});
//为removeBtn添加事件监听器
removeBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedIndex = bookList.getSelectedIndex();
if (selectedIndex>=0){
bookModel.remove(selectedIndex);
}
}
});
JPanel p = new JPanel();
p.add(bookName);
p.add(addBtn);
p.add(removeBtn);
mainWin.add(new JScrollPane(bookList));
mainWin.add(p, BorderLayout.SOUTH);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}
public static void main(String[] args) {
new DefaultListModelTest().init();
}
}
使用ListCellRenderer改变列表外观
前面程序中的 JList 和 JComboBox 采用的都是简单的字符串列表项, 实际上 , JList 和 JComboBox还可以支持图标列表项,如果在创建 JList 或 JComboBox 时传入图标数组,则创建的 JList 和 JComboBox的列表项就是图标 。
如果希望列表项是更复杂 的组件,例如,希望像 QQ 程序那样每个列表项既有图标,此时需要使用ListCellRenderer接口的实现类对象,自定义每个条目组件的渲染过程:
public interface ListCellRenderer<E>
{
Component getListCellRendererComponent(
JList<? extends E> list,//列表组件
E value,//当前列表项的值额索引
int index,//当前列表项d
boolean isSelected,//当前列表项是否被选中
boolean cellHasFocus);//当前列表项是否获取了焦点
}
通过JList的setCellRenderer(ListCellRenderer<? super E> cellRenderer)
方法,把自定义的ListCellRenderer对象传递给JList,就可以按照自定义的规则绘制列表项组件了。
案例:
使用ListCellRenderer实现下图效果:
演示代码:
import javax.swing.*;
import java.awt.*;
public class ListCellRendererTest {
private JFrame mainWin = new JFrame("好友列表");
private String[] friends = {
"李清照",
"苏格拉底",
"李白",
"弄玉",
"虎头"
};
//定义一个JList对象
JList friendsList = new JList(friends);
public void init() {
//设置JList使用ImageCellRenderer作为列表项绘制器
friendsList.setCellRenderer(new ImageCellRenderer());
mainWin.add(new JScrollPane(friendsList));
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}
public static void main(String[] args) {
new ListCellRendererTest().init();
}
class ImageCellRenderer extends JPanel implements ListCellRenderer {
private ImageIcon icon;
private String name;
//定义绘制单元格的背景色
private Color background;
//定义绘制单元格的前景色
private Color foreground;
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
icon = new ImageIcon(ImagePathUtil.getRealPath("9\\" + value + ".gif"));
name = value.toString();
background = isSelected ? list.getSelectionBackground() : list.getBackground();
foreground = isSelected ? list.getSelectionForeground() : list.getForeground();
//返回当前JPanel对象,作为列表项绘制器
return this;
}
@Override
protected void paintComponent(Graphics g) {
int width = icon.getImage().getWidth(null);
int height = icon.getImage().getHeight(null);
//填充背景矩形
g.setColor(background);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(foreground);
//绘制好友头像
g.drawImage(icon.getImage(),getWidth()/2-width/2,10,null);
//绘制好友昵称
g.setFont(new Font("SansSerif",Font.BOLD,18));
g.drawString(name,getWidth()/2-name.length()*10,height+30);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(60,80);
}
}
}