`
mylxiaoyi
  • 浏览: 328506 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

List Model Controls(一)

 
阅读更多

12章探讨了支持滚动与输入或是显示某些边界范围值的边界范围组件。在本章中,我们将会探讨表示选项列表的两个选择控件:JList与JComboBox。这两个组件之间的主要区别在于JList组件支持多项选择,而JComboBox不支持。同时,JComboBox允许用户提供不在可用选项中的选项。

13.1 ListModel接口

图13-1显示了在本章中我们将要探讨的两个控件。


这两个组件之间所共享的数据模型是ListModel,从而形成了ListMode接口。AbstractListModel类通过支持ListDataListener对象的管理与通知提供了实现基础。

对于JList组件,数据模型的实现是DefaultListModel类。这个类添加了一个实际的数据仓库,其遵循Vector API,可以用于在JList组件内显示的不同的元素。

对于JComboBox组件,一个名为ComboBoxModel的ListModel接口扩展提供了在模型内选择项目的概念。DefaultComboBoxModel类通过另一个接口,MutableComboBoxModel实现了ComboBoxModel接口,MutableComboBoxModel为模型中元素的添加与移除提供了支持方法。

注意,BasicDirectoryModel类是另一个ListModel实现。这个实现为第9章所描述的文件选择器组件JFileChooser所用。

实际上ListModel接口非常简单。他提供了ListDataListener管理,并且访问模型特定元素的尺寸。

public interface ListModel {
  // Properties
  public int getSize();
  // Listeners
  public void addListDataListener(ListDataListener l);
  public void removeListDataListener(ListDataListener l);
  // Other methods
  public Object getElementAt(int index);
}


13.1.1 AbstractListModel类

AbstractListModel类提供了ListModel接口的部分实现。我们只需要提供数据结构与数据。这个类为ListDataListener的列表管理提供对象并且当数据变化时为这些监听器的通知提供框架。我们可以使用public ListDataListener[] getListDataListener()方法获取监听器列表。当我们修改数据模型时,我们必须调用AbstractListModel的相应方法来通知监听在ListDataListener对象:

  • protected void fireIntervalAdded(Object source, int index0, int index1):在向列表添加一个连续的范围值之后调用。
  • protected void fireIntervalRemoved(Object source, int index0, int index1):在由列表移除一个连续的范围值之后调用。
  • protected void fireContentsChanged(Object source, int index0, int index1):如果修改的范围对于插入,移除或是两者,不是连续的时调用。

如果我们的数据在一个已存在数据结构中,我们需要将其转换为Swing组件可以理解的格式或是我们自己实现ListModel接口。正如我们将要看到,数据或是Vector是直接为JList与JComboBox所支持的。我们可以将我们的数据结构包装进AbstractListModel。例如,如果我们的初始数据结构是集合框架中的ArrayList,我们可以使用下面的代码转换为一个ListModel:

final List arrayList = ...;
ListModel model = new AbstractListModel() {
  public int getSize() {
    return arrayList.size();
  }
  public Object getElementAt(int index) {
    return arrayList.get(index);
  }
}

另一个选择就是将List传递给Vector构造函数,然后将Vector传递给JList构造函数。事实上,我们已经完成了相同的事件。

13.1.2 DefaultListModel类

DefaultListModel类为我们提供了一个数据结构用来以Vector的形式存储内部数据。我们只需要添加数据,因为这个类为我们管理ListDataListener。

首先,我们使用无参数的构造函数创建数据结构:DefaultListModel model = new DefaultListModel()。然后我们进行冰封装填。如表13-1所示,DefaultListModel类只有两个属性。


DefaultListModel类通过一系列的公开方法提供了所有的操作方法。要添加元素,可以使用下面的方法:

public void add(int index, Object element)
public void addElement(Object element)
public void insertElementAt(Object element, int index)

DefaultListModel的addElement()方法将元素添加到数据模型的尾部。要修改元素,使用下面的方法:

public Object set(int index, Object element)
public void setElementAt(Object element, int index)

要移除元素,可以使用下面的方法:

public void clear()
public Object remove(int index)
public void removeAllElements()
public boolean removeElement(Object element)
public void removeElementAt(int index)
public void removeRange(int fromIndex, int toIndex)

removeElement()方法返回一个状态:如果他找到对象并且移除则返回true,否则返回false。

当我们并没有将数据存储在已存在数据结构中时,DefalutListModel类十分有用。例如,数据库查询的结果会作为JDBC ResultSet返回。如果我们希望使用这些结果作为显示在JList中的内容的基础,我们必须将其存储在某些地方。这就可以存储在DefaultListModel中,如下面的代码所示:

ResultSet results = aJDBCStatement.executeQuery(
   "SELECT columnName FROM tableName");
DefaultListModel model = new DefaultListModel();
while (results.next()) {
  model.addElement(result.getString(1));
}


13.1.3 使用ListDataListener监听ListModel事件

如果我们对确定列表模型的内容何时发生变化感兴趣,我们可以向模型注册一个ListDataListsener。接口的三个单独方法可以告诉我们内容何时被添加,被移除或是被修改。修改数据模型意味着由数据模型的一个或多个区域添加或移除内容或者是没有添加或是移除元素修改已存在的内容。接口定义如下:

public interface ListDataListener extends EventListener {
  public void contentsChanged(ListDataEvent e);
  public void intervalAdded(ListDataEvent e);
  public void intervalRemoved(ListDataEvent e);
}

基于列表修改事件的通知,我们可以传递一个ListDataEvent实现,其包含三修改必,如表13-2所示。


索引并不是顺序所必须的,也不是修改区域的边界。在列表模型内容修改的例子中,并不是区域中的所有内容都会被修改。内容实际变化的区域是通过索引指定的边界区域。type属性的设置是表13-3中的三个常量之一,这直接映射到所调用的接口方法。


如果当DefaultListModel类的操作方法被调用时,ListDataListener对象被关联到数据模型,每一个监听器都会得到数据模型变化的通知。为了演示ListDataListesner的使用以及数据模型的动态更新,列表13-1中的ModifyModelSample程序使用的DefaultListModel类修改方法,以事件的形式发送输出并列出JTextArea的内容。

package swingstudy.ch13;
 
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintWriter;
import java.io.StringWriter;
 
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
 
public class ModifyModelSample {
 
	static String labels[] = {"Chardonnay", "Sauvignon", "Riesling", "Cabernet",
		"Zinfandel", "Merlot", "Pinot Noir", "Sauvignon Blanc", "Syrah", "Gewurztraminer"
	};
 
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
 
		Runnable runner = new Runnable() {
			public void run() {
				JFrame frame = new JFrame("Modifying Model");
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
				// Fill model
				final DefaultListModel model = new DefaultListModel();
				for(int i=0; i<labels.length; i++) {
					model.addElement(labels[i]);
				}
 
				JList jList = new JList(model);
				JScrollPane scrollPane1 = new JScrollPane(jList);
				frame.add(scrollPane1, BorderLayout.WEST);
 
				final JTextArea textArea = new JTextArea();
				textArea.setEditable(false);
				JScrollPane scrollPane2 = new JScrollPane(textArea);
				frame.add(scrollPane2, BorderLayout.CENTER);
 
				ListDataListener listDataListener = new ListDataListener() {
					public void contentsChanged(ListDataEvent event) {
						appendEvent(event);
					}
					public void intervalAdded(ListDataEvent event) {
						appendEvent(event);
					}
					public void intervalRemoved(ListDataEvent event) {
						appendEvent(event);
					}
					private void appendEvent(ListDataEvent event) {
						StringWriter sw = new StringWriter();
						PrintWriter pw = new PrintWriter(sw);
						switch(event.getType()) {
						case ListDataEvent.CONTENTS_CHANGED:
							pw.print("Type: contents Changed");
							break;
						case ListDataEvent.INTERVAL_ADDED:
							pw.print("Type: Interval Added");
							break;
						case ListDataEvent.INTERVAL_REMOVED:
							pw.print("Type: Interval Removed");
							break;
						}
						pw.print(", Index0: "+event.getIndex0());
						pw.print(", Index1 "+event.getIndex1());
						DefaultListModel theModel = (DefaultListModel)event.getSource();
						pw.println(theModel);
						textArea.append(sw.toString());
					}
				};
 
				model.addListDataListener(listDataListener);
 
				// Set up buttons
				JPanel jp = new JPanel(new GridLayout(2,1));
				JPanel jp1 = new JPanel(new FlowLayout(FlowLayout.CENTER, 1,1));
				JPanel jp2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 1,1));
				jp.add(jp1);
				jp.add(jp2);
				JButton jb = new JButton("add F");
				jp1.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						model.add(0, "First");
					}
				});
				jb = new JButton("addElement L");
				jp1.add(jb);
				jb.addActionListener(new ActionListener(){
					public void actionPerformed(ActionEvent event) {
						model.addElement("Last");
					}
				});
				jb = new JButton("insertElementAt M");
				jp1.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						int size = model.getSize();
						model.insertElementAt("Middle", size/2);
					}
				});
				jb = new JButton("set F");
				jp1.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						int size = model.getSize();
						if (size != 0) {
							model.set(0, "New First");
						}
					}
				});
				jb = new JButton("setElementAt L");
				jp1.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						int size = model.getSize();
						if(size!=0)
							model.setElementAt("New Last", size-1);
					}
				});
				jb = new JButton("load 10");
				jp1.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						for(int i=0, n=labels.length; i<n; i++) {
							model.addElement(labels[i]);
						}
					}
				});
				jb =  new JButton("clear");
				jp2.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						model.clear();
					}
				});
				jb = new JButton("remove F");
				jp2.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						int size = model.getSize();
						if(size !=0)
							model.remove(0);
					}
				});
				jb = new JButton("removeAllElements");
				jp2.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						model.removeAllElements();
					}
				});
				jb = new JButton("removeElement 'Last'");
				jp2.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						model.removeElement("Last");
					}
				});
				jb = new JButton("removeElementAt M");
				jp2.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						int size = model.getSize();
						if(size != 0)
							model.removeElementAt(size/2);
					}
				});
				jb = new JButton("removeRange FM");
				jp2.add(jb);
				jb.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent event) {
						int size = model.getSize();
						if(size !=0)
							model.removeRange(0, size/2);
					}
				});
				frame.add(jp, BorderLayout.SOUTH);
				frame.setSize(640, 300);
				frame.setVisible(true);
			}
		};
		EventQueue.invokeLater(runner);
	}
 
}

图13-2显示了程序的运行结果。


DefaultListModel类的获取方法会随着他们的功能而变化。这个类具有基本的访问方法public Object get(int index), public Object getElementAt(int index)以及public Object elementAt(int index),这三个方法都可以完成相同的事情。DefaultListModel类同时也具有更为特殊的方法。例如,为了使用所有的元素,我们可以使用public Enumeration elements()方法获取Enumeration的实例。

或者是如果我们希望以数组的方式操作所有的元素,可以使用public Object[] toArray()或是public void copyInto(Object anArray[])。我们也可以使用方法来检测模型中是否存在某一个元素,public boolean contains(Object element), public int indexOf(Object element), public int indexOf(Object element, int index), public int lastIndexOf(Object element)以及public int lastIndexOf(Object element, int index)。

提示,一旦我们完成了向数据模型添加元素,使用public void trimToSize()方法修整其长度是个好主意。这会移除数据结构内部所分配的额外空间。另外,如果我们知道数据尺寸,我们可以调用public void ensureCapacity(int minCapacity)来预分配空间。这两种方法都可以用于DefaultListModel。

13.1.4 ComboBoxModel接口

ComboBoxModel接口扩展了ListModel接口。扩展的主要原因是因为实现ComboBoxModel接口的类需要通过selectedItem属性来管理被选中的项目,如下面的接口定义所示:

public interface ComboBoxModel extends ListModel {
  // Properties
  public Object getSelectedItem();
  public void setSelectedItem(Object anItem);
}


13.1.5 MutableComboBoxModel接口

除了ComboBoxModel接口以外,另一个数据模型接口MutableComboBoxModel扩展了ComboBoxModel从而构成了可以修改数据模型的方法。

public interface MutableComboBoxModel extends ComboBoxModel {
  // Other methods
  public void addElement(Object obj);
  public void insertElementAt(Object obj, int index);
  public void removeElement(Object obj);
  public void removeElementAt(int index);
}

JComboBox组件默认使用这个接口的实现。

13.1.6 DefaultComboBoxModel类

DefaultComboBoxModel类扩展了AbstractListModel类来为JComboBox提供相应的方法。由于这种扩展,他继承了ListDataListener列表的管理。

类似于DefaultListModel,DefaultComboBoxModel为我们添加了收集显示在组件中的元素所必需的数据结构。同时,由于模型是可修改的,实现MutableComboBoxModel会使得当模型中的数据元素发生变化时,数据模型调用AbstractListModel的各种fileXXX()方法。

注意,如果我们由一个数组创建了DefaultComboBoxModel,数组的元素会被拷贝到一个内部数据结构中。如果我们使用Vector,他们不会被拷贝;相反,在内部会使用实际的Vector。

要使用数据模型,我们必须首先使用下面的构造函数来创建模型:

public DefaultComboBoxModel()
DefaultComboBoxModel model = new DefaultComboBoxModel();
public DefaultComboBoxModel(Object listData[])
String labels[] = { "Chardonnay", "Sauvignon", "Riesling", "Cabernet", "Zinfandel",
  "Merlot", "Pinot Noir", "Sauvignon Blanc", "Syrah", "Gewürztraminer"};
DefaultComboBoxModel model = new DefaultComboBoxModel(labels);
public DefaultComboBoxModel(Vector listData)
Vector vector = aBufferedImage.getSources();
DefaultComboBoxModel model = new DefaultComboBoxModel(vector);

然后,我们操作模型。DefaultComboBoxModel类引入了两个新属性,如表13-4所示。


DefaultComboBoxModel的数据模型修改方法不同于DefaultListModel的模型修改方法。他们来自于MutableComboBoxModel接口:

public void addElement(Object element)
public void insertElementAt(Object element, int index)
public boolean removeElement(Object element)
public void removeElementAt(int index)

由于DefaultComboBoxModel的灵活性(以及功能性),通常并不需要创建我们自己的ComboBoxModel实现。只需要创建一个DefaultComboBoxModel实例,然后简单的使用相应的数据源对其进行装配。

注意,我们也许希望提供我们自己模型的一个例子就是当我们需要支持模型多个项目中相同项目的表示。对于DefaultComboBoxModel,如果我们在其equals()方法返回true的列表中有两个项目,模型不会正确的工作。

如果我们确实希望定义我们自己的模型实现,也许是因为在我们已经有数据存储在我们的数据结构中,最好的方法就是继承AbstractListModel并且实现ComboBoxModel或是MutableComboBoxModel接口方法。当继承AbstractListModel时,我们只需要提供数据结构以及对其的访问接口。因为数据模型的“选中项目”部分是在基本的数据结构之外进行管理的,我们也需要提供的一个位置进行存储。列表13-2中的程序源码演示了使用ArrayList作为数据结构的实现。程序包含main()方法来演示JComboBox中模型的使用。

package swingstudy.ch13;
 
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Collection;
 
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
 
public class ArrayListComboBoxModel extends AbstractListModel implements ComboBoxModel{
 
	private Object selectedItem;
	private ArrayList anArrayList;
 
	public ArrayListComboBoxModel(ArrayList arrayList) {
		anArrayList = arrayList;
	}
 
	public Object getSelectedItem() {
		return selectedItem;
	}
 
	public void setSelectedItem(Object newValue) {
		selectedItem = newValue;
	}
 
	public int getSize() {
		return anArrayList.size();
	}
 
	public Object getElementAt(int i) {
		return anArrayList.get(i);
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
 
		Runnable runner = new Runnable() {
			public void run() {
				JFrame frame = new JFrame("ArrayListComboBoxModel");
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
				Collection<Object> col = System.getProperties().values();
				ArrayList<Object> arrayList = new ArrayList<Object>(col);
				ArrayListComboBoxModel model = new ArrayListComboBoxModel(arrayList);
 
				JComboBox comboBox = new JComboBox(model);
 
				frame.add(comboBox, BorderLayout.NORTH);
				frame.setSize(300, 225);
				frame.setVisible(true);
			}
		};
		EventQueue.invokeLater(runner);
	}
 
}


图13-3实际显示了使用当前的系统属性作为数据模型元素的数据源中模型。


分享到:
评论

相关推荐

    wowmodelview 含源代码

    WOW模型查看器,有源代码. WoWmodelview - World of Warcraft 3D model viewer This program displays 3D models from World of Warcraft: characters, creatures, spell ...- Equipment: choose from the popup list

    C#浏览器编程,学习使用

    List&lt;int&gt; cishu = new List(); //储存寻找关键字符的位置 int i = 0; //记录列表cishu的下标 } } //***********************************************// // // // 启动程序运行时 // // // //*************...

    qtquick-trello:使用 QtQuick Controls 的 trello.com 客户端

    在qtquick-trello项目中,开发者首先会定义一个主窗口(Window),在其中嵌入各种QtQuick Controls组件来模拟Trello的看板(Board)、列表(List)和卡片(Card)的布局。例如,使用ListView来展示Trello的列表,每...

    EXCEL 2007 宝典 附光盘文件

    worksheet controls.xlsm:一个包含所有用于一个工作表的ActiveX控件的例子的工作簿。 Chapter 43 monitor a range.xlsm:一个包含VBA Worksheet_Change宏用于控制指定区域的所有更改的工作簿。 selection change ...

    get_form winform获取表单

    List&lt;ControlInfo&gt; controls = new List(); foreach (Control control in this.Controls) { controls.Add(new ControlInfo { TypeName = control.GetType().Name, Name = control.Name, Location = control....

    Qt Quick自定义下拉框

    height: label.height + list.height + padding property alias model: listView.model property alias currentIndex: listView.currentIndex property bool open: false // 其他属性,如padding, ...

    如何使QtQML中ListView从底往上排列.docx

    1. `model`:定义数据模型,可以是`ListModel`、`PropertyList`或者其他类型的数据模型,用于提供列表中的数据项。 2. `delegate`:定义每个列表项的外观和行为,通常是一个包含QML元素的组件。 3. `height`和`width...

    KSDev DXScene v4.42 for Delphi BCB Lazarus Full Source

    Powerful 2D vector engine like Adobe Flash or Microsoft WPF includes high quality shapes and controls like window, label, button, checkbox, textbox and more Advanced GUI engine - window, button, ...

    occam一维反演

    c model found, but rather the response of the last model tested in the univariate c optimization routines, we leave it to the user to find the response of c the final model, using low cunning or the ...

    Programming ASP.NET

    These include topics such as using ASP.NET's list-bound controls; accessing data using the ADO.NET object model, and updating data with or without transaction support. Programming ASP.NET also ...

    Jump Start Aspen HYSYS V8

    创建一个新案例通常包括几个步骤:在“属性环境”(The Properties Environment)中创建一个组分列表(Creating a Component List),选择一个流体包(Selecting a Fluid Package),然后在“模拟环境”(The ...

    asp.net mvc chart(MVC 使用chart图表控件的示例)

    private List&lt;SalesItem&gt; GetSalesData() { // 这里填充数据,例如从数据库查询 // ... } } ``` 接下来,创建一个对应的视图`Index.cshtml`,在这个视图中我们将使用Chart控件来渲染图表。首先,我们需要在...

    WPF Programmer's Reference: Windows Presentation Foundation with C# 2010 and .NET 4

    The chapters list various properties such as `Width`, `Height`, `Background`, `FontFamily`, and `Visibility`, explaining their usage and providing examples. #### Content Controls (Chapter 5 & ...

    Enhanced Host Controller Interface Specification for Universal Serial Bus

    - **Periodic Frame List:** Contains a list of isochronous transfer descriptors that define the periodic transfers within a frame. - **Asynchronous List Queue Head Pointer:** Points to the head of the ...

    js用户中心tab标签切换特效.zip

    7. ** Accessibility**:考虑到无障碍性(A11y),确保Tab组件符合WCAG标准,例如使用ARIA属性(`aria-selected`、`aria-controls`等)来辅助屏幕阅读器用户。 在“1197”这个文件中,可能包含了HTML文件(用于结构...

    外文翻译 stus MVC

    ActionErrors is Struts way of keeping up with a list of errors. The ActionMapping class An incoming event is normally in the form of an HTTP request, which the servlet Container turns into an ...

    MP3等音乐播放器c#

    你可以创建一个`List`或`ObservableCollection`来存储歌曲信息,包括文件路径、艺术家、专辑等。这样用户可以添加、删除和排序歌曲。同时,你可以利用`ICollectionView`来实现分页和过滤功能,提高用户体验。 ```...

    c# 温度采集控件折线图

    List&lt;double&gt; temperatureData = new List(); // 将读取到的温度值添加到列表 temperatureData.Add(Convert.ToDouble(indata)); ``` 为了展示温度数据,C#提供了多种图形库,如ZedGraph、OxyPlot等。这里以OxyPlot...

    asp.net mvc @html用法

    @Html.DropDownList("Country", new SelectList(Model.Countries, "Value", "Text")) ``` - **ListBox** - 生成多选列表框控件。 ```csharp @Html.ListBox("Fruits", new MultiSelectList(Model.Fruits, "Id", ...

    C# ASP.NET获取音乐信息(标题,地址,缩略图)

    @model List @foreach (var song in Model) { &lt;h2&gt;@song.Title &lt;audio src="@song.Url" controls&gt; } ``` 至此,我们已经展示了如何使用C# ASP.NET获取虾米音乐的音乐信息,并在网页上呈现。注意,实际操作时...

Global site tag (gtag.js) - Google Analytics