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

Swing JTable 渲染器 进度条 事件线程 观察者模式

    博客分类:
  • Java
阅读更多

Swing编程中JTable应该是个经常被用到的组件,进度条也不赖,有了它,不至于给用户“程序是不是死掉了”的疑惑,当然如果能做到像迅雷等下载工具那样,把表格和进度条组合在一起,那就太酷了!
好,下面就来看下这种界面在swing中是如何使用Jtable和JprogressBae实现的。
首先来看JTable,需要定制一个模型,既一个实现了TableModel接口的数据模型类

	/**
	 * 状态区表格模型类
	 * @author SavageGarden
	 *
	 */
	class StatusTableModel extends DefaultTableModel {
		public Object[] rowData = {"..", 0, "", "", ""};
		public StatusTableModel() {
			super();
			addColumn("Name");
			addColumn("Status");
			addColumn("Size");
			addColumn("Speed");
		}
		/**
		 * 设置为不可编辑
		 */
		public boolean isCellEditable(int row, int column) {
	        return false;
	    }
		/**
		 * 向状态区添加一个进度条
		 *
		 */
		public void addProgressBar() {
			rowData[0] = "文件" + (this.getRowCount() + 1);
			addRow(rowData);
		}
	}
	


如果想把某列显示成进度条的话,还需要定制某列单元格的渲染器,既一个实现了TableCellRenderer接口的类

	/**
	 * 工具条的渲染器
	 * @author SavageGarden
	 *
	 */
	class ProgressBarRenderer extends DefaultTableCellRenderer{
		private static final long serialVersionUID = 1L;
	    private final JProgressBar b;
	    public ProgressBarRenderer(){
	    	super();
	        setOpaque(true);
	        b = new JProgressBar();
			//是否显示进度字符串
			b.setStringPainted(true);
			b.setMinimum(0);
			b.setMaximum(100);
			//是否绘制边框
			b.setBorderPainted(true);
	        b.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
	    }
		public Component getTableCellRendererComponent(JTable table, Object value,
	            boolean isSelected, boolean hasFocus, int row, int column) {
	        Integer i = (Integer) value;
	        b.setValue(i);
	        return b;
	    }
	}
	


然后来做一个按钮的监听类,实现ActionListener接口,当点击按钮时就调用表格模型的addProgressBar()方法,

	/**
	 * 按钮的事件响应类
	 * @author SavageGarden
	 *
	 */
	class AddButtonActionListener implements ActionListener {

		public void actionPerformed(ActionEvent e) {
			statusTableModel.addProgressBar();
		}
		
	}
	


然后来把这些代码组织到一起

	/**
 	* 主面板,显示表格和按钮
* @author SavageGarden
 	*
 	*/
	class FrameTest extends JFrame {
		private static int WIDTH;
		private static int HEIGHT;
		public JButton addButton;
		public JScrollPane statusScrollPane1;
		public static JTable statusTable;
		public static StatusTableModel statusTableModel;
		public FrameTest() {
			Toolkit kit = Toolkit.getDefaultToolkit();
			Dimension screenSize = kit.getScreenSize();
			WIDTH = screenSize.width/2;
			HEIGHT = screenSize.height/2;
			setSize(WIDTH, HEIGHT);
			setLocationRelativeTo(null);
			setResizable(false);
			setLayout(null);
			setTitle("FrameTest");
			setVisible(false);
			createStatusPanel();
			statusScrollPane1.setBounds(0, 0, WIDTH, 330);
			getContentPane().add(statusScrollPane1);
			addButton = new JButton("添加");
			addButton.setBounds(WIDTH/2 -40, 350 , 80, 20);
			addButton.addActionListener(new AddButtonActionListener());
			getContentPane().add(addButton);
		}
		/**
	 	* 创建状态区
	 	*
	 	*/
		private void createStatusPanel() {
			statusTableModel = new StatusTableModel();
			statusTable = new JTable(statusTableModel);
			//设置"Status"列由定制的ProgressBarRenderer渲染
			TableColumn statusColumn = statusTable.getColumn("Status");
	    	statusColumn.setCellRenderer(new ProgressBarRenderer());
			statusScrollPane1 = new JScrollPane(statusTable);
		}
}
	


最后写个主程序来调用

	/**
 	* 主程序
* @author SavageGarden
 	*
 	*/
	public class SwingTest {
		public static void main(String args[]) {
			FrameTest frame = new FrameTest();
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setVisible(true);
		}
}
	


好了,现在可以看到这样的界面了

进度条是添加上了,但是要怎么样才能让它动起来呢?
因为我们使用了定制的渲染器ProgressBarRenderer,它实现了TableCellRenderer接口的
public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column)
方法

public Component getTableCellRendererComponent(JTable table, Object value,
	            boolean isSelected, boolean hasFocus, int row, int column) {
Integer i = (Integer) value;
	b.setValue(i);
	return b;
}


所以只需要调用statusTableModel的setValueAt(Object aValue, int row, int column)方法即可
为了模拟进度,将AddButtonActionListener的按钮事件处理修改为以下代码

public void actionPerformed(ActionEvent event) {
statusTableModel.addProgressBar();
	int price = 0;
int index = statusTableModel.getRowCount()-1;
	while (price < 100) {  
		try {  
	    	Thread.sleep(10);  
	    } catch (InterruptedException e) {  
	        e.printStackTrace();  
	    }  
	    price ++;
	    statusTableModel.setValueAt(price, index, 1);
	}
}



点击“添加”按钮,程序好像死掉了, 100 * 10 ms后,表格中突然出现了新添加的一行数据,而且进度条已经到达了百分之百的状态。这是什么原因呢?
《JFC核心编程第2版》中665页“进度条和事件线程”一小节写的非常透彻,摘抄如下:
理想情况下,在使用进度条时,应用程序代码只须用适当的值来调用setValue方法,重新绘制的工作是由控件自身完成的,以反映工作的进度。但实际上,事情并非如此简单。有两种不同的情况需要考虑,这取决于操作时在AWT事件线程中执行,还是在另一个线程中执行。
在AWT事件线程中执行的操作
当应用程序在事件线程中执行长时间的操作时,会阻塞正常的AWT事件处理,因此阻止了重绘操作的发生。这通常会发生在下列情况下发生:应用程序响应一个来自于用户界面的请求时,在连接到一个按钮或其它GUI组件的事件处理程序中执行任务,任务的内容可能涉及计算质数的循环或在网络上进行一系列同步调用,后者会使事件线程挂起,直至远程系统发出答复为止。当应用程序调用JprogressBar的setValue方法时,进度条可能更新其内部状态并调用repaint,这样做会把一个事件放置到AWT事件队列中。不幸的是,直至应用程序的事件处理程序完成其处理并把控制权返回到线程的事件处理循环,才能处理该事件。

然后还有667页的一段
在另一个线程中执行的操作
要避免前一个例子中的问题,另一种方法就是不允许在事件线程中进行计算。如果在一个单独的线程中执行该操作,当调用进度条的setValue方法,它的更新就不会出现任何问题,因为即使工作线程正忙于计算或连接网络资源,事件线程的执行也不会受到影响。这种途径的问题在于,后台线程必须调用JprogressBar的setValue,而我们在第2章中提出过,Swing组件只有在事件线程中才能安全的访问。因此,从执行实际工作的线程调用setValue方法时不安全的!幸而有一个简单的途径可以绕过该问题---你只需要使用SwingUtilities的invokeLater方法,让AWT事件线程稍后进行setValue调用。

书中还给出了几个例子,理论加实践,非常容易理解,那么现在结合上面的理论,再把AddButtonActionListener的按钮事件处理修改一下

public void actionPerformed(ActionEvent event) {
statusTableModel.addProgressBar();
	Thread t = new ProgressBarThread(); 
	t.start();
}


来看下ProgressBarThread的run方法中做了什么

/**
* 进度条线程
* @author SavageGarden
*
 */
class ProgressBarThread extends Thread {
int price = 0;
int index = statusTableModel.getRowCount()-1;
	public void run() {
		while (price < 100) {  
	    	try {  
	        	Thread.sleep(10);  
	        } catch (InterruptedException e) {  
	            e.printStackTrace();  
	        }  
	        price ++;
	        SwingUtilities.invokeLater(new Runnable(){
        		public void run() {  
        	    	statusTableModel.setValueAt(price, index, 1);   
        	    }  
        	});
	   	}  
	}
}



可以看到每点击一次“添加”就会在表格中多一行,进度条之间并没有影响。
问题好像已经得到了完美的解决,但是常见的一种情况是:点击按钮或菜单,后台线程开始连接网络或者进行复杂的运算时,后台程序还要调用statusTableModel的setValueAt方法,如果将对前台界面组件的调用也写在后台程序中,对于习惯了“MVC”人们好像有点会感觉很不爽,那么就用观察者模式来解决这个问题吧,而且java.util. Observable、java.util. Observer已经做了观察对象和观察者的实现。

/**
* 用于实现观察者观察的对象---进度条的显示值
* @author SavageGarden
*
*/
class ProgressBarObservable extends Observable {
	private String price;
	public String getPrice() {
		return price;
	}
	public void setPrice(String price) {
		this.price = price;
	    setChanged();
	    notifyObservers(price);
	}
}
/**
* 用于实现观察者 ---进度条的显示值变化时更新statusTableModel
* @author SavageGarden
*
 */
class ProgressBarObserver implements Observer {
public void update(Observable o, Object arg) {
		String showValue = (String)arg;
        statusTableModel.setValueAt(Integer.parseInt(showValue.split(":")[1]), Integer.parseInt(showValue.split(":")[0]), 1);
	}
}


然后在FrameTest的构造函数中添加调用

progressBarObservable = new ProgressBarObservable();
progressBarObserver = new ProgressBarObserver();
progressBarObservable.addObserver(progressBarObserver);


然后将ProgressBarThread中的run方法修改为

SwingUtilities.invokeLater(new Runnable(){
public void run() {  
    	//statusTableModel.setValueAt(price, index, 1);  
       	progressBarObservable.setPrice(rowIndex + ":" + price);
    }  
});


运行,即可达到图三相同的效果。

 

  • 大小: 23.7 KB
  • 大小: 20.9 KB
  • 大小: 36 KB
分享到:
评论
2 楼 bigkai13 2009-12-10  
很好的学习资料,期待Java桌面级程序能流行起来
1 楼 on_the_windy 2009-12-08  
恩,不错,我也很久没做swing了,真让人怀念啊

相关推荐

    Java Swing JTable分页

    ### Java Swing JTable 分页实现详解 #### 一、引言 在开发基于Java Swing的应用程序时,我们经常需要处理大量的数据展示问题。对于表格形式的数据展示,`JTable`是一个非常常用且强大的组件。然而,当数据量过大时...

    Java Swing实现JTable检测单元格数据变更事件的方法示例

    Java Swing 实现 JTable 检测单元格数据变更事件的方法示例 Java Swing 中的 JTable 是一个功能强大且灵活的表格组件,广泛应用于各种桌面应用程序中。然而,在实际开发中,我们经常需要检测单元格数据的变更事件...

    Swing JTable组件设置单元格合并

    Swing JTable组件设置单元格合并,内置Test测试类,核心API GridBagTable tasktable = new GridBagTable(model); tasktable.mergeCells(startRow, endRow, 0, 0);

    java Swing Jtable 下拉动态加载数据

    JTable实现下拉动态加载数据,滑动动态加载数据,纯原生态java。

    Java Swing中JTable渲染器与编辑器用法示例

    Java Swing中JTable渲染器与编辑器用法示例 Java Swing中JTable渲染器与编辑器用法示例是Java Swing中一个重要的组件,主要用于显示和编辑表格数据。在Java Swing中,JTable是最基本的表格组件,渲染器和编辑器是...

    Swing之JTable+JComboBox的详细介绍[zone yan]

    在Java的Swing库中,`JTable`和`JComboBox`是两个非常重要的组件,它们常被用于创建用户界面,展示数据并提供交互性。`JTable`用于展示二维表格数据,而`JComboBox`则是一种下拉选择框,可以提供多个选项供用户选择...

    java swing 导出Jtable里面的数据到excel

    java swing 导出Jtable里面的数据到excel,不用拷贝代码,直接粘贴代码

    swing Jtable使用checkbox

    swing Jtable使用checkboxswing Jtable使用checkboxswing Jtable使用checkboxswing Jtable使用checkboxswing Jtable使用checkboxswing Jtable使用checkboxswing Jtable使用checkboxswing Jtable使用checkboxswing ...

    Swing中JTABLE中添加JBUTTON控件

    在Java的Swing库中,`JTable`是用于创建数据网格视图的重要组件,它允许用户以表格的形式查看和操作数据。而`JButton`则是一个常用的按钮控件,通常用于触发某些动作或事件。将`JButton`添加到`JTable`中可以为用户...

    Swing之JTable的详细介绍

    - `addMouseListener(MouseListener listener)`和`addKeyListener(KeyListener listener)`: 添加鼠标和键盘事件监听器,以响应用户的交互操作。 - `getValueAt(int row, int col)`和`setValueAt(Object value, int ...

    Swing JTable 常用属性

    Swing JTable 常用属性详解 Swing JTable 是 Java 中一个常用的表格控件,用于显示和编辑表格数据。在实际开发中,经常需要对 JTable 进行各种设置和操作,这篇文章将详细介绍 JTable 的常用属性和方法。 一、创建...

    Java Swing高级空件JTable的用法

    Java Swing高级空件JTable的用法 Java Swing中的JTable组件是用于显示和编辑数据的表格控件。它是Swing中最复杂和强大的组件之一,具有许多高级功能和灵活的配置选项。下面将详细介绍JTable的用法和相关知识点。 1...

    Jtable 中放入多个按钮

    首先,`JTable`默认的渲染器和编辑器无法直接支持在单元格内放置多个组件,如按钮。因此,我们需要创建一个自定义的`TableCellRenderer`来实现这个功能。一个简单的实现方式是创建一个`JPanel`,并在其中添加多个`...

    用Java Swing Jtable做的日历

    用Java Swing Jtable做的日历

    Swing之JTable用法

    Swing之JTable用法

    Swing之JTable详解

    ### Swing之JTable详解 #### 类层次结构图 在Swing框架中,`JTable`是构建GUI应用程序中表格的主要组件。它继承自`JComponent`类,这使得`JTable`能够支持丰富的图形用户界面功能。下面展示了`JTable`的类层次结构...

    共享一个Swing JTable组件的CheckBox管理类

    1. **初始化复选框**:为JTable的某一列创建并设置CheckBox作为默认的单元格编辑器和渲染器。 2. **状态同步**:确保当用户通过界面勾选或取消复选框时,模型中的对应数据也会更新,反之亦然。 3. **事件处理**:...

    JTable颜色渲染代码

    根据给定文件的信息,本文将围绕“JTable颜色渲染代码”这一主题展开,重点解析JTable的基本操作、初始化方法及高级的颜色渲染功能。 ### JTable简介 JTable是Swing库中的一个组件,用于在Java应用程序中显示表格...

    复杂JTable-跨列表头

    实现跨列表头涉及的主要技术包括Swing的自定义组件、UI委托、渲染器以及事件处理。开发者需要深入理解Swing组件模型,才能有效地扩展和定制JTable的行为。在实际应用中,这样的功能可以显著提高用户体验,尤其是在...

Global site tag (gtag.js) - Google Analytics