JavaGUI技术——Swing概览

前言

本文原载于我的博客,地址:blog.guoziyang.top/archives/16/

写swing也有一段时间了,Java是一个不适合写GUI的程序,GUI语句繁琐又不优雅,界面也没法定制。

Oracle推出的新一代JavaFX(其实也不新了,jdk1.8以上)由于文档缺失,学习曲线陡峭,也不被人看好。

而且我也一直没有成功进入JavaFX,因为XML文件读不懂。

所以还是写一写swing吧。

这里大致的讲解一下swing的一些关键类和方法。

以我最近写的一个Java的项目——数独游戏为例

Sudoku

以上是我写的数独游戏的界面。

扫描二维码关注公众号,回复: 11506689 查看本文章

万恶之源——JFrame

GUI界面的类继承自JFrame类,而项目的主类建议实例化这个GUI类即可。

JFrame大致控制的整个界面的“壳子”,其它的组件像菜单栏啊,按钮啊什么的都是直接或者间接地添加在JFrame上的。

“壳子”的范围,可见的基本也就是窗口的标题和”关闭“”最大化“”最小化“这三个按钮。

它的初始化任务,比如设置大小啊,设置标题啊,添加其它组件啊,在构造方法里面进行即可。

主要方法讲解

setSize(int width, int length)

​ 设置窗口的长和宽

setBounds(int x, int y, int width, int length)

​ 设置窗口的默认显示位置(x,y)、长和宽

setLocationRelativeTo(null)

​ 将窗口的位置默认设置为居中,和setSize搭配使用即可

setDefaultCloseOperation(int operation)

​ 设置窗口的默认关闭动作,即点击”关闭“红叉叉的动作。operation有一下四种动作

  • JFrame.DO_NOTHING_ON_CLOSE //啥也不干
  • JFrame.HIDE_ON_CLOSE //隐藏当前窗口
  • JFrame.DISPOSE_ON_CLOSE //关闭当前窗口
  • JFrame.EXIT_ON_CLOSE //退出JVM,完全退出

一般主窗口的关闭动作设置为EXIT_ON_CLOSE,而次要的弹出界面(提示或者设置界面)设置为HIDE_ON_CLOSE或者DISPOSE_ON_CLOSE。

setResizable(false)

​ 设置窗口不可通过拖动重置大小

setTitle(String title)

​ 设置窗口标题

setContentPane(JPanel contentPane)

​ 设置窗口的主要面板

setVisible(boolean b)

​ 设置窗口是否可见,true为可见

​ 初始化窗口后请在添加完所有组件之后再设置可见,设置不可见时可用于次要窗口的关闭按钮的触发事件

add(Component c)

​ 将组件添加到主窗口上

setJMenuBar(final JMenuBar menubar)

​ 设置默认的菜单栏

必须要做的事情

  • 设置大小,setSize()或者setBounds()

  • 设置标题,setTitle()

  • 设置默认关闭动作,setDefaultCloseOperation()

  • 设置可见,setVisible()

  • (推荐)设置主面板,不推荐直接将组件添加到JFrame上

组件的容器——JPanel

容器可以自己写一个类继承JPanel,也可以直接实例化JPanel

自己写一个类多用于重写paint()方法,在面板上画画什么的……

如果没有那种“特殊的需求”,直接实例化JPanel即可

JPanel的好处在于可以选择各种各样的布局,使组件的排列更加有序。

当JPanel被设置为JFrame的主面板时,JPanel就会自适应JFrame的大小,充满JFrame

主要方法讲解

setLayout(LayoutManager mgr)

​ 设置面板的布局方式,常用的布局方式有以下四种

  • BorderLayout,把容器的的布局分为五个位置:CENTER、EAST、WEST、NORTH、SOUTH,对应着上北下南之类的。添加进去的组件会自适应位置的大小。

  • GridLayout,把容器的布局按照表格分割成等长等宽的块。构造方法GridLayout(int row, int col)指定表格的行和列。

  • FlowLayout,流水式,后添加的组件覆盖先添加的。

  • null,啥都没有,最好用,只要指定组件的x,y,width,height,就可以随便移动组件位置。

    例子:

myJPanel.setLayout(new GridLayout(9, 9));

非主面板的话建议设置下x,y,长和宽,setSize()或者setLocation()或者setBounds()皆可

必须要做的事情

  • 设置布局方式,setLayout()

菜单栏——JMenuBar

菜单栏就是窗口顶端的窄窄的一条……不是显示标题的那个!

JMenuBar是那窄窄的一条,默认什么都没有,例子中的”游戏“啊,”帮助“啊啥的叫JMenu,而JMenu下又有一些可以选择的具体的东西,叫做JMenuItem……

很乱……

总之,设计菜单栏的时候自顶向下设计,实例化以及逐层添加的时候由下而上添加,即先把JMenuItem添加到JMenu,再把JMenu添加到JMenuBar

很乱……

最后在窗口中调用setJMenuBar即可

主要方法讲解

JMenuBar的方法没啥,也就一个add,可以把JMenu添加进去

JMenu(String name)
JMenuItem(String name)

两个都是构造方法,传入的String是默认显示在菜单项上的东西

别的没了……

必须要做的事情

记得要添加!添加!添加!

实例化后不要忘了添加!!!

没了……

按钮——JButton

按钮,就是个按钮,可以按来按去那种。

简介并没啥。

实例化、设置设置之后,就可以添加到容器了

主要方法讲解

JButton(String name)

​ 传入的String是默认显示在按钮上的东西

addActionListener(ActionListener l)

​ 添加鼠标监听事件,后来再讲。

​ 1.8之后可以使用lambda表达式改写

​ ActionListener多使用匿名内部类

setText(String text)

​ 设置按钮上显示的东西

必须要做的事情

​ 给它一个名字,构造方法中或者setText()设置一个

​ 其实不设置也行,就是啥都没有

单行文本框——JTextField

没啥好说的,就是一个可以写一行文本的文本框。

可以写多行的是JTextArea——多行文本框。

嗯,就是个单行文本框。

对了,对应的还有一个JPasswordField,方法都差不多,就是输入的东西都会变成*而已,获取实际字符串需要用getPassword(),返回字符数组。

主要方法讲解

setText(String text)

​ 设置文本框内的文字

getText()

​ 返回文本框内的字符串

setEnabled(boolean l)

​ 设置是否可编辑,true为可编辑

必须要做的事情

​ 没啥,添加就行。

JTextArea

​ 多行文本框,主要方法和单行一样

​ 有一个问题,就是添加到面板上之后没有滚动框

​ 解决办法:

​ 把文本框添加到JScrollPane中,再给滚动条加一个setViewportView(JTextArea),再把JScrollPane添加到面板中

下拉菜单——JComboBox

初始化时使用一个字符串数组

其它没啥好说的,就是一个下拉菜单而已

主要方法讲解

JComboBox<>(Object[] item)

​ <>之中是范型,item是一个Object数组,大多情况是字符串数组

getSelectedItem()

​ 返回当前选中的Object

getSelectedIndex()

​ 返回当前选中的Object的序列

一定要做的事

​ 没啥,也就记得用数组初始化它就行

​ 记得添加

事件驱动——ActionListener

这是swing的精髓!

ActionListener是监听默认的鼠标点击事件,大多用于按钮和JMenuItem

在jdk1.8之后lambda表达式的引入大大简化了匿名内部类的使用,也就简化了ActionListener的使用。

ActionListener是一个接口(Interface),如果不使用匿名内部类的话请implements它而不是extends。

需要重写actionPerformed()方法,方法体就是按键之后执行的语句。

举例

举一个简单的例子,一个按钮叫作exampleBtn,点击后控制台输出hello,world。

exampleBtn.addActionListener(new ActionListener(){
   @Override
   public void actionPerformed(ActionEvent e){
       System.out.println("hello,world");
   }
});

如果要写一个类的话:

class myActionListener implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e){
       System.out.println("hello,world");
   }
}
exampleBtn.addActionListener(new myActionListener());

其中,actionPerformed()方法的参数ActionEvent e是触发该方法的组件,如果该组件是一个按钮,在方法体中就可以这样写来获得触发者的一个实例

JButton example = (JButton)e.getSource();

JDK1.8以上可以使用lamnda表达式,以上可改写为

exampleBtn.addActionListener(e -> System.out.println("hello,world));

不要问我怎么改写,我也不知道,我是用IntelliJ IDEA,里面可以自动改写为lambda表达式。

swing的事件驱动模式

​ 老师让讲一下事件驱动,google了一下……大致说说吧,主要还得听课。

​ swing的事件处理机制大致分为四步:

​ 1). 注册事件源(组件)的事件监听器

​ 2). 用户操作事件源(组件)产生事件

​ 3). 事件被注册的监听器监听到

​ 4). 监听器对事件进行处理

​ 其中,一二步是用户做的,三四步是由JVM虚拟机执行。

​ 第一步注册监听器就是add一个ActionListener,第二步操作事件源产生事件,也就是用户按下按钮或者注册的其它事件。第三步由JVM虚拟机监听到事件之后,第四步JVM虚拟机调用actionPerformed方法。

​ 大致就这样了,其它我大概也不懂了……

键盘监听——KeyListener

也算是精髓之一了吧……(瞎吹)

用于监听当前的键盘事件并作出反应

addKeyListener的定义如下

public void addKeyListener(KeyListener l)

同ActionListener一样,KeyListener也是个接口

KeyListener中有三个需要实现的方法:

  • keyPressed(KeyEvent e) 按下某个键时调用此方法。

  • keyReleased(KeyEvent e) 释放某个键时调用此方法。

  • keyTyped(KeyEvent e) 键入某个键时调用此方法。

然而我们可能并不需要这么多乱七八糟的方法,我只想按一个键执行一个动作而已……

好在有一个简单的解决方案——KeyAdapter类,只需要重写有用的方法即可。

一般重写KeyPressed()。

举例

examplePanel.addKeyLietener(new KeyAdapter(){
    @Override
    public void keyPressed(KeyEvent e){
        if(e.getKeyChar() == 'c'){
            System.out.println("hello,world);
        }
    }
});

​ 自己新建类的版本就不说了

​ 其中,参数KeyEvent e代表按键的事件(按的是哪个键)。

​ 获取具体键位有三种方法:

  • getKeyChar():处理的是比较高层的事件,返回的是每欠敲击键盘后得到的字符(中文输入法下就是汉字)。
  • getKeyCode():键盘上每一个按钮都有对应码(Code),可用来查知用户按了什么键,返回当前按钮的数值
  • getKeyText():返回与此事件中的键关联的字符。比如getKeyText(e.getKeyCode())就返回你所按下的键盘

一般如果是abc啊123啊什么的就使用getKeyChar()就差不多了,如果是什么空格键回车键之类的,就使用getKeyCode(),具体的键码对照表自己百度。getKeyText()没用过。

大坑

默认的键盘焦点(focus)是落在JFrame上的,所以如果把键盘事件绑定在其它的组件上而不设置的话,就会出现“叫天天不应”之类的奇怪现象。所以一般添加完键盘事件后,调用该组件的requestFocus()方法即可。

画画!——paintComponent

每一个组件都有一个paintCompoent()方法,用于绘制组件的默认外观,我们可以重写此方法来达到在组件上面“乱涂乱画”的效果。

paintComponent()方法定义如下

public void paintComponent(Graphics g)

当重写使,请在第一行首先调用super.paintComponent(g);来重新绘制组件,以达到每次重绘都可以清除上一次画画留下的“糟粕”的效果。

Graphics这个类的功能比较垃圾,也就能设置个线条颜色,连个粗细都设置不了,所以一般会先讲Graphics g强转成为Graphics2D,功能较为强大。

Graphics2D g2 = (Graphics2D)g;

画线举例

class MyJPanel extends JPanel{
    public MyJPanel(){
        ...
    }
    
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.setColor(Color.RED);
        g2.setStroke(new Stroke(3));
        g2.drawLine(0, 0, 20, 20);
    }
}

​ 以上的例子就是将颜色设置为红色,粗细设置为3,在(0, 0)到(20, 20)之间划一条线。

​ 如果组件的外形和某个变量有关,那个变量改变之后想要更新组件,只需调用该组件的repaint()方法即可。

​ 其它什么画圆啊、画矩形啊都有对应的方法,百度或者查API都行。

其它可能比较重要的组件

JList——列表

JTable——表格

这两个在初始化的时候可以使用DefaultListModel和DefaultTableModel作为参数初始化,好处是列表和表格中显示的数据和DefaultListModel或者DefaultTableModel绑定,只需要修改其中的一个即可。这样省去了刷新界面的麻烦。

最后不得不说一句……Thread.stop()真TM好使!!!

虽然是一个已经过时的API,但是!想停就停,绝不含糊!!!

比interrupt()效率不只高到哪里去了。

也许这就是为什么,stop()早已过时,但一直没有被删除的原因吧……

(但是一些涉及重要数据的线程就不要用这个方法了,无关紧要的像计时啊之类的用用还是很爽的)

谨以此blog献给我的小傻

猜你喜欢

转载自blog.csdn.net/qq_40856284/article/details/106498984
今日推荐