swing中的表格

1.基本表格
显示一个Table需要两组数据

  1. 一维数组: String[]columnNames 表示表格的标题
  2. 二维数组: String[][] heros 表格中的内容
    默认情况下,表格的标题是不会显示出来了,除非使用了JScrollPane

代码如下:

package gui10;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JTable;

public class test1 {

	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

代码如下:

package gui10;
import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
public class test2 {

	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);
        f.add(sp);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
	}
}

结果:
在这里插入图片描述
和上面的相比,这里的表格多了标题。(这就是使用JScrollPane的区别)

3.列宽
如何来设置表格列的宽度呢?
代码如下:

package gui10;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;

public class test3 {

	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);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
	}
}

结果:
在这里插入图片描述
和上面的表格相比,这张表格的宽度发生了改变。其实就是多加了:t.getColumnModel().getColumn(0).setPreferredWidth(10);

4.TableModel
这里要介绍一个新概念叫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 gui10;

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() {
    	System.out.println("aaac");
        return heros.length;
    } 
    // 返回一共有多少列
    public int getColumnCount() {
    	System.out.println("aaab");
        return columnNames.length;
    }
    // 获取每一列的名称
    public String getColumnName(int columnIndex) {
        return columnNames[columnIndex];
    }
    // 单元格是否可以修改
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return false;
    }
    // 每一个单元格里的值
    public Object getValueAt(int rowIndex, int columnIndex) {
        return heros[rowIndex][columnIndex];
    }
}

package gui10;

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 tm = new HeroTableModel();
        //根据 TableModel来创建 Table
        JTable t = new JTable(tm);
        JScrollPane sp = new JScrollPane(t);
        f.add(sp,BorderLayout.CENTER);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
    }

}

结果:
在这里插入图片描述
控制台输出:

aaab
aaab
aaab
aaab
aaab
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac
aaac

这里的显示出的结果和1是一样的,但是控制台输出了许多行字符串,这表示了JTable在建立的过程中调用了HeroTableModel类中的getRowCount()和getColumnCount()函数。

5.理解TableModel
在使用TableModel之前,是使用

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

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

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

为了证明上述说的,我这里用到了调试的方法:
在这一行标记断点:
在这里插入图片描述
然后进入调试:
在这里插入图片描述
可以看出JTable内部实现就是AbstractTableModel()。

6.TableModel 与DAO结合
之前在我的博客jdbc篇章中写过DAO,是为了实现对象和数据库的交互,现在想通过TableModel与DAO结合显示数据库中Hero信息。

DAO使用HeroDAO
在TableModel中,使用从DAO返回的List作为TableModel的数据

只需要修改HeroTableModel,无需修改TestGUI。 这正好演绎了Model设计思想中的数据分离的好处,当只需要数据发生变化的时候,修改Model即可,界面GUI部分,不需要做任何改动.

另外注意一点在执行以下操作时先要导入Jar包。

代码如下:

package gui11;

public class Hero {
    //增加id属性
    public int id;
    public String name;
    public float hp;
    public int damage;
 
}
package gui11;

import java.util.List;

public interface DAO{
    //增加
    public void add(Hero hero);
    //修改
    public void update(Hero hero);
    //删除
    public void delete(int id);
    //获取
    public Hero get(int id);
    //查询
    public List<Hero> list();
    //分页查询
    public List<Hero> list(int start, int count);
}
package gui11;

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;

public class TestGUI3 {

	public static void main(String[] args) {
		JFrame f = new JFrame("LoL");
        f.setSize(400, 300);
        f.setLocation(200, 200);
        f.setLayout(new BorderLayout());
 
        //创建一个TableModel
        HeroTableModel2 htm = new HeroTableModel2();
        JTable t = new JTable(htm);
        // 准备一个Panel上面放一个Label用于显示哪条被选中了
        JPanel p = new JPanel();
        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);
	}
}

package gui11;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class HeroDAO implements DAO{
	//驱动初始化
	public HeroDAO() {
		try {
			Class.forName("com.mysql.jdbc.Driver");
		}catch(ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	//获取连接
	public Connection getConnection() throws SQLException {
		return DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8","root","admin");
	}
	//计算总数
	public int getTotal() {
        int total = 0;
        try (Connection c = getConnection(); Statement s = c.createStatement();) {
            String sql = "select count(*) from item";
            ResultSet rs = s.executeQuery(sql);
            while (rs.next()) {
                total = rs.getInt(1);
            }
            System.out.println("total:" + total);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return total;
    } 
	//添加
	public void add(Hero hero) {
		String sql = "insert into hero values(null,?,?,?)";
		try(Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);){
			ps.setString(1, hero.name);
            ps.setFloat(2, hero.hp);
            ps.setInt(3, hero.damage);
            ps.execute();
            ResultSet rs = ps.getGeneratedKeys();
            if(rs.next()) {
            	int id = rs.getInt(1);
            	hero.id = id;
            }
		}catch(SQLException e) {
			e.printStackTrace();
		}
	}
	//更改信息
	public void update(Hero hero) {
		String sql = "update hero set name = ?,hp = ?,damage = ? where id = ?";
		try(Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);){
			ps.setString(1, hero.name);
            ps.setFloat(2, hero.hp);
            ps.setInt(3, hero.damage);
            ps.setInt(4, hero.id);
  
            ps.execute();
		}catch(SQLException e) {
			e.printStackTrace();
		}
	}
	//删除信息
	public void delete(int id) {
		try(Connection c = getConnection();Statement s = c.createStatement();){
			String sql = "delete from hero where id = " + id;
			s.execute(sql);
		}catch(SQLException e) {
			e.printStackTrace();
		}
	}
	//获取信息
	public Hero get(int id) {
		Hero hero = null;
		try(Connection c = getConnection();Statement s = c.createStatement();){
			String sql = "select * from hero where id = " + id;
			ResultSet rs = s.executeQuery(sql);
			if (rs.next()) {
                hero = new Hero();
                String name = rs.getString(2);
                float hp = rs.getFloat("hp");
                int damage = rs.getInt(4);
                hero.name = name;
                hero.hp = hp;
                hero.damage = damage;
                hero.id = id;
            }
		}catch(SQLException e) {
			e.printStackTrace();
		}
		return hero;
	}

	public List<Hero> list() {
		return list(0, Short.MAX_VALUE);
	}
	//保存中间的几条信息
	public List<Hero> list(int start, int count) {
		List<Hero> heros = new ArrayList<Hero>();
		String sql = "select * from hero order by id desc limit ?,? ";
        try (Connection c = getConnection(); PreparedStatement ps = c.prepareStatement(sql);) {
            ps.setInt(1, start);
            ps.setInt(2, count);
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                Hero hero = new Hero();
                int id = rs.getInt(1);
                String name = rs.getString(2);
                float hp = rs.getFloat("hp");
                int damage = rs.getInt(4);
                hero.id = id;
                hero.name = name;
                hero.hp = hp;
                hero.damage = damage;
                heros.add(hero);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
		return heros;
	}
}

package gui11;
 
import java.util.List;
 
import javax.swing.table.AbstractTableModel;
 
import gui11.HeroDAO;
import gui11.Hero;
 
public class HeroTableModel2 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() {
        return heros.size();
    }
 
    public int getColumnCount() {
        return columnNames.length;
    }
 
    public String getColumnName(int columnIndex) {
        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;
    }
 
}

这里我把ADO实现的代码也全放上来了,所以代码会显得很长…

结果:
在这里插入图片描述
7.TableSelectionModel
通过table可以获取一个 TableSelectionModel,专门用于监听jtable选中项的变化

代码如下:

package gui11;

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;

public class TestGUI3 {

	public static void main(String[] args) {
		JFrame f = new JFrame("LoL");
        f.setSize(400, 300);
        f.setLocation(200, 200);
        f.setLayout(new BorderLayout());
 
        //创建一个TableModel
        HeroTableModel2 htm = new HeroTableModel2();
        JTable t = new JTable(htm);
        // 准备一个Panel上面放一个Label用于显示哪条被选中了
        JPanel p = new JPanel();
        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
这里来实现如何在表格上直接输入数据,然后添加到数据库中。
代码如下:

package gui11;

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;

public class TestGUI4 {

	public static void main(String[] args) {
		JFrame f = new JFrame("LoL");
        f.setSize(400, 300);
        f.setLocation(200, 200);
        f.setLayout(new BorderLayout());
        HeroTableModel2 htm = new HeroTableModel2();
        JTable t = new JTable(htm);
        // 增加 一个 panel用于放置名称,血量输入框和增加 按钮
        JPanel p = new JPanel();
        JLabel lName = new JLabel("名称");
        JTextField tf = new JTextField("");
        JLabel lHp = new JLabel("血量");
        JTextField tfHp = new JTextField("");
        JButton b = new JButton("增加");
        tf.setPreferredSize(new Dimension(80, 30));
        tfHp.setPreferredSize(new Dimension(80, 30));
        
        p.add(lName);
        p.add(tf);
        p.add(lHp);
        p.add(tfHp);
        p.add(b);
        // 为增加按钮添加监听
        b.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				HeroDAO hdo = new HeroDAO();
				// 根据输入框数据创建一个Hero对象
				Hero h = new Hero();
				h.name = tf.getText();
				h.hp = Integer.parseInt(tfHp.getText());
				// 通过hdo把该对象加入到数据库
				hdo.add(h);
				//通过hdo更新tablemodel中的数据
				htm.heros = hdo.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 gui11;

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;

public class TestGUI5 {

	public static void main(String[] args) {
		JFrame f = new JFrame("LoL");
        f.setSize(400, 300);
        f.setLocation(200, 200);
        f.setLayout(new BorderLayout());
        HeroTableModel2 htm = new HeroTableModel2();
        JTable t = new JTable(htm);
        // 增加 一个 panel用于放置名称,血量输入框和增加 按钮
        JPanel p = new JPanel();
        JLabel lName = new JLabel("名称");
        JTextField tf = new JTextField("");
        JLabel lHp = new JLabel("血量");
        JTextField tfHp = new JTextField("");
        JButton b = new JButton("增加");
        tf.setPreferredSize(new Dimension(80, 30));
        tfHp.setPreferredSize(new Dimension(80, 30));
        
        p.add(lName);
        p.add(tf);
        p.add(lHp);
        p.add(tfHp);
        p.add(b);
        // 为增加按钮添加监听
        b.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				HeroDAO hdo = new HeroDAO();
				// 根据输入框数据创建一个Hero对象
				Hero h = new Hero();
				String name = tf.getText();
				if(name.length() == 0) {
					// 弹出对话框提示用户
                    JOptionPane.showMessageDialog(f, "名称不能为空");
                    tf.grabFocus();	
				}
				h.name = name;
				String hp = tfHp.getText().trim();
				try {
					Float.parseFloat(hp);//将hp转换为字符串类型,如果hp不是浮点数就会抛出异常
				}catch(NumberFormatException e1) {
					e1.printStackTrace();
				}
				h.hp = Float.parseFloat(hp);
				hdo.add(h);
				htm.heros = hdo.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 gui11;

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;

public class TestGUI6 {

	public static void main(String[] args) {
		JFrame f = new JFrame("LoL");
        f.setSize(400, 300);
        f.setLocation(200, 200);
        f.setLayout(new BorderLayout());
        HeroTableModel2 htm = new HeroTableModel2();
        JTable t = new JTable(htm);
        // 增加 一个 panel用于放置名称,血量输入框和增加 按钮
        JPanel p = new JPanel();
        JLabel lName = new JLabel("名称");
        JTextField tf = new JTextField("");
        JLabel lHp = new JLabel("血量");
        JTextField tfHp = new JTextField("");
        JButton b = new JButton("增加");
        tf.setPreferredSize(new Dimension(80, 30));
        tfHp.setPreferredSize(new Dimension(80, 30));
        
        p.add(lName);
        p.add(tf);
        p.add(lHp);
        p.add(tfHp);
        p.add(b);
        // 为增加按钮添加监听
        b.addActionListener(new ActionListener() {
			
			public void actionPerformed(ActionEvent e) {
				HeroDAO hdo = new HeroDAO();
				// 根据输入框数据创建一个Hero对象
				Hero h = new Hero();
				String name = tf.getText();
				if(name.length() == 0) {
					// 弹出对话框提示用户
                    JOptionPane.showMessageDialog(f, "名称不能为空");
                    tf.grabFocus();	
				}
				h.name = name;
				String hp = tfHp.getText().trim();
				try {
					Float.parseFloat(hp);//将hp转换为字符串类型,如果hp不是浮点数就会抛出异常
				}catch(NumberFormatException e1) {
					e1.printStackTrace();
				}
				h.hp = Float.parseFloat(hp);
				hdo.add(h);
				htm.heros = hdo.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);
	}
}

结果:
在这里插入图片描述

发布了263 篇原创文章 · 获赞 23 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41998938/article/details/89930066