欢迎光临建站营销网站,一站式轻松搭建网站

快速平台建站公司

自助建站搭建平台系统,企业快速建站平台

从零开始制作高仿QQ(一)——为窗口添加背景

作者:jcmp      发布时间:2021-04-16      浏览量:0
一、目录二、背景三、开发环境四、开始之前

一、目录

二、背景

三、开发环境

四、开始之前

五、真正的正文

1. 制作无标题栏的窗口

2. 给窗口添加关闭按钮

3. 添加鼠标拖动事件

4. 添加背景图片

本节代码

附录

1. 相关链接

2. 资源图片

六、背景

我的Java作业源程序: Java局域网聊天儿小软件儿 (本文的程序和我的作业还是有一丢丢的区别的,作业毕竟是作业, 总要给老师留点面子不是嘛 )。

在程序的开发过程中,我还借鉴了许多前辈们的代码 但忘了保存链接 ,我会 尽可能地找到这些链接,并 在相应的章节把这些链接 中能找到的部分 贴在附录中。好了,废话不多说,下面开始正文。

七、开发环境

八、开始之前

九、真正的正文

1. 制作无标题栏的窗口

由于我们现在做的是群聊的界面,我测量了一下,得到了窗口的尺寸为 。因此可以用如下的代码创建我们的窗口:

package com.jianshu.main; import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent; import javax.swing.JDialog;import javax.swing.JFrame; public class JavaMyOICQ extends JFrame { public static void main(String[] args) { JavaMyOICQ dlg = new JavaMyOICQ(); dlg.setVisible(true); } public JavaMyOICQ() { super(); this.setSize(891, 683); this.setLocationRelativeTo(null);// 设置窗口在屏幕正中间显示 this.setResizable(false); this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); this.setUndecorated(true);// 这句话用来阻止窗体采用本机系统修饰,这样窗体就没有标题栏和边框了 }}

但是这个时候点击运行,会发现弹出了一个灰色的长方形,怎么关都关不掉,只能用任务管理器结束进程。仔细想想也对,Java窗体的关闭按钮是在标题栏上的,关了标题栏,关闭按钮可不就没了么!所以就有了下面的步骤。

2. 给窗口添加关闭按钮

—— 什么?!不就是加一个按钮么,至于搞一个小节出来?

话是这么说没错,但是这里有一个要注意的地方。有的同学喜欢用 System.exit(0); 来关闭窗口。但是这会出现一个问题,就是点了关闭按钮,窗口确实关了,但是程序也退出了。这就有点不好了吧,总不能每次想要和别人聊天都要重新登录一遍呀?所以,我们只需要把 System.exit(0); 改为 frame.dispose(); 就好了。

接下来,我们可以再添加“最大化”和“最小化”按钮,它们的代码分别如下:

frame.setExtendedState(JFrame.ICONIFIED);// 使窗口最小化到任务栏图标frame.setExtendedState(JFrame.MAXIMIZED_BOTH);// 使窗口最大化到全屏frame.setExtendedState(JFrame.NORMAL);// 使窗口恢复正常(设置的)大小。

还需要注意的是,在向窗口中添加控件时,要尽量先将控件添加到一个 JPanel 容器中,再一并添加到窗口中,方便控件的管理。

3. 添加鼠标拖动事件

标题栏也去掉了,关闭按钮也加了,但是现在还是有一个美中不足的地方,就是程序的窗口总是在屏幕的正中央,没办法拖动了。所以接下来,我们要给我们的窗口添加鼠标拖动事件。

“鼠标拖动事件”听起来像是一个事件,但其实它由两部分组成:一个是当鼠标按下时,要记录开始拖动时鼠标的坐标,另一个就是在鼠标拖动时记录鼠标的水平和垂直位移。这样就可以实时地计算出窗口应处的位置,并移动它。

// 需要额外添加的属性private int xOld = 0;private int yOld = 0; // ...... // 给窗口添加鼠标拖动事件frame.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { xOld = e.getX();//记录鼠标按下时的坐标 yOld = e.getY(); }}); frame.addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { if (yOld > 37) return;// 限定鼠标拖动事件的有效坐标范围,至于为什么不是38,请大家自行测试 int xOnScreen = e.getXOnScreen(); int yOnScreen = e.getYOnScreen(); int xx = xOnScreen - xOld; int yy = yOnScreen - yOld; setLocation(xx, yy);//设置拖拽后,窗口的位置 }});

4. 添加背景图片

说了这么多,总算到这节的重点了。说到添加背景图片,我可是在网上找了种方法。这些方法不是图片添加不上,就是会覆盖控件,就算没有覆盖控件的,把窗口拖到屏幕外面(一部分)再拖回来,刚才到屏幕外面的那一部分控件甚至都不会重绘(真是个不错的方法)。

但是下面这段代码就不同了。它的原理也很简单,就是在窗口中添加一个覆盖整个窗口的 JPanel 控件,并重写它的 paintComponent() 方法,使它在绘制时将背景图片一并绘制出来。 paintComponent() 是Java虚拟机在触发窗口绘制事件时自动调用的方法,不需要我们手动去调用。这个方法只一个 Graphics 型变量作为形参。这个参数可以理解为一个画笔,我们可以用它画出任意的自己想要的东西(有关 paintComponent() 方法及 Graphics 型变量更详细的用法我们会在之后的教程中讲到,在这里就只简单的提一下)。

还有一个问题,就是Java如何读取图片呢?其实,Java读取图片可以有两种方法:一是直接根据路径读取图片文件,二是读取资源文件中的图片文件。在这里我们选择第二种方法。

将图片添加到Java工程的资源文件中的方法其实很简单。只需要:新建包->打开工程文件夹下的src文件夹->进入新建的包->将图片复制进文件夹->回到Eclipse->右键单击工程名称->选择Refresh 即可。

若要读取资源文件中的图片,只需要如下代码:

import java.awt.Image;import javax.swing.ImageIcon; String FILE_PATH = ""; // FILE_PATH是图片的路径。// 如:图片存放在com.jianshu.images.frameui包中,则图片路径为:// /com/jianshu/image/frameui/main_frame_background_default.png// 注意不要落下第一个斜杠 ImageIcon imageIcon = new ImageIcon(getClass().getResource(FILE_PATH));Image image = imageIcon.getImage();

so,给窗口添加背景图的代码如下:

// 给窗口设置背景图片JPanel imagePanel = new JPanel() { public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g;// Graphics2D比Graphics具有更强大的功能,所以在这里我们做一下类型转换 // 设置画笔的抗锯齿属性 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.drawImage(background, 0, 0, 891, 683, this); g2d.drawImage(functionList, 0, 40, 364, 49, this); }};frame.add(imagePanel);

但需要注意的是,现在我们窗口上的所有控件都要先添加到 imagePanel 控件中,而不是直接添加到窗口里了。

十、本节代码

总结一下本节完成后发生更改的所有代码。同时,我将主方法单独拿出来放到一个专门的主类中,作为程序的入口,方便之后的修改和做其它的设置。

JavaMyOICQ.java

package com.jianshu.main; import com.jianshu.frames.JGroupChatDlg; public class JavaMyOICQ { public static void main(String[] args) { JGroupChatDlg dlg = new JGroupChatDlg(); dlg.setVisible(true); }}

JGroupChatDlg.java

package com.jianshu.frames; import java.awt.BorderLayout;import java.awt.Dimension;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.GridLayout;import java.awt.Image;import java.awt.RenderingHints;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import java.awt.event.MouseMotionAdapter; import javax.swing.ImageIcon;import javax.swing.JButton;import javax.swing.JDialog;import javax.swing.JFrame;import javax.swing.JPanel; public class JGroupChatDlg extends JFrame implements ActionListener { private static final long serialVersionUID = 2973650460422728478L; // 由于这里我们用了静态变量存储图片,所以就不能用getClass()方法了 private static final Image background = new ImageIcon(JGroupChatDlg.class.getResource("/com/jianshu/images/frameui/main_frame_background_default.png")).getImage(); private static final Image functionList = new ImageIcon(JGroupChatDlg.class.getResource("/com/jianshu/images/frameui/main_frame_function_list.png")).getImage(); private boolean state = false; private int xOld = 0;// 辅助窗口拖动事件 private int yOld = 0; private JPanel titleBar = new JPanel(); private JPanel upButtonsPanel = new JPanel(); private JButton minUp = new JButton(); private JButton maxUp = new JButton(); private JButton closeUp = new JButton(); public JGroupChatDlg() { super(); this.setSize(891, 683); this.setLocationRelativeTo(null);// 设置窗口在屏幕正中间显示 this.setResizable(false); init(); this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); this.setUndecorated(true);// 这句话用来阻止窗体采用本机系统修饰,这样窗体就没有标题栏和边框了 this.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { xOld = e.getX(); //记录鼠标按下时的坐标 yOld = e.getY(); } }); this.addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { if (yOld > 37) return; int xOnScreen = e.getXOnScreen(); int yOnScreen = e.getYOnScreen(); int xx = xOnScreen - xOld; int yy = yOnScreen - yOld; setLocation(xx, yy); //设置拖拽后,窗口的位置 } }); } private void init() { JPanel imagePanel = new JPanel() { private static final long serialVersionUID = -2493397636069899072L; public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g;// Graphics2D比Graphics具有更强大的功能,所以在这里我们做一下类型转换 // 设置画笔的抗锯齿属性 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.drawImage(background, 0, 0, 891, 683, this); g2d.drawImage(functionList, 0, 40, 364, 49, this); } }; this.add(imagePanel); imagePanel.setLayout(new BorderLayout()); titleBar.setPreferredSize(new Dimension(891, 32)); titleBar.setLayout(new BorderLayout()); titleBar.setOpaque(false); imagePanel.add(titleBar, BorderLayout.NORTH); upButtonsPanel.setSize(96, 32); upButtonsPanel.setLayout(new GridLayout(1, 3)); upButtonsPanel.add(minUp); upButtonsPanel.add(maxUp); upButtonsPanel.add(closeUp); minUp.addActionListener(this); maxUp.addActionListener(this); closeUp.addActionListener(this); titleBar.add(upButtonsPanel, BorderLayout.EAST); } public void actionPerformed(ActionEvent e) { if (e.getSource() == minUp) { this.setExtendedState(JFrame.ICONIFIED); } else if (e.getSource() == maxUp) { if (state) { this.setExtendedState(JFrame.NORMAL); state = false; } else { this.setExtendedState(JFrame.MAXIMIZED_BOTH); state = true; } } else if (e.getSource() == closeUp) { this.dispose(); } }}

十一、附录

1. 相关链接

2. 资源图片