【Java基础】swing-图形界面学习(下)
就是个人学习的笔记,按照下面的Demo一个一个复制粘贴跑起来大概就会使用Swing了,建议先从上半部分开始看
上半部分
【Java基础】swing-图形界面学习(上)
八.菜单
GUI的菜单分为 菜单栏JMenuBar
,菜单JMenu
和菜单项JMenuItem
8.1.菜单栏和菜单
@Test
public void MenuAndJMenuBar() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
// 菜单栏
JMenuBar mb = new JMenuBar();
// 菜单
JMenu mHero = new JMenu("英雄");
JMenu mItem = new JMenu("道具");
JMenu mWord = new JMenu("符文");
JMenu mSummon = new JMenu("召唤师");
JMenu mTalent = new JMenu("天赋树");
// 把菜单加入到菜单栏
mb.add(mHero);
mb.add(mItem);
mb.add(mWord);
mb.add(mSummon);
mb.add(mTalent);
// 把菜单栏加入到frame,这里用的是set而非add
frame.setJMenuBar(mb);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
8.2.菜单项
@Test
public void MenuItem() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 400);
frame.setLocation(200, 200);
JMenuBar mb = new JMenuBar();
JMenu mHero = new JMenu("英雄");
JMenu mItem = new JMenu("道具");
JMenu mWord = new JMenu("符文");
JMenu mSummon = new JMenu("召唤师");
JMenu mTalent = new JMenu("天赋树");
// 菜单项
mHero.add(new JMenuItem("近战-Warriar"));
mHero.add(new JMenuItem("远程-Range"));
mHero.add(new JMenuItem("物理-physical"));
mHero.add(new JMenuItem("坦克-Tank"));
mHero.add(new JMenuItem("法系-Mage"));
mHero.add(new JMenuItem("辅助-Support"));
mHero.add(new JMenuItem("打野-Jungle"));
mHero.add(new JMenuItem("突进-Charge"));
mHero.add(new JMenuItem("男性-Boy"));
mHero.add(new JMenuItem("女性-Girl"));
// 分隔符
mHero.addSeparator();
mHero.add(new JMenuItem("所有-All"));
mb.add(mHero);
mb.add(mItem);
mb.add(mWord);
mb.add(mSummon);
mb.add(mTalent);
frame.setJMenuBar(mb);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
8.3.练习-完成一个完整的记事本界面
@Test
public void NotePad() throws InterruptedException {
//创建容器
JFrame frame = new JFrame("记事本");
//
frame.setSize(400, 400);
frame.setLocation(200, 200);
//创建菜单栏
JMenuBar menuBar = new JMenuBar();
//创建菜单
JMenu document = new JMenu("文件(A)");
JMenu editor = new JMenu("编辑(E)");
JMenu layout = new JMenu("格式(O)");
JMenu viewing = new JMenu("查看(V)");
JMenu help = new JMenu("帮助(H)");
// 文件菜单添加菜单项
document.add(new JMenuItem("新建(N) Ctrl+N"));
document.add(new JMenuItem("打开(O) Ctrl+O"));
document.add(new JMenuItem("保存(S) Ctrl+S"));
document.add(new JMenuItem("另存为(N)"));
document.addSeparator();//分割线
document.add(new JMenuItem("页面设置(U)"));
document.add(new JMenuItem("打印(P) Ctrl+P"));
document.addSeparator();//分割线
document.add(new JMenuItem("退出(X)"));
// 编辑项菜单添加菜单项
editor.add(new JMenuItem("撤销(U) Ctrl+Z"));
editor.addSeparator();//分割线
editor.add(new JMenuItem("剪切(T) Ctrl+X"));
editor.add(new JMenuItem("复制(C) Ctrl+C"));
editor.add(new JMenuItem("粘贴(P) Ctrl+V"));
editor.add(new JMenuItem("删除(L) Del"));
editor.addSeparator();//分割线
editor.add(new JMenuItem("查找(frame) Ctrl+frame"));
editor.add(new JMenuItem("查找下一个(N) F3"));
editor.add(new JMenuItem("替换(R) Ctrl+H"));
editor.add(new JMenuItem("转到(G) Ctrl+G"));
editor.addSeparator();//分割线
editor.add(new JMenuItem("全选(A) Ctrl+A"));
editor.add(new JMenuItem("时间/日期(D) F5"));
//格式菜单添加菜单项
layout.add(new JMenuItem("自动换行(W) "));
layout.add(new JMenuItem("字体(frame)"));
//查看菜单添加菜单项
viewing.add(new JMenuItem("状态栏(S)"));
//帮助菜单添加菜单项
help.add(new JMenuItem("查看帮助(H)"));
help.add(new JMenuItem("关于记事本(A)"));
//菜单加入菜单栏
menuBar.add(document);
menuBar.add(editor);
menuBar.add(layout);
menuBar.add(viewing);
menuBar.add(help);
//多行文本框
JTextArea textArea = new JTextArea();
textArea.setLineWrap(true);
//加入滚动条面版
JScrollPane scrollPane = new JScrollPane(textArea);
//滚动面板加入容器
frame.setContentPane(scrollPane);
//容器加入菜单栏
frame.setJMenuBar(menuBar);
//设置默认关闭操作
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置容器可显示
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
九.工具栏
工具栏用于存放常用的按钮
btn.setToolTipText() 可以设置按钮提示信息
toolBar.setFloatable(false);禁止工具栏拖动:true可以拖动 false不可以拖动
@Test
public void JToolBarTest() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
// 添加菜单
addMenu(frame);
// 工具栏
JToolBar toolBar = new JToolBar();
// 为工具栏增加按钮
JButton b1 = new JButton(new ImageIcon("e:/data/1.jpg"));
//设置按钮提示文字
b1.setToolTipText("坑爹英雄");
JButton b2 = new JButton(new ImageIcon("e:/data/2.jpg"));
JButton b3 = new JButton(new ImageIcon("e:/data/3.jpg"));
JButton b4 = new JButton(new ImageIcon("e:/data/4.jpg"));
JButton b5 = new JButton(new ImageIcon("e:/data/5.jpg"));
JButton b6 = new JButton(new ImageIcon("e:/data/6.jpg"));
toolBar.add(b1);
toolBar.add(b2);
toolBar.add(b3);
toolBar.add(b4);
toolBar.add(b5);
toolBar.add(b6);
// 禁止工具栏拖动
toolBar.setFloatable(false);
// 把工具栏放在north的位置
frame.setLayout(new BorderLayout());
frame.add(toolBar, BorderLayout.NORTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
十.日期控件
10.1.DatePicker
<dependency>
<groupId>org.jdatepicker</groupId>
<artifactId>jdatepicker</artifactId>
<version>1.3.4</version>
</dependency>
本例使用datepicker.jar 包
,有一个缺点,不能设置时间
,只能在创建控件的时候传入指定日期。
需要设置日期,请使用JXDatePicker
import com.eltima.components.ui.DatePicker;
public class TestGUI {
public static void main(String[] args) {
JFrame f = new JFrame("LoL");
f.setSize(400, 300);
f.setLocation(200, 200);
f.setLayout(null);
final DatePicker datepick;
datepick = getDatePicker();
f.add(datepick);
JButton b = new JButton("获取时间");
b.setBounds(137, 183, 100, 30);
f.add(b);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(f, "获取控件中的日期:" + datepick.getValue());
System.out.println(datepick.getValue());
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
private static DatePicker getDatePicker() {
final DatePicker datepick;
// 格式
String DefaultFormat = "yyyy-MM-dd HH:mm:ss";
// 当前时间
Date date = new Date();
// 字体
Font font = new Font("Times New Roman", Font.BOLD, 14);
Dimension dimension = new Dimension(177, 24);
int[] hilightDays = { 1, 3, 5, 7 };
int[] disabledDays = { 4, 6, 5, 9 };
datepick = new DatePicker(date, DefaultFormat, font, dimension);
datepick.setLocation(137, 83);
datepick.setBounds(137, 83, 177, 24);
// 设置一个月份中需要高亮显示的日子
datepick.setHightlightdays(hilightDays, Color.red);
// 设置一个月份中不需要的日子,呈灰色显示
datepick.setDisableddays(disabledDays);
// 设置国家
datepick.setLocale(Locale.CHINA);
// 设置时钟面板可见
datepick.setTimePanleVisible(true);
return datepick;
}
}
10.2.JXDatePicker
本例使用 包swingx-core-1.6.5-1.jar,界面比较简约,可以设置日期
<dependency>
<groupId>org.swinglabs.swingx</groupId>
<artifactId>swingx-core</artifactId>
<version>1.6.5-1</version>
</dependency>
10.3.时间控件练习:统计大于指定更新时间文件
借助时间控件,选中一个时间,然后根据这个时间,统计e:\project 修改时间大于这个时间的文件,一共有多少
import com.eltima.components.ui.DatePicker;
public class TestGUI {
public static void main(String[] args) {
JFrame f = new JFrame("LoL");
f.setSize(500, 300);
f.setLocation(200, 200);
f.setLayout(null);
final DatePicker datepick;
datepick = getDatePicker();
f.add(datepick);
JButton b = new JButton("统计在D:/eclipse3.1 目录下,修改时间大于控件日期的文件总数");
b.setBounds(20, 183, 450, 30);
f.add(b);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
File folder = new File("D:\\eclipse3.1");
Date d = (Date) datepick.getValue();
JOptionPane.showMessageDialog(f, "文件总数是:" + countFilesModifiedDateGreaterThan(folder, d));
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
private static DatePicker getDatePicker() {
final DatePicker datepick;
// 格式
String DefaultFormat = "yyyy-MM-dd HH:mm:ss";
// 当前时间
Date date = new Date();
// 字体
Font font = new Font("Times New Roman", Font.BOLD, 14);
Dimension dimension = new Dimension(177, 24);
int[] hilightDays = { 1, 3, 5, 7 };
int[] disabledDays = { 4, 6, 5, 9 };
datepick = new DatePicker(date, DefaultFormat, font, dimension);
// datepick.setLocation(137, 83);
datepick.setBounds(137, 83, 177, 24);
// 设置一个月份中需要高亮显示的日子
datepick.setHightlightdays(hilightDays, Color.red);
// 设置一个月份中不需要的日子,呈灰色显示
datepick.setDisableddays(disabledDays);
// 设置国家
datepick.setLocale(Locale.CHINA);
// 设置时钟面板可见
datepick.setTimePanleVisible(true);
return datepick;
}
//递归获取文件
public static List<File> scanFolder(File folder){
List<File> files = new ArrayList<>();
File[] fs = folder.listFiles();
for (File f : fs) {
if(f.isDirectory()){
List<File> subFolderFiles = scanFolder(f);
files.addAll(subFolderFiles);
}
if(f.isFile()){
files.add(f);
}
}
return files;
}
//通过stream过滤文件修改时间大于指定时间的个数
public static long countFilesModifiedDateGreaterThan(File folder,Date d){
List<File> fs = scanFolder(folder);
return fs.stream()
.filter(f->f.lastModified()>d.getTime())
.count();
}
}
十一.表格
11.1.基本表格
显示一个Table需要两组数据
- 一维数组: String[] columnName: 表示表格的标题
- 二维数组: String[][] heros : 表格中的内容
- 默认情况下,表格的标题不会显示,除非使用了
JScrollPane
@Test
public void JTable() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
frame.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 table = new JTable(heros, columnNames);
frame.add(table, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
11.2.使用JScrollPane
JScrollPane: 带滚动条的Panel
- 把table放进去就可以看到table的title
同样的把textarea放进去,并且textarea内容够长的话,就会看到滚动条
@Test
public void JTableShowTitle() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
frame.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 table = new JTable(heros, columnNames);
// 根据t创建 JScrollPane
JScrollPane scrollPane = new JScrollPane(table);
//或则创建一个空的JScrollPane,再通过setViewportView把table放在JScrollPane中
// JScrollPane sp = new JScrollPane(table);
// sp.setViewportView(table);
// 把sp而非JTable加入到JFrame上,
frame.add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
TimeUnit.SECONDS.sleep(1000);
}
11.3.设置列宽
// 设置列宽度
table.getColumnModel().getColumn(0).setPreferredWidth(10);
11.4.TableModel
TableMode可以将表格的数据和显示分离
- 比如对于
JTable
而言,有数据部分,也有显示部分(比如列宽等信息)。 数据部分可以声明一个叫做TableModel存放要显示的数据。
使用TableModel的方式存放Table需要显示的数据
需要继承AbstractTableModel
,进而实现了接口TableModel
,在实现类
中提供一个table显示需要的所有信息
getRowCount
返回一共有多少行getColumnCount
返回一共有多少列getColumnName
每一列的名字isCellEditable
单元格是否可以修改
5.getValueAt
每一个单元格里的值
当图形界面需要渲染第一个单元格的数据的时候,就会调用方法TabelModel
的getValueAt(0,0)
,把返回值拿到并显示
public class HeroTableModel extends AbstractTableModel {
private static final long serialVersionUID = -4796525201897896613L;
String[] columnNames = new String[]{ "id", "name", "hp", "damage"};
String[][] heros = new String[][]{ { "1", "盖伦", "616", "100"}, { "2", "提莫", "512", "102"}, { "3", "奎因", "832", "200"}};
// 返回一共有多少行
@Override
public int getRowCount() {
return heros.length;
}
// 返回一共有多少列
@Override
public int getColumnCount() {
return columnNames.length;
}
// 获取每一列的名称
@Override
public String getColumnName(int columnIndex) {
return columnNames[columnIndex];
}
// 单元格是否可以修改
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
// 每一个单元格里的值
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return heros[rowIndex][columnIndex];
}
/** * 自定义TableModel * @throws InterruptedException */
@Test
public void customTableModel() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
frame.setLayout(new BorderLayout());
//创建一个TableModel
HeroTableModel htm = new HeroTableModel();
//根据 TableModel来创建 Table
JTable t = new JTable(htm);
JScrollPane sp = new JScrollPane(t);
frame.add(sp, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
TimeUnit.SECONDS.sleep(1000);
}
}
也可以直接通过JTable的构造方法传入标题以及数据
创建表格
JTable(Object[][] rowData, Object[] columnNames)
11.5.TableModel 与 数据库结合(使用List模拟数据库操作)
只需要修改HeroTableModel,引入Dao层,无需修改展示。 这正好演绎了Model设计思想中的数据分离的好处,当只需要数据发生变化的时候,修改Model即可,界面GUI部分,不需要做任何改动
由于时间关系 下面的代码都是使用内存来模拟数据库的增删改查, 实际上操作数据库也只是把 HeroDAO代码改为操作数据库代码而已
public class HeroTableModelDB extends AbstractTableModel {
private static final long serialVersionUID = -4796525201897896613L;
// 使用从DAO返回的List作为TableModel的数据
public List<Hero> heros = new HeroDAO().list(0,10);
public String[] columnNames = new String[]{ "id", "name", "hp", "damage"};
// heros.size返回一共有多少行
@Override
public int getRowCount() {
return heros.size();
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public String getColumnName(int columnIndex) {
return columnNames[columnIndex];
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
// 先通过heros.get(rowIndex)获取行对应的Hero对象
// 然后根据columnIndex返回对应的属性
@Override
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;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Hero {
private String id;
private String name;
private String hp;
private String damage;
}
//静态内部类HeroDAO,通过操作内存来模拟对数据库的增删改查
static class HeroDAO {
//保存模拟数据容器
public static List<Hero> heroList = new ArrayList<>();
//模拟数据
static {
Hero h1 = new Hero("1", "盖伦", "616", "100");
Hero h2 = new Hero("2", "提莫", "512", "102");
Hero h3 = new Hero("3", "奎因", "832", "200");
Hero h4 = new Hero("4", "后羿", "822", "500");
Hero h5 = new Hero("5", "盖伦", "616", "100");
Hero h6 = new Hero("6", "提莫", "512", "102");
Hero h7 = new Hero("7", "奎因", "832", "200");
Hero h8 = new Hero("8", "后羿", "822", "500");
Hero h9 = new Hero("9", "盖伦", "616", "100");
Hero h10 = new Hero("10", "提莫", "512", "102");
Hero h11 = new Hero("11", "奎因", "832", "200");
Hero h12 = new Hero("12", "后羿", "822", "500");
heroList.add(h1);
heroList.add(h2);
heroList.add(h3);
heroList.add(h4);
heroList.add(h5);
heroList.add(h6);
heroList.add(h7);
heroList.add(h8);
heroList.add(h9);
heroList.add(h10);
heroList.add(h11);
heroList.add(h12);
}
/** * 模拟查询数据库方法 * * @return */
public List<Hero> list() {
return heroList;
}
/** * 模拟查询数据库方法 * @return */
public List<Hero> list(int start, int number) {
return page(heroList,start,number);
}
/** * 模拟新增 * @return */
public void add(Hero hero) {
heroList.add(0, hero);
}
/** * 根据id删除 * @param id */
public void delete(String id) {
heroList.removeIf(hero -> StringUtils.equals(id, hero.getId()));
}
/** * 获取数据总量 * @return */
public int getTotal() {
return heroList.size();
}
/** * 根据id更新数据 * @param hero */
public void update(Hero hero) {
heroList.forEach(e -> {
if (StringUtils.equals(e.getId(), hero.getId())) {
e.setName(hero.getName());
e.setHp(hero.getHp());
e.setDamage(hero.getDamage());
}
});
}
/** * 循环截取某页列表进行分页 * @param dataList 分页数据 * @param pageSize 页面大小 * @param currentPage 当前页面 */
public static List<Hero> page(List<Hero> dataList,int currentPage, int pageSize ) {
List<Hero> currentPageList = new ArrayList<>();
if (dataList != null && dataList.size() > 0) {
int currIdx = (currentPage > 1 ? (currentPage - 1) * pageSize : 0);
for (int i = 0; i < pageSize && i < dataList.size() - currIdx; i++) {
Hero data = dataList.get(currIdx + i);
currentPageList.add(data);
}
}
return currentPageList;
}
}
}
11.5.1.测试展示数据库内容
@Test
public void customTableModelDB() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
frame.setLayout(new BorderLayout());
//创建一个TableModel
HeroTableModelDB htm = new HeroTableModelDB();
//根据 TableModel来创建 Table
JTable table = new JTable(htm);
JScrollPane sp = new JScrollPane(table);
frame.add(sp, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
11.5.2.监听table选中项的变化
通过table可以获取一个 TableSelectionModel
,专门用于监听jtable选中项的变化
@Test
public void getSelectionModel() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
frame.setLayout(new BorderLayout());
final HeroTableModelDB htm = new HeroTableModelDB();
final JTable table = new JTable(htm);
// 准备一个Panel上面放一个Label用于显示哪条被选中了
JPanel panel = new JPanel();
final JLabel label = new JLabel("暂时未选中条目");
panel.add(label);
JScrollPane sp = new JScrollPane(table);
// 使用selection监听器来监听table的哪个条目被选中
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
// 当选择了某一行的时候触发该事件
@Override
public void valueChanged(ListSelectionEvent e) {
// 获取哪一行被选中了
int row = table.getSelectedRow();
// 根据选中的行,到HeroTableModel中获取对应的对象
HeroTableModelDB.Hero h = htm.heros.get(row);
// 更新标签内容
label.setText("当前选中的英雄是: " + h.getName());
}
});
frame.add(panel, BorderLayout.NORTH);
frame.add(sp, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
11.5.3.输入项校验
如果用户输入的名称为空,或者血量不是小数,在提交数据的时候弹出提示。
@Test
public void checkTable() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
frame.setLayout(new BorderLayout());
final HeroTableModelDB htm = new HeroTableModelDB();
final JTable table = new JTable(htm);
// 增加 一个 panel用于放置名称,血量输入框和增加 按钮
JPanel panel = 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));
panel.add(lName);
panel.add(tfName);
panel.add(lHp);
panel.add(tfHp);
panel.add(bAdd);
// 为增加按钮添加监听
bAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
HeroDAO heroDAO = new HeroDAO();
String name = tfName.getText();
// 通过name长度判断 名称是否为空
if (name.length() == 0) {
// 弹出对话框提示用户
JOptionPane.showMessageDialog(frame, "名称不能为空");
// 名称输入框获取焦点
tfName.grabFocus();
return;
}
String hp = tfHp.getText().trim();
try {
// 把hp转换为浮点型,如果出现异常NumberFormatException表示不是浮点型格式
Float.parseFloat(hp);
} catch (NumberFormatException e1) {
JOptionPane.showMessageDialog(frame, "血量只能是小数 ");
tfHp.grabFocus();
return;
}
// 根据输入框数据创建一个Hero对象
Hero hero = new Hero();
hero.setName(name);
hero.setHp(hp);
hero.setId(System.currentTimeMillis() + "");
hero.setDamage("0");
// 通过dao把该对象加入到数据库
heroDAO.add(hero);
// 通过dao更新tablemodel中的数据
htm.heros = heroDAO.list();
// 调用JTable的updateUI,刷新界面。
// 刷新界面的时候,会到tableModel中去取最新的数据
// 就能看到新加进去的数据了
table.updateUI();
}
});
JScrollPane scrollPane = new JScrollPane(table);
frame.add(panel, BorderLayout.NORTH);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
11.5.4.选中指定行
- table初始化后,默认选中第一行
新增数据后,默认选中新增的这一条
@Test
public void selectLine() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(200, 200);
frame.setLayout(new BorderLayout());
final HeroTableModelDB htm = new HeroTableModelDB();
final JTable table = new JTable(htm);
// 设置选择模式为 只能选中一行
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// 选中第一行 (基本0)
table.getSelectionModel().setSelectionInterval(0, 0);
// 增加 一个 panel用于放置名称,血量输入框和增加 按钮
JPanel panel = 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));
panel.add(lName);
panel.add(tfName);
panel.add(lHp);
panel.add(tfHp);
panel.add(bAdd);
// 为增加按钮添加监听
bAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
HeroDAO heroDAO = new HeroDAO();
String name = tfName.getText();
// 通过name长度判断 名称是否为空
if (name.length() == 0) {
// 弹出对话框提示用户
JOptionPane.showMessageDialog(frame, "名称不能为空");
// 名称输入框获取焦点
tfName.grabFocus();
return;
}
String hp = tfHp.getText().trim();
try {
// 把hp转换为浮点型,如果出现异常NumberFormatException表示不是浮点型格式
Float.parseFloat(hp);
} catch (NumberFormatException e1) {
JOptionPane.showMessageDialog(frame, "血量只能是小数 ");
tfHp.grabFocus();
return;
}
// 根据输入框数据创建一个Hero对象
Hero hero = new Hero();
hero.setName(name);
hero.setHp(hp);
hero.setId(System.currentTimeMillis() + "");
hero.setDamage("0");
// 通过dao把该对象加入到数据库
heroDAO.add(hero);
// 通过dao更新tablemodel中的数据
htm.heros = heroDAO.list();
// 调用JTable的updateUI,刷新界面。
// 刷新界面的时候,会到tableModel中去取最新的数据
// 就能看到新加进去的数据了
table.updateUI();
// 选中 第一行 ,因为 DAO是按照 ID倒排序查询,所以第一行就是新加入的数据
table.getSelectionModel().setSelectionInterval(0, 0);
}
});
JScrollPane scrollPane = new JScrollPane(table);
frame.add(panel, BorderLayout.NORTH);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
TimeUnit.SECONDS.sleep(1000);
}
11.6.练习:表格增删改查(使用List模拟数据库操作)
public class TableCrudDemo {
static HeroTableModelDB htm = new HeroTableModelDB();
static JTable table = new JTable(htm);
// 把分页按钮放在这里,后面监听器好访问
static JButton bFirst = new JButton("首页");
static JButton bPre = new JButton("上一页");
static JButton bNext = new JButton("下一页");
static JButton bLast = new JButton("末页");
static JComboBox<Integer> goPageCheckBox = new JComboBox<>();//跳转页数
static int pageSize = 10;// 每页显示10个
static int pageNum = 1;// 开始的页码
private static boolean cbListenerEnabled = true;
public static void main(String[] args) {
//WebLookAndFeel.install ();
final JFrame frame = new JFrame("LoL");
frame.setSize(400, 340);
frame.setLocation(200, 200);
//默认选中第0行
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.getSelectionModel().setSelectionInterval(0, 0);
JPanel pOperation = new JPanel();
JButton bAdd = new JButton("增加");
JButton bDelete = new JButton("删除");
JButton bEdit = new JButton("编辑");
pOperation.add(bAdd);
pOperation.add(bDelete);
pOperation.add(bEdit);
JPanel pPage = new JPanel();
pPage.add(bFirst);
pPage.add(bPre);
pPage.add(goPageCheckBox);
pPage.add(bNext);
pPage.add(bLast);
//新增按钮事件
addBtnListener(frame, bAdd);
//编辑按钮事件
editBtnListener(frame, bEdit);
//删除按钮事件
deleteBtnListener(frame, bDelete);
//分页按钮事件
pageBtnListener();
//跳转按钮事件
goPageBtnListener(goPageCheckBox);
JScrollPane scrollPane = new JScrollPane(table);
frame.setLayout(null);
scrollPane.setBounds(0, 0, 394, 200);
pOperation.setBounds(0, 200, 394, 50);
pPage.setBounds(0, 250, 394, 200);
frame.add(scrollPane);
frame.add(pOperation);
frame.add(pPage);
updateButtonStatus();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
/** * 跳转按钮事件 */
private static void goPageBtnListener(JComboBox<Integer> goPageCheckBox) {
//跳转按钮事件
goPageCheckBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!cbListenerEnabled) {
return;
}
int currentPage = (int) goPageCheckBox.getSelectedItem();
pageNum = currentPage;//当前页数=跳转页数
updateTable();
updateButtonStatus();
}
});
}
/** * 新增按钮事件 */
private static void addBtnListener(JFrame frame, JButton bAdd) {
bAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
new AddDialog(frame).setVisible(true);
updateButtonStatus();
}
});
}
/** * 编辑按钮事件 */
private static void editBtnListener(JFrame frame, JButton bEdit) {
bEdit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 判断是否选中
int index = table.getSelectedRow();
if (-1 == index) {
JOptionPane.showMessageDialog(frame, "编辑前需要先选中一行");
return;
}
// 获取选中的对象
HeroTableModelDB.Hero hero = htm.heros.get(index);
// 显示编辑Dialog
EditDialog ed = new EditDialog(frame);
ed.tfName.setText(hero.getName());
ed.tfHp.setText(hero.getHp());
ed.setVisible(true);
}
});
}
/** * 删除按钮事件 */
private static void deleteBtnListener(JFrame frame, JButton bDelete) {
bDelete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 判断是否选中
int index = table.getSelectedRow();
if (-1 == index) {
JOptionPane.showMessageDialog(frame, "删除前需要先选中一行");
return;
}
// 进行确认是否要删除
if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(frame, "确认要删除?")) {
return;
}
// 获取id
HeroTableModelDB.Hero hero = htm.heros.get(index);
String id = hero.getId();
// 删除
new HeroTableModelDB.HeroDAO().delete(id);
// 更新table
pageNum = 1;
updateTable();
updateButtonStatus();
}
});
}
/** * 分页按钮事件 */
private static void pageBtnListener() {
bFirst.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pageNum = 1;
updateTable();
updateButtonStatus();
}
});
bPre.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pageNum = pageNum - 1;
updateTable();
updateButtonStatus();
}
});
bNext.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pageNum = pageNum + 1;
updateTable();
updateButtonStatus();
}
});
bLast.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pageNum = calcTotalPage();
updateTable();
updateButtonStatus();
}
});
}
/** * 更新按钮状态 */
private static void updateButtonStatus() {
int totalPage = calcTotalPage();
// 是否有上一页
if (1 != pageNum) {
bFirst.setEnabled(true);
bPre.setEnabled(true);
}
// 是否是第一页
if (1 == pageNum) {
bFirst.setEnabled(false);
bPre.setEnabled(false);
}
// 是否是最后一页
if (pageNum == totalPage) {
bLast.setEnabled(false);
bNext.setEnabled(false);
}
// 是否有下一页
if (pageNum < totalPage) {
bLast.setEnabled(true);
bNext.setEnabled(true);
}
cbListenerEnabled = false;
goPageCheckBox.removeAllItems();
//总共的页数
for (int i = 0; i < totalPage; i++) {
goPageCheckBox.addItem(i + 1);
}
cbListenerEnabled = true;
goPageCheckBox.setSelectedItem(pageNum);
}
/** * 根据表格数据 */
public static void updateTable() {
htm.heros = new HeroTableModelDB.HeroDAO().list(pageNum, pageSize);
table.updateUI();
if (!htm.heros.isEmpty()) {
table.getSelectionModel().setSelectionInterval(0, 0);
}
}
/** * 检查输入框空 * * @param tf * @param msg * @return */
private static boolean checkEmpty(JTextField tf, String msg) {
String value = tf.getText();
if (0 == value.length()) {
JOptionPane.showMessageDialog(null, msg + " 不能为空");
tf.grabFocus();
return false;
}
return true;
}
/** * 检查输入框空与非数字 * * @param tf * @param msg * @return */
private static boolean checkNumber(JTextField tf, String msg) {
String value = tf.getText();
if (0 == value.length()) {
JOptionPane.showMessageDialog(null, msg + " 不能为空");
tf.grabFocus();
return false;
}
try {
Integer.parseInt(value);
} catch (NumberFormatException e) {
JOptionPane.showMessageDialog(null, msg + " 只能是整数");
tf.grabFocus();
return false;
}
return true;
}
/** * 计算总页数 * * @return */
private static int calcTotalPage() {
int total = new HeroTableModelDB.HeroDAO().getTotal();
int last = total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1;
return last;
}
/** * 新增对话框 */
static class AddDialog extends JDialog {
JLabel lName = new JLabel("名称");
JLabel lHp = new JLabel("血量");
JTextField tfName = new JTextField();
JTextField tfHp = new JTextField();
JButton bSubmit = new JButton("提交");
AddDialog(JFrame frame) {
super(frame);
this.setModal(true);
int gap = 50;
this.setLayout(null);
JPanel pInput = new JPanel();
JPanel pSubmit = new JPanel();
pInput.setLayout(new GridLayout(2, 2, gap, gap));
pInput.add(lName);
pInput.add(tfName);
pInput.add(lHp);
pInput.add(tfHp);
pSubmit.add(bSubmit);
pInput.setBounds(50, 20, 200, 100);
pSubmit.setBounds(0, 130, 300, 150);
this.add(pInput);
this.add(pSubmit);
this.setSize(300, 200);
this.setLocationRelativeTo(frame);
bSubmit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (checkEmpty(tfName, "名称")) {
if (checkNumber(tfHp, "hp")) {
String name = tfName.getText();
String hp = tfHp.getText();
HeroTableModelDB.Hero h = new HeroTableModelDB.Hero();
h.setName(name);
h.setHp(hp);
h.setId(System.currentTimeMillis() + "");
h.setDamage("0");
new HeroTableModelDB.HeroDAO().add(h);
JOptionPane.showMessageDialog(frame, "提交成功 ");
AddDialog.this.setVisible(false);
pageNum = 1;
updateTable();
}
}
}
});
}
}
/** * 编辑对话框 */
static class EditDialog extends JDialog {
JLabel lName = new JLabel("名称");
JLabel lHp = new JLabel("血量");
JTextField tfName = new JTextField();
JTextField tfHp = new JTextField();
JButton bSubmit = new JButton("提交");
EditDialog(JFrame frame) {
super(frame);
this.setModal(true);
int gap = 50;
this.setLayout(null);
JPanel pInput = new JPanel();
JPanel pSubmit = new JPanel();
pInput.setLayout(new GridLayout(2, 2, gap, gap));
pInput.add(lName);
pInput.add(tfName);
pInput.add(lHp);
pInput.add(tfHp);
pSubmit.add(bSubmit);
pInput.setBounds(50, 20, 200, 100);
pSubmit.setBounds(0, 130, 300, 150);
this.add(pInput);
this.add(pSubmit);
this.setSize(300, 200);
this.setLocationRelativeTo(frame);
bSubmit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (checkEmpty(tfName, "名称")) {
if (checkNumber(tfHp, "hp")) {
// 获取id
int index = table.getSelectedRow();
String id = htm.heros.get(index).getId();
String name = tfName.getText();
String hp = tfHp.getText();
HeroTableModelDB.Hero h = new HeroTableModelDB.Hero();
h.setName(name);
h.setHp(hp);
h.setId(id);
h.setDamage("0");
new HeroTableModelDB.HeroDAO().update(h);
JOptionPane.showMessageDialog(frame, "提交成功 ");
EditDialog.this.setVisible(false);
updateTable();
}
}
}
});
}
}
}
十二.Swing中的线程
12.1.三种线程
在Swing程序的开发中,有3种线程的概念
- 初始化线程
用于创建各种容器,组件并显示他们
,一旦创建并显示,初始化线程的任务就结束了。 - 事件调度线程
Swing是一个事件驱动的模型
,所有和事件相关的操作都放是放在事件调度线程 (Event Dispatch)
中进行的。比如按钮的点击时间,ActionListener.actionPerformed
中的代码,就是在事件调度线程 Event Dispatch Thread
中执行的。 - 长耗时任务线程
有时候需要进行一些长时间的操作,比如访问数据库,文件复制,连接网络,统计文件总数
等等。 这些操作就不适合放在事件调度线程中进行,因为占用时间久了,会让使用者感觉界面响应很卡顿。 为了保持界面响应的流畅性,所有长耗时任务都应该放在专门的长耗时任务线程
中进行
12.2.事件调度线程是单线程的
为什么 事件调度线程是单线程的呢?
因为 Swing的各种组件,比如JTextField,JButton
都不是线程安全的,这就意味着,如果有多个线程,那么同一个JTextField的setText方法,可能会被多个线程同时调用,这会导致线程安全问题)
的发生。
- 如果把组件类设计成为
线程安全
的,由于Swing事件调度的复杂性,就很有可能导致死锁
的发生。
为了规避同步问题,以及降低整个Swing设计的复杂度,提高Swing的相应速度,Swing中的 事件调度线程被设计成为了单线程
模式,即只有一个线程
在负责事件的响应工作。
12.3.初始化线程
如代码所示,我们初始化一个图形界面的时候,都会直接在主方法的主线程里,直接调用如下代码来进行初始化
new TestFrame().setVisible(true);
如果是简单程序这没有什么问题,如果是复杂的程序就有可能产生问题了。
- 因为这里有两个线程在同时访问组件:
1. 主线程 2. 事件调度线程。
如果是复杂的图形界面程序,就有可能出现这两个线程同时操作的情况,导致同步问题的产生。
为了规避这个问题的产生,创建和显示界面的工作,最好也交给事件调度线程
,这样就保证了只有一个线程在访问这些组件
public class TestGUI {
public static void main(String[] args) {
//交给主线程创建和显示界面
// new TestFrame().setVisible(true);
//交给事件调度线程创建和显示界面
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestFrame().setVisible(true);
}
});
}
static class TestFrame extends JFrame {
public TestFrame() {
setTitle("LoL");
setSize(400, 300);
setLocation(200, 200);
setLayout(null);
JButton btn = new JButton("一键秒对方基地挂");
btn .setBounds(50, 50, 280, 30);
add(btn );
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
System.out.println("当前线程是否是 事件调度线程: " + SwingUtilities.isEventDispatchThread());
}
}
}
12.4.事件调度线程
以 按钮监听 中的代码为例,ActionListener.actionPerformed
中的代码,就是事件调度线
程执行的。
可以借助
SwingUtilities.isEventDispatchThread()
判断是否是事件调度线程
@Test
public void eventDiapatcherThread() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(400, 300);
frame.setLocation(580, 200);
frame.setLayout(null);
final JLabel label = new JLabel();
ImageIcon i = new ImageIcon("E:\\data\\gareen.jpg");
label.setIcon(i);
label.setBounds(50, 50, i.getIconWidth(), i.getIconHeight());
JButton b = new JButton("隐藏图片");
b.setBounds(150, 200, 100, 30);
b.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
label.setVisible(false);
System.out.println("当前使用的是事件调度线程:" + SwingUtilities.isEventDispatchThread());
}
});
frame.add(label);
frame.add(b);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
点击按钮
12.5.长耗时任务线程
有时候需要执行长耗时任务,比如数据库查询,文件复制,访问网络等。
- 这些操作一般都会在
事件响应后发起
,就会自动进入事件调度线程
。 而事件调度线程又是单线程模式
,其结果就会是在执行这些长耗时任务的时候,界面就无响应了。 - 如图所示,当点击第一个按钮的时候,会在其中进行一个5秒钟的任务,这个期间,第一个按钮会保持按下状态,其他按钮也无法点击,出现了无响应了状态。
为了解决这个问题,Swing提供了一个SwingWorker类
来解决。SwingWorker是一个抽象类
,为了使用,必须实现方法 doInBackground
,在doInBackground中编写我们的任务,然后执行SwingWorker的execute方法
,放在专门的工作线程中去运行。
@Test
public void longConsumingTask() throws InterruptedException {
JFrame frame = new JFrame("LoL");
frame.setSize(300, 300);
frame.setLocation(200, 200);
frame.setLayout(new FlowLayout());
JButton b1 = new JButton("在事件调度线程中执行长耗时任务");
JButton b2 = new JButton("使用SwingWorker执行长耗时任务");
JLabel label = new JLabel("任务执行结果");
frame.add(b1);
frame.add(b2);
frame.add(label);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
label.setText("开始执行完毕");
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
label.setText("任务执行完毕");
}
});
b2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
System.out.println("执行这个SwingWorder的线程是:" + Thread.currentThread().getName());
label.setText("开始执行完毕");
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
label.setText("任务执行完毕");
return null;
}
};
worker.execute();
}
});
frame.setVisible(true);
//设置休眠时间,是为了延缓主线程销毁
TimeUnit.SECONDS.sleep(1000);
}
12.6.练习:搜索指定目录下的所有Java文件
public class SearchJavaFile {
private static int FOUND_COUNT = 0;
public static void main(String[] args) {
// 为了规避线程安全问题的产生,创建和显示界面的工作,使用调度线程初始化窗口,这样就保证了只有一个线程在访问这些组件
SwingUtilities.invokeLater(() -> {
WebLookAndFeel.install();
new SearchFrame().setVisible(true);
}
);
}
/** * 递归搜索文件内容,是否包含指定关键字文件个数 * * @param file 目录 * @param search 关键字 * @param suffix 文件后缀 */
public static void search(File file, String search, String suffix) {
if (file.isFile()) {
if (file.getName().toLowerCase().endsWith(suffix)) {
String fileContent = readFileConent(file);
if (fileContent.contains(search)) {
System.out.println(fileContent);
FOUND_COUNT++;
}
}
}
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
search(f, search, suffix);
}
}
}
/** * 递归搜索文件内容,是否包含指定关键字文件个数 * * @param file 目录 * @param search 关键字 * @param suffix 文件后缀 */
public static int searchPlus(File file, String search, String suffix) {
int foundCount = 0;
if (file.isFile()) {
if (file.getName().toLowerCase().endsWith(suffix)) {
String fileContent = readFileConent(file);
if (fileContent.contains(search)) {
System.out.println(fileContent);
foundCount += 1;
}
}
}
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
foundCount += searchPlus(f, search, suffix);
}
}
return foundCount;
}
/** * 读取文件文本内容 * * @param file * @return */
public static String readFileConent(File file) {
try (FileReader fr = new FileReader(file)) {
char[] all = new char[(int) file.length()];
fr.read(all);
return new String(all);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/** * 查询窗口初始化 */
static class SearchFrame extends JFrame {
JLabel lLocation = new JLabel("查询目录");
JLabel lSearch = new JLabel("文件内容");
JTextField tfLocation = new JTextField("e:/data");
JTextField tfSearch = new JTextField("g");
JButton bSubmit = new JButton("搜索");
/** * 容器初始化 */
SearchFrame() {
int gap = 50;
this.setLayout(null);
JPanel pInput = new JPanel();
JPanel pSubmit = new JPanel();
pInput.setLayout(new GridLayout(2, 2, gap, gap));
pInput.add(lLocation);
pInput.add(tfLocation);
pInput.add(lSearch);
pInput.add(tfSearch);
pSubmit.add(bSubmit);
pInput.setBounds(50, 20, 200, 100);
pSubmit.setBounds(0, 130, 300, 150);
this.add(pInput);
this.add(pSubmit);
this.setSize(300, 200);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
bSubmit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
FOUND_COUNT = 0;
String location = tfLocation.getText();
String search = tfSearch.getText();
if (0 == location.length()) {
JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空");
tfLocation.grabFocus();
return;
}
if (0 == search.length()) {
JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空");
tfSearch.grabFocus();
return;
}
File folder = new File(location);
if (!folder.exists()) {
JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不存在");
tfLocation.grabFocus();
return;
}
if (!folder.isDirectory()) {
JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不是一个文件夹");
tfLocation.grabFocus();
return;
}
//正在搜索按钮
freeze();
SwingWorker worker = new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
//work here
int foundCount = searchPlus(folder, search, ".java");
JOptionPane.showMessageDialog(SearchFrame.this, "总共找到满足条件的文件: " + foundCount + " 个");
//搜索按钮
unfreeze();
return null;
}
};
worker.execute();
}
});
}
/** * 搜索按钮 */
private void unfreeze() {
bSubmit.setText("搜索");
bSubmit.setEnabled(true);
tfLocation.setEnabled(true);
tfSearch.setEnabled(true);
}
/** * 正在搜索按钮 */
private void freeze() {
bSubmit.setText("正在搜索");
bSubmit.setEnabled(false);
tfLocation.setEnabled(false);
tfSearch.setEnabled(false);
}
}
}
十三.界面风格
Java提供了非常便捷的方式切换界面风格
引入依赖
<dependency>
<groupId>com.weblookandfeel</groupId>
<artifactId>weblaf-ui</artifactId>
<version>1.2.13</version>
</dependency>
加入这一行代码就行了
WebLookAndFeel.install();
使用前:
使用后:
网上还有很多Swing风格包,大家可以自己去网上搜索
还没有评论,来说两句吧...