首页 > 代码库 > 【Swing 7】坐标陷阱与单个组件拖放

【Swing 7】坐标陷阱与单个组件拖放

  之前模仿QQ界面的时候,一直很苦恼布局的问题。虽说绝对定位相对于JFrame默

认的BorderLayout(布局管理器),JPanel的FlowLayout(流式管理器)方便了不少。可

以通过setBounds()直接设置坐标,可要是组件一多起来。非把你累死不可。

  好了,不多说,为什么说会有陷阱呢。大家看看下面这两个有界面。源代码贴在这。

两个界面就差了句setUndecorated(true); 可以看出,无论有无边框,它们的大小都是

一样的。到这里倒是还没出现问题!接着往下看。

技术分享
 1 package demo;
 2 
 3 import javax.swing.*;
 4 import java.awt.*;
 5 
 6 public class DrawPanel {
 7     public static void main(String[] args) {
 8         new DrawPanel();
 9     }
10     public DrawPanel() {
11         JFrame f = new JFrame();
12         
13         f.setUndecorated(true);         // 去掉界面的边框
14         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关掉界面时结束javaw.exe进程
15         f.setBounds(200, 200, 400, 300); // 参数分别是界面左上角的横, 纵坐标, 长和宽
16         f.setVisible(true);           
17     }
18 }
去掉边框的界面

技术分享

上面是什么都没加,空空如也的情况。接下来我们给它们加一个按钮,看看会有什么变化。

技术分享
 1 package demo;
 2 
 3 import javax.swing.*;
 4 import java.awt.*;
 5 
 6 public class DrawPanel {
 7     public static void main(String[] args) {
 8         new DrawPanel();
 9     }
10     public DrawPanel() {
11         JFrame f = new JFrame();
12         f.setUndecorated(true);         // 去掉界面的边框
13         f.setLayout(null);              // null即不使用JFrame默认的边界布局, 而是设置布局为绝对定位
14         
15         JButton b = new JButton("点击");
16         b.setBounds(50, 50, 60, 20);    // 参数分别是界面左上角的横, 纵坐标, 长和宽
17         f.add(b);
18         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关掉界面时结束javaw.exe进程
19         f.setBounds(200, 200, 400, 300); 
20         f.setVisible(true);           
21     }
22 }
添加按钮的对比

问题来了,来两张对比的图片。一目了然。

水平坐标(x值)的对比。

技术分享

纵坐标(y值)的对比 

技术分享

    从上面两幅图很容易看出,有边框的按钮无论是左上角的x值或是y值都比没边框的

大了点。当然,这是相对于窗口来说的。现在,让我们来寻找真相。

System.out.println("按钮在界面上的坐标: " + b.getX() + ", " + b.getY()); 

    很不幸,输出都是50, 50

    这和前面我们设置按钮的位置时用的代码相符的。横纵坐标均是50。

b.setBounds(50, 50, 60, 20);    // 参数分别是界面左上角的横, 纵坐标, 长和宽

    咦,那就奇怪了。明明有边框的按钮横纵坐标明显都偏大。(事实上,那个多出来的坐

就是左边框和上边框啦!),我们再来证明一下。先要解决怎么获得坐标的问题,也就

是怎么得到界面上任一点的坐标呢?

    有没有想到监听窗口呢?其实就是使用鼠标事件!

1 import java.awe.event.*; // 别忘了导入
2 
3 b.addMouseListener(new MouseAdapter() {
4     public void mousePressed(MouseEvent e);
5         System.out.println("按钮在界面上的实际坐标: " + e.getX() + ", " + e.getY());
6 });

    大家在按钮周围随便点点,便会发现,实际上按钮的x值是接近9,y值是接近36的。事

实上,这也是左边框和上边框的宽度。别问我为什么,这是我昨晚点了好久才得出来的数据。

    好了,说了这么多,结论:

    1. e.getX()和e.getY()得到的坐标才是真正的坐标。监听界面时,得到的是界面的点坐标。

而监听按钮时,得到的是在按钮上的点坐标。(而不是在界面上的坐标)。

    2. b.getX()和b.getY()得到的不过是去除边框后的坐标,也就是setBounds()或setLocation

()里面设置的坐标。

    3. 当没有边框时,上面两者是相等的。当有边框时,要分别加上左边框和上边框的宽度。

即b.getX() + 9和b.getY() + 36。

---------------------------------------------------------------------------------------------

    解决了上面两个易错的问题,接下来就是讲讲拖动的原理了。和移动界面的原理差不多。好比

我们知道鼠标按住按钮上的那一点在按钮上的坐标 (下图中蓝色粗线),然后用该点在屏幕上的坐

标,(红色线到蓝色粗线的长度)减去该点在按钮上(蓝色粗线)的坐标。再减去界面的左上角坐标

(红色粗线),万事大吉,搞定!

     技术分享

1. 先来个没有边框的版本

技术分享
 1 package demo;
 2 
 3 import javax.swing.*;
 4 import java.awt.event.*;
 5 import java.awt.*;
 6 
 7 public class DrawPanel {
 8     int pointXOnButton, pointYOnButton;
 9     JFrame f;
10     public static void main(String[] args) {
11         new DrawPanel();
12     }
13     public DrawPanel() {
14         f = new JFrame("移动按钮");
15         f.setLayout(null);     // 不使用JFrame默认的边界布局而是使用直接设置坐标的绝对定位
16         f.setUndecorated(true);                                   // 去掉窗口自带的边框
17         
18         // 获取电脑屏幕的尺寸(如15.6寸的屏幕一般是1920 * 1080像素), 使窗口在屏幕上居中显示
19         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
20         int xOnScreen = screenSize.width / 2 - 400;       // 窗口左上角的x坐标
21         int yOnScreen = screenSize.height / 2 - 300;      // 窗口左上角的y坐标
22         init();
23         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口后退出javaw.exe进程
24         f.setBounds(xOnScreen, yOnScreen, 800, 600);
25         f.setVisible(true);
26     }
27     public void init() {
28         JButton b = new JButton("点我");
29         b.setBounds(200, 100, 60, 20);
30         b.addMouseListener(new MouseAdapter() {
31             public void mousePressed(MouseEvent e) {
32                 pointXOnButton = e.getX();                   // 点在按钮上的x坐标
33                 pointYOnButton = e.getY();                  
34             }
35         });
36         b.addMouseMotionListener(new MouseMotionAdapter() {
37             public void mouseDragged(MouseEvent e) {
38                 int pointXOnScreen = e.getXOnScreen();        // 点在屏幕上的x坐标
39                 int pointYOnScreen = e.getYOnScreen();    
40                 int frameXOnScreen = f.getX();                // 窗口左上角的x坐标 
41                 int frameYOnScreen = f.getY();
42                 int buttonXOnFrame = pointXOnScreen - pointXOnButton - frameXOnScreen;
43                 int buttonYOnFrame = pointYOnScreen - pointYOnButton - frameYOnScreen;
44                 b.setLocation(buttonXOnFrame, buttonYOnFrame);
45             }
46         });
47         f.add(b);
48     }
49 }
无边框

2. 再来个有边框的版本(要考虑边框宽度了)

技术分享
 1 package demo;
 2 
 3 import javax.swing.*;
 4 import java.awt.event.*;
 5 import java.awt.*;
 6 
 7 public class DrawPanel {
 8     int pointXOnButton, pointYOnButton;
 9     JFrame f;
10     public static void main(String[] args) {
11         new DrawPanel();
12     }
13     public DrawPanel() {
14         f = new JFrame("移动按钮");                       
15         f.setLayout(null);     // 不使用JFrame默认的边界布局而是使用直接设置坐标的绝对定位
16         
17         // 获取电脑屏幕的尺寸(如15.6寸的屏幕一般是1920 * 1080像素), 使窗口在屏幕上居中显示
18         Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
19         int xOnScreen = screenSize.width / 2 - 400;       // 窗口左上角的x坐标
20         int yOnScreen = screenSize.height / 2 - 300;      // 窗口左上角的y坐标
21         init();
22         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口后退出javaw.exe进程
23         f.setBounds(xOnScreen, yOnScreen, 800, 600);
24         f.setVisible(true);
25     }
26     public void init() {
27         JButton b = new JButton("点我");
28         b.setBounds(200, 100, 60, 20);
29         b.addMouseListener(new MouseAdapter() {
30             public void mousePressed(MouseEvent e) {
31                 pointXOnButton = e.getX();                   // 点在按钮上的x坐标
32                 pointYOnButton = e.getY();                  
33             }
34         });
35         b.addMouseMotionListener(new MouseMotionAdapter() {
36             public void mouseDragged(MouseEvent e) {
37                 int pointXOnScreen = e.getXOnScreen();        // 点在屏幕上的x坐标
38                 int pointYOnScreen = e.getYOnScreen();    
39                 int frameXOnScreen = f.getX();                // 窗口左上角的x坐标 
40                 int frameYOnScreen = f.getY();
41                 int buttonXOnFrame = pointXOnScreen - pointXOnButton - frameXOnScreen - 9; // 9是左边边框的宽度
42                 int buttonYOnFrame = pointYOnScreen - pointYOnButton - frameYOnScreen - 36; // 36是上边边框的厚度
43                 b.setLocation(buttonXOnFrame, buttonYOnFrame);
44             }
45         });
46         f.add(b);
47     }
48 }
考虑边框宽度

 

【Swing 7】坐标陷阱与单个组件拖放