经典代码收藏之——JList过滤

现在许多电子书都有这样的功能块:右侧导航视窗上部具有一个供输入的文本框,下部有个列表.当在上部输入某个字符时下面的列表会自动给出以输入框中字串为前缀的所有匹配字符.下面的代码就实现了类似的功能(仅显示前缀匹配的字串)

 代码不一定工作,但主要给出实现思路:JList中所显示的数据来源于其模型--与大多数的Swing组件一样.从上面的场景中可看出有这样的动作序列:1.文本框中输入字符

2.列表中动态过滤并显示;这是表面情况,当我们要实现这样的功能时就需要知道"变化"到底发生在那里:输入的字符,导致JTextField的模型中(Document)的数据发生变化(变化--是产生事件的原因,就是说任何事件都是因为某个东西变了才有的)其产生的事件需要通知给JList并显示过滤后的字符列表,所以JList可以被作为JTextField的监听器加入到JTextField的列表中.下面是用一个内部类FilterField实现监听逻辑的(紧凑的实现!).

 FilterModel即为JList的模型,不过它的内部维护了两份数据的容器(ArrayList用于放数据),一个items用于放原始数据,另一个filterItems用于放过滤后的数据,而最终显示的是过滤后的数据.这样的技巧很棒!不会改变原始数据,显示时只是原始数据的子集,当然这样的逻辑可成为一个技巧,

如:可用原始数据做为输入,产生一个排序后的数据,但并不改变原来的数据(看JTable的排序篇就有类似的思想).总结成这样的设计小模式:显示模型:=fun( const 原始数据模型)//fun表示某种变换const表示变换不会改变原始的数据模型.,这样可用不同的变换策略生成不同的显示模型来,使设计易于修改.当然可用定义一个接口来完成.可以基于下面的代码思想完成更复杂的过滤或排序功能.

import java.awt.BorderLayout;
import java.util.ArrayList;

import javax.swing.AbstractListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class FilteredJList<E> extends JList {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private FilterField filterField; // 输入用的文本框__为JTextfield的子类
	private int DEFAULT_FIELD_WIDTH = 20; // 最多输入20个字符

	public FilteredJList() {
		super();
		setModel(new FilterModel<E>());
		filterField = new FilterField(DEFAULT_FIELD_WIDTH);
	}
	


	public void setModel(ListModel m) {
		if (!(m instanceof FilterModel)) // 仅仅能以FielterModel作为JList的模型
			throw new IllegalArgumentException();
		super.setModel(m);
	}
	
	private FilterModel<E> getFModel() {
		return (FilterModel<E>) getModel();
	}

	public void addItem(E o) {
		getFModel().addElement(o);
	}

	public JTextField getFilterField() {
		return filterField;
	}

	class FilterModel<E> extends AbstractListModel {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		ArrayList<E>  items; // 原始数据
		ArrayList<E>  filterItems;// 过滤后的显示数据

		public FilterModel() {
			super();
			items = new ArrayList<E>();
			filterItems = new ArrayList<E>();
		}

		public E getElementAt(int index) {
			if (index < filterItems.size())
				return filterItems.get(index);
			else
				return null;
		}

		public int getSize() {
			return filterItems.size();
		}

		public void addElement(E o) {
			items.add(o);
			refilter();
		}

		private void refilter() { //完成过滤逻辑的函数
			 // public void refilter() {
	          filterItems.clear(); //清空用于显示的数据容器
	          String term = getFilterField().getText(); //获取输入的字串
	          for (int i=0; i<items.size(); i++)    //遍历原始数据容器列表
	          {
	        	  if (items.get(i).toString().indexOf(term, 0) != -1)
	        		  filterItems.add (items.get(i));        //如果满足条件被放入到过滤后的数据容器中用于显示
	          }
	          fireContentsChanged(this, 0, getSize());  //通知JList的视图更新显示数据源自filterItems
              //它会产生ListDataEvent 事件给 JList JList重新从模型中获取数据并重绘(repaint)自己.
        }
	}

	// FilterField inner class listed below
	// inner class provides filter-by-keystroke field
	class FilterField extends JTextField implements DocumentListener {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		// 在文本框中的增 删 改 都会改变其模型中的数据并激发DocumentEvent事 .在此事件发生时
		// 唯一要做的就是对JList的FilterModel重新进行过滤,过滤后JList就会显示过滤后的数据
		public FilterField(int width) {
			super(width);
			getDocument().addDocumentListener(this);
		}

		public void changedUpdate(DocumentEvent e) {
			getFModel().refilter();

		}

		public void insertUpdate(DocumentEvent e) {
			getFModel().refilter();
		}

		public void removeUpdate(DocumentEvent e) {
			getFModel().refilter();
		}


	}

	public static void main(String[] args) {
		String[] listItems = { "Chris", "Joshua", "Daniel", "Michael", "Don",
				"Kimi", "Kelly", "Keagan" };
		JFrame frame = new JFrame("FilteredJList");
		frame.getContentPane().setLayout(new BorderLayout());
		// populate list
		FilteredJList<String> list = new FilteredJList<String>();
		for (int i = 0; i < listItems.length; i++)
			list.addItem(listItems[i]);
		// add to gui
		JScrollPane pane = new JScrollPane(list,
				ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		frame.getContentPane().add(pane, BorderLayout.CENTER);
		frame.getContentPane().add(list.getFilterField(), BorderLayout.NORTH);
		frame.pack();
		frame.setVisible(true);
	}
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JList 是 Java Swing 组件中的一个列表组件,可以用来展示一组数据。以下是 JList 的用法: 1. 创建 JList 对象: ``` JList list = new JList(); ``` 2. 设置数据模型: JList 组件需要一个数据模型来存储数据,可以使用 DefaultListModel 类来创建数据模型: ``` DefaultListModel model = new DefaultListModel(); model.addElement("Item 1"); model.addElement("Item 2"); list.setModel(model); ``` 3. 设置显示样式: 可以通过设置 ListCellRenderer 对象来改变每个列表项的显示样式: ``` list.setCellRenderer(new MyListRenderer()); ``` 4. 添加到容器中: 将 JList 添加到容器中即可显示出来: ``` JScrollPane scrollPane = new JScrollPane(list); frame.getContentPane().add(scrollPane); ``` 完整示例代码: ``` import javax.swing.DefaultListModel; import javax.swing.JFrame; import javax.swing.JList; import javax.swing.JScrollPane; import javax.swing.ListCellRenderer; public class MyListRenderer implements ListCellRenderer<String> { public static void main(String[] args) { JFrame frame = new JFrame(); JList<String> list = new JList<String>(); DefaultListModel<String> model = new DefaultListModel<String>(); model.addElement("Item 1"); model.addElement("Item 2"); list.setModel(model); list.setCellRenderer(new MyListRenderer()); JScrollPane scrollPane = new JScrollPane(list); frame.getContentPane().add(scrollPane); frame.pack(); frame.setVisible(true); } public MyListRenderer() { } @Override public Component getListCellRendererComponent(JList<? extends String> list, String value, int index, boolean isSelected, boolean cellHasFocus) { JLabel label = new JLabel(); label.setText(value); if (isSelected) { label.setBackground(list.getSelectionBackground()); label.setForeground(list.getSelectionForeground()); } else { label.setBackground(list.getBackground()); label.setForeground(list.getForeground()); } label.setOpaque(true); return label; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值