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-08-17 步骤八
java渣渣
t.getSelectionModel().setSelectionInterval(0, 0); 步骤八的setselectionInterval里面的两个参数是什么意思?








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




2018-07-30 例子6报错了
zhumimimimi



例子6报错了

							
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:292)
	at jdbc.HeroDAO.<init>(HeroDAO.java:19)
	at gui.HeroTableModel.<init>(HeroTableModel.java:16)
	at gui.TestGUI.main(TestGUI.java:18)
java.sql.SQLException: No suitable driver found for jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8
	at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:703)
	at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:229)
	at jdbc.HeroDAO.getConnection(HeroDAO.java:26)
	at jdbc.HeroDAO.list(HeroDAO.java:141)
	at jdbc.HeroDAO.list(HeroDAO.java:133)
	at gui.HeroTableModel.<init>(HeroTableModel.java:16)
	at gui.TestGUI.main(TestGUI.java:18)






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





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

上传截图