how2j.cn

本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频



22分42秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)



示例 1 : 基本表格   
示例 2 : JScrollPane   
示例 3 : 列宽   
示例 4 : TableModel   
示例 5 : 进一步理解TableModel   
示例 6 : TableModel 与DAO结合   
示例 7 : TableSelectionModel   
示例 8 : 更新Table   
示例 9 : 输入项验证   
示例 10 : 选中指定行   

示例 1 :

基本表格

显示一个Table需要两组数据
1. 一维数组: String[]columnNames 表示表格的标题
2. 二维数组: String[][] heros 表格中的内容
默认情况下,表格的标题是不会显示出来了,除非使用了JScrollPane
基本表格
package gui; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JTable; public class TestGUI { public static void main(String[] args) { JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(200, 200); f.setLayout(new BorderLayout()); // 表格上的title String[] columnNames = new String[] { "id", "name", "hp", "damage" }; // 表格中的内容,是一个二维数组 String[][] heros = new String[][] { { "1", "盖伦", "616", "100" }, { "2", "提莫", "512", "102" }, { "3", "奎因", "832", "200" } }; JTable t = new JTable(heros, columnNames); f.add(t, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
示例 2 :

JScrollPane

JScrollPane: 带滚动条的Panel
把table放进去就可以看到table的title
同样的把textarea放进去,并且textarea内容够长的话,就会看到滚动条
JScrollPane
package gui; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; public class TestGUI { public static void main(String[] args) { JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(200, 200); f.setLayout(new BorderLayout()); String[] columnNames = new String[] { "id", "name", "hp", "damage" }; String[][] heros = new String[][] { { "1", "盖伦", "616", "100" }, { "2", "提莫", "512", "102" }, { "3", "奎因", "832", "200" } }; JTable t = new JTable(heros, columnNames); // 根据t创建 JScrollPane JScrollPane sp = new JScrollPane(t); //或则创建一个空的JScrollPane,再通过setViewportView把table放在JScrollPane中 // JScrollPane sp = new JScrollPane(t); // sp.setViewportView(t); // 把sp而非JTable加入到JFrame上, f.add(sp, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
示例 3 :

列宽

设置列宽度
列宽
package gui; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; public class TestGUI { public static void main(String[] args) { JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(200, 200); f.setLayout(new BorderLayout()); String[] columnNames = new String[] { "id", "name", "hp", "damage" }; String[][] heros = new String[][] { { "1", "盖伦", "616", "100" }, { "2", "提莫", "512", "102" }, { "3", "奎因", "832", "200" } }; JTable t = new JTable(heros, columnNames); JScrollPane sp = new JScrollPane(t); // 设置列宽度 t.getColumnModel().getColumn(0).setPreferredWidth(10); f.add(sp, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
示例 4 :

TableModel

首先说下TableModel的设计思想,在Model这种思想的指导下,数据和显示分离开来了。 比如对于JTable而言,有数据部分,也有显示部分(比如列宽等信息)。 数据部分,专门做一个类,叫做TableModel,就用于存放要显示的数据。

使用TableModel的方式存放Table需要显示的数据
HeroTableModel 继承AbstractTableModel ,进而实现了接口TableModel
在HeroTableModel 中提供一个table显示需要的所有信息
1. getRowCount 返回一共有多少行
2. getColumnCount 返回一共有多少列
3. getColumnName 每一列的名字
4. isCellEditable 单元格是否可以修改
5. getValueAt 每一个单元格里的值

当图形界面需要渲染第一个单元格的数据的时候,就会调用方法TabelModel的getValueAt(0,0) ,把返回值拿到并显示
package gui; import javax.swing.table.AbstractTableModel; public class HeroTableModel extends AbstractTableModel { String[] columnNames = new String[] { "id", "name", "hp", "damage" }; String[][] heros = new String[][] { { "1", "盖伦", "616", "100" }, { "2", "提莫", "512", "102" }, { "3", "奎因", "832", "200" } }; // 返回一共有多少行 public int getRowCount() { // TODO Auto-generated method stub return heros.length; } // 返回一共有多少列 public int getColumnCount() { // TODO Auto-generated method stub return columnNames.length; } // 获取每一列的名称 public String getColumnName(int columnIndex) { // TODO Auto-generated method stub return columnNames[columnIndex]; } // 单元格是否可以修改 public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } // 每一个单元格里的值 public Object getValueAt(int rowIndex, int columnIndex) { // TODO Auto-generated method stub return heros[rowIndex][columnIndex]; } }
package gui; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; public class TestGUI { public static void main(String[] args) { JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(200, 200); f.setLayout(new BorderLayout()); //创建一个TableModel HeroTableModel htm= new HeroTableModel(); //根据 TableModel来创建 Table JTable t = new JTable(htm); JScrollPane sp = new JScrollPane(t); f.add(sp, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
示例 5 :

进一步理解TableModel

在使用TableModel之前,是使用

String[] columnNames =。。。
String[][] heros = 。。。
JTable t = new JTable(heros, columnNames);

这样的风格创建一个JTable的
所以实际上调用的是如下的构造方法:

JTable(Object[][] rowData, Object[] columnNames)

如图所示,在JTable的的源代码中,它就会根据rowData和columnNames去创建一个TableModel对象
进一步理解TableModel
示例 6 :

TableModel 与DAO结合

通过TableModel与DAO结合显示数据库中Hero信息。
DAO使用HeroDAO
在TableModel中,使用从DAO返回的List作为TableModel的数据

只需要修改HeroTableModel,无需修改TestGUI。 这正好演绎了Model设计思想中的数据分离的好处,当只需要数据发生变化的时候,修改Model即可,界面GUI部分,不需要做任何改动
TableModel 与DAO结合
package gui; import java.util.List; import javax.swing.table.AbstractTableModel; import jdbc.HeroDAO; import charactor.Hero; public class HeroTableModel extends AbstractTableModel { String[] columnNames = new String[] { "id", "name", "hp", "damage" }; // 使用从DAO返回的List作为TableModel的数据 public List<Hero> heros = new HeroDAO().list(); // heros.size返回一共有多少行 public int getRowCount() { // TODO Auto-generated method stub return heros.size(); } public int getColumnCount() { // TODO Auto-generated method stub return columnNames.length; } public String getColumnName(int columnIndex) { // TODO Auto-generated method stub return columnNames[columnIndex]; } public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } // 先通过heros.get(rowIndex)获取行对应的Hero对象 // 然后根据columnIndex返回对应的属性 public Object getValueAt(int rowIndex, int columnIndex) { Hero h = heros.get(rowIndex); if (0 == columnIndex) return h.id; if (1 == columnIndex) return h.name; if (2 == columnIndex) return h.hp; if (3 == columnIndex) return h.damage; return null; } }
示例 7 :

TableSelectionModel

通过table可以获取一个 TableSelectionModel,专门用于监听jtable选中项的变化
TableSelectionModel
package gui; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import charactor.Hero; public class TestGUI { public static void main(String[] args) { JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(200, 200); f.setLayout(new BorderLayout()); final HeroTableModel htm = new HeroTableModel(); final JTable t = new JTable(htm); // 准备一个Panel上面放一个Label用于显示哪条被选中了 JPanel p = new JPanel(); final JLabel l = new JLabel("暂时未选中条目"); p.add(l); JScrollPane sp = new JScrollPane(t); // 使用selection监听器来监听table的哪个条目被选中 t.getSelectionModel().addListSelectionListener( new ListSelectionListener() { // 当选择了某一行的时候触发该事件 public void valueChanged(ListSelectionEvent e) { // 获取哪一行被选中了 int row = t.getSelectedRow(); // 根据选中的行,到HeroTableModel中获取对应的对象 Hero h = htm.heros.get(row); // 更新标签内容 l.setText("当前选中的英雄是: " + h.name); } }); f.add(p, BorderLayout.NORTH); f.add(sp, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
示例 8 :

更新Table

以新增数据到数据库中,然后更新Table为例
更新Table
package gui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import jdbc.HeroDAO; import charactor.Hero; public class TestGUI { public static void main(String[] args) { JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(200, 200); f.setLayout(new BorderLayout()); final HeroTableModel htm = new HeroTableModel(); final JTable t = new JTable(htm); // 增加 一个 panel用于放置名称,血量输入框和增加 按钮 JPanel p = new JPanel(); final JLabel lName = new JLabel("名称"); final JTextField tfName = new JTextField(""); final JLabel lHp = new JLabel("血量"); final JTextField tfHp = new JTextField(""); JButton bAdd = new JButton("增加"); tfName.setPreferredSize(new Dimension(80, 30)); tfHp.setPreferredSize(new Dimension(80, 30)); p.add(lName); p.add(tfName); p.add(lHp); p.add(tfHp); p.add(bAdd); // 为增加按钮添加监听 bAdd.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { HeroDAO dao = new HeroDAO(); // 根据输入框数据创建一个Hero对象 Hero h = new Hero(); h.name = tfName.getText(); h.hp = Integer.parseInt(tfHp.getText()); // 通过dao把该对象加入到数据库 dao.add(h); // 通过dao更新tablemodel中的数据 htm.heros = dao.list(); // 调用JTable的updateUI,刷新界面。 // 刷新界面的时候,会到tablemodel中去取最新的数据 // 就能看到新加进去的数据了 t.updateUI(); } }); JScrollPane sp = new JScrollPane(t); f.add(p, BorderLayout.NORTH); f.add(sp, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
示例 9 :

输入项验证

如果用户输入的名称为空,或者血量不是小数,在提交数据的时候都会报错。
“感觉上” 界面就卡住了。 这是不友好的人机交互行为。
所以需要加上输入项的验证,如果输入的数据不合格,应该弹出对话框提示用户具体原因。
输入项验证
package gui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import jdbc.HeroDAO; import charactor.Hero; public class TestGUI { public static void main(String[] args) { final JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(200, 200); f.setLayout(new BorderLayout()); final HeroTableModel htm = new HeroTableModel(); final JTable t = new JTable(htm); JPanel p = new JPanel(); final JLabel lName = new JLabel("名称"); final JTextField tfName = new JTextField(""); final JLabel lHp = new JLabel("血量"); final JTextField tfHp = new JTextField(""); JButton bAdd = new JButton("增加"); tfName.setPreferredSize(new Dimension(80, 30)); tfHp.setPreferredSize(new Dimension(80, 30)); p.add(lName); p.add(tfName); p.add(lHp); p.add(tfHp); p.add(bAdd); bAdd.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { HeroDAO dao = new HeroDAO(); Hero h = new Hero(); String name = tfName.getText(); // 通过name长度判断 名称是否为空 if (name.length() == 0) { // 弹出对话框提示用户 JOptionPane.showMessageDialog(f, "名称不能为空"); // 名称输入框获取焦点 tfName.grabFocus(); return; } String hp = tfHp.getText().trim(); try { // 把hp转换为浮点型,如果出现异常NumberFormatException表示不是浮点型格式 Float.parseFloat(hp); } catch (NumberFormatException e1) { JOptionPane.showMessageDialog(f, "血量只能是小数 "); tfHp.grabFocus(); return; } h.name = name; h.hp = Float.parseFloat(hp); dao.add(h); htm.heros = dao.list(); t.updateUI(); } }); JScrollPane sp = new JScrollPane(t); f.add(p, BorderLayout.NORTH); f.add(sp, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
示例 10 :

选中指定行

1. table初始化后,应该默认选中第一行
2. 增加数据后,也应该选中新增的这一条
选中指定行
package gui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import jdbc.HeroDAO; import charactor.Hero; public class TestGUI { public static void main(String[] args) { final JFrame f = new JFrame("LoL"); f.setSize(400, 300); f.setLocation(200, 200); f.setLayout(new BorderLayout()); final HeroTableModel htm = new HeroTableModel(); final JTable t = new JTable(htm); // 设置选择模式为 只能选中一行 t.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // 选中第一行 (基本0) t.getSelectionModel().setSelectionInterval(0, 0); JPanel p = new JPanel(); final JLabel lName = new JLabel("名称"); final JTextField tfName = new JTextField(""); final JLabel lHp = new JLabel("血量"); final JTextField tfHp = new JTextField(""); JButton bAdd = new JButton("增加"); tfName.setPreferredSize(new Dimension(80, 30)); tfHp.setPreferredSize(new Dimension(80, 30)); p.add(lName); p.add(tfName); p.add(lHp); p.add(tfHp); p.add(bAdd); bAdd.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { HeroDAO dao = new HeroDAO(); Hero h = new Hero(); String name = tfName.getText(); if (name.length() == 0) { JOptionPane.showMessageDialog(f, "名称不能为空"); tfName.grabFocus(); return; } String hp = tfHp.getText().trim(); try { Float.parseFloat(hp); } catch (NumberFormatException e1) { JOptionPane.showMessageDialog(f, "血量只能是小数 "); tfHp.grabFocus(); return; } h.name = name; h.hp = Float.parseFloat(hp); dao.add(h); htm.heros = dao.list(); t.updateUI(); // 选中 第一行 ,因为 DAO是按照 ID倒排序查询,所以第一行就是新加入的数据 t.getSelectionModel().setSelectionInterval(0, 0); } }); JScrollPane sp = new JScrollPane(t); f.add(p, BorderLayout.NORTH); f.add(sp, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }


HOW2J公众号,关注后实时获知布最新的教程和优惠活动,谢谢。


问答区域    
2018-09-07 试图做一个像Excel一样的可编辑表格,但是遇到了困难
暗金教的拉兹



第一段只给一个单元格添加监听器的代码没问题 第二段会报错,因为Table.getCellEditor()会返回null 但是又不能用Table.getCellEditor(int rowIndex,int columnIndex),匿名类里要求i是用final修饰的
【只给一个单元格添加监听器,没问题】
t.getCellEditor(0, 0).addCellEditorListener(new CellEditorListener(){

			@Override
			public void editingStopped(ChangeEvent e) {
				// TODO Auto-generated method stub
				Object v=t.getCellEditor(0, 0).getCellEditorValue();
				System.out.println(v);
				
				stm.setValueAt(Float.parseFloat(v.toString()), 0, 0);
				t.updateUI();
			}

			@Override
			public void editingCanceled(ChangeEvent e) {
				// TODO Auto-generated method stub
				
			}
        	
        });

【想着用for循环给所有单元格都这样做一次】
        for(int i=0;i<t.getRowCount();i++){
        	for(int j=0;j<t.getColumnCount();j++){
        		t.getCellEditor(i,j).addCellEditorListener(new CellEditorListener(){

					@Override
					public void editingStopped(ChangeEvent e) {
						// TODO Auto-generated method stub
						Object v=t.getCellEditor().getCellEditorValue();
						try{
							Float.parseFloat(v.toString());
						}catch(NumberFormatException e1){
							JOptionPane.showMessageDialog(f, "血量只能是小数 ");
							e1.printStackTrace();
						}
						stm.setValueAt(Float.parseFloat(v.toString()),t.getEditingRow(),t.getEditingColumn() );
					}

					@Override
					public void editingCanceled(ChangeEvent e) {
						// TODO Auto-generated method stub
						
					}
        			
        		});
        	}
        }

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
	at gui.TestTableModel$1.editingStopped(TestTableModel.java:38)
	at javax.swing.AbstractCellEditor.fireEditingStopped(AbstractCellEditor.java:141)
	at javax.swing.DefaultCellEditor$EditorDelegate.stopCellEditing(DefaultCellEditor.java:368)
	at javax.swing.DefaultCellEditor.stopCellEditing(DefaultCellEditor.java:233)
	at javax.swing.JTable$GenericEditor.stopCellEditing(JTable.java:5467)
	at javax.swing.plaf.basic.BasicTableUI$Actions.actionPerformed(BasicTableUI.java:503)
	at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1663)
	at javax.swing.JComponent.processKeyBinding(JComponent.java:2882)
	at javax.swing.JTable.processKeyBinding(JTable.java:5259)
	at javax.swing.JComponent.processKeyBindings(JComponent.java:2943)
	at javax.swing.JComponent.processKeyEvent(JComponent.java:2845)
	at java.awt.Component.processEvent(Component.java:6312)
	at java.awt.Container.processEvent(Container.java:2236)
	at java.awt.Component.dispatchEventImpl(Component.java:4891)
	at java.awt.Container.dispatchEventImpl(Container.java:2294)
	at java.awt.Component.dispatchEvent(Component.java:4713)
	at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1954)
	at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:806)
	at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1074)
	at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:945)
	at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:771)
	at java.awt.Component.dispatchEventImpl(Component.java:4762)
	at java.awt.Container.dispatchEventImpl(Container.java:2294)
	at java.awt.Window.dispatchEventImpl(Window.java:2750)
	at java.awt.Component.dispatchEvent(Component.java:4713)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
	at java.awt.EventQueue.access$500(EventQueue.java:97)
	at java.awt.EventQueue$3.run(EventQueue.java:709)
	at java.awt.EventQueue$3.run(EventQueue.java:703)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.awt.EventQueue$4.run(EventQueue.java:731)
	at java.awt.EventQueue$4.run(EventQueue.java:729)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)






答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2018-08-17 步骤八
java渣渣
t.getSelectionModel().setSelectionInterval(0, 0); 步骤八的setselectionInterval里面的两个参数是什么意思?








答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到




2018-07-30 例子6报错了
2018-01-24 Pane和Panel是不同的,前者为嵌板,后者为镶板。
2017-11-18 代码问题
2017-07-30 继承AbstractTableModel 可以不实现getColumnName 和 isCellEditable吧?
2017-06-19 实例8中有错误
2017-05-25 增加监听器的差异
2017-05-05 新加数据后,怎么让页面自动滑到最下面
2017-04-20 额,我不是倒序查看的也




提问之前请登陆
关于 JAVA 中级-图形界面-表格 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 902680467
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: http://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图