Java 在组件中心之间画一条线
Java drawing a line between the centre of components
当用户单击一个标签、在另一个标签上拖动并释放时,我试图在两个 JLabel 的中心之间画一条线。无论 window 大小如何,哪个都应该有效。
但是线条不居中,请问如何解决?
以下示例有效,但线条似乎被 JFrame 的边界偏移,因此它们不在中心。
我不想尝试从点计算中删除 JFrame 边框,因为实际界面比给出的示例更复杂,并且 JFrame 中包含的组件更多。
我认为点计算将与我使用的 JPanel 相关,所以我不会 运行 进入 JFrame 边界问题,但事实似乎并非如此。
提前感谢您的帮助。
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class test extends JFrame implements MouseListener {
private static JPanel panel = new JPanel();
private static test window = new test();
public test() {
panel.setLayout(new GridLayout(2, 2));
JLabel l1 = new JLabel();
JLabel l2 = new JLabel();
JLabel l3 = new JLabel();
JLabel l4 = new JLabel();
l1.setOpaque(true);
l2.setOpaque(true);
l3.setOpaque(true);
l4.setOpaque(true);
l1.setBackground(Color.RED);
l2.setBackground(Color.BLUE);
l3.setBackground(Color.GREEN);
l4.setBackground(Color.ORANGE);
l1.setName("l1");
l2.setName("l2");
l3.setName("l3");
l4.setName("l4");
panel.add(l1);
panel.add(l2);
panel.add(l3);
panel.add(l4);
panel.addMouseListener(this);
this.add(panel);
}
public static void drawArcs(int x1, int y1, int x2, int y2) {
Graphics g = window.getGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.drawLine(x1, y1, x2, y2);
}
private static int x1 = 0;
private static int y1 = 0;
public void mousePressed(MouseEvent e) {
Component square1 = panel.getComponentAt(new Point(e.getX(), e.getY()));
System.out.println( square1.getName() );
x1 = square1.getX() + square1.getWidth() / 2;
y1 = square1.getY() + square1.getHeight() / 2;
}
public void mouseReleased(MouseEvent e) {
Component square2 = panel.getComponentAt(new Point(e.getX(), e.getY()));
System.out.println( square2.getName() );
int x2 = square2.getX() + square2.getWidth() / 2;
int y2 = square2.getY() + square2.getHeight() / 2;
drawArcs(x1, y1, x2, y2);
}
@Override
public void mouseClicked(MouseEvent arg0) {}
@Override
public void mouseEntered(MouseEvent arg0) {}
@Override
public void mouseExited(MouseEvent arg0) {}
public static void main(String[] args) {
window.setVisible(true);
window.setSize(400, 400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
所以,基本问题是,你的组件的位置是相对于 panel
,它被框架的装饰所抵消,但你正在使用框架现有的 Graphics
上下文来绘制行,因此行未对齐。
除了不使用 getGraphics
之外,您可以通过使用框架的 glassPane
来达到预期的效果,例如
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test extends JFrame implements MouseListener {
private JPanel panel = new JPanel();
public Test() {
ConnectTheDots dots = new ConnectTheDots();
setGlassPane(dots);
dots.setVisible(true);
panel.setLayout(new GridLayout(2, 2));
panel.add(createLabel(Color.RED));
panel.add(createLabel(Color.BLUE));
panel.add(createLabel(Color.GREEN));
panel.add(createLabel(Color.ORANGE));
panel.addMouseListener(this);
this.add(panel);
}
private Component pressComponent;
private Component releaseComponent;
public void mousePressed(MouseEvent e) {
pressComponent = panel.getComponentAt(new Point(e.getX(), e.getY()));
}
public void mouseReleased(MouseEvent e) {
releaseComponent = panel.getComponentAt(new Point(e.getX(), e.getY()));
joinTheDots();
}
@Override
public void mouseClicked(MouseEvent arg0) {
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
protected void joinTheDots() {
Rectangle bounds = pressComponent.getBounds();
Point startPoint = centerOf(bounds);
bounds = releaseComponent.getBounds();
Point endPoint = centerOf(bounds);
((ConnectTheDots) getGlassPane()).drawLine(startPoint, endPoint);
}
protected Point centerOf(Rectangle bounds) {
return new Point(
bounds.x + (bounds.width / 2),
bounds.y + (bounds.height / 2));
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Test frame = new Test();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected JLabel createLabel(Color background) {
JLabel label = new JLabel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
};
label.setOpaque(true);
label.setBackground(background);
return label;
}
public class ConnectTheDots extends JPanel {
private Point startPoint;
private Point endPoint;
public ConnectTheDots() {
setOpaque(false);
}
public void drawLine(Point startPoint, Point endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint;
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (startPoint != null && endPoint != null) {
Graphics2D g2d = (Graphics2D) g.create();
Line2D line = new Line2D.Double(startPoint, endPoint);
g2d.setColor(Color.BLACK);
g2d.draw(line);
g2d.dispose();
}
}
}
}
现在,这仅在内容覆盖 contentPane
的整个可见区域时才有效,虽然您可能会把位置信息从一个组件上下文转换为另一个组件上下文,但更简单的解决方案是使用 JXLayer
.
在这种情况下我会避免重写 paint
的原因是 Swing 组件可以在不需要绘制父组件的情况下更新,这可能会清除父组件上次绘制的内容画...
查看 How to Use Root Panes 了解更多详情
当用户单击一个标签、在另一个标签上拖动并释放时,我试图在两个 JLabel 的中心之间画一条线。无论 window 大小如何,哪个都应该有效。
但是线条不居中,请问如何解决?
以下示例有效,但线条似乎被 JFrame 的边界偏移,因此它们不在中心。
我不想尝试从点计算中删除 JFrame 边框,因为实际界面比给出的示例更复杂,并且 JFrame 中包含的组件更多。
我认为点计算将与我使用的 JPanel 相关,所以我不会 运行 进入 JFrame 边界问题,但事实似乎并非如此。
提前感谢您的帮助。
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class test extends JFrame implements MouseListener {
private static JPanel panel = new JPanel();
private static test window = new test();
public test() {
panel.setLayout(new GridLayout(2, 2));
JLabel l1 = new JLabel();
JLabel l2 = new JLabel();
JLabel l3 = new JLabel();
JLabel l4 = new JLabel();
l1.setOpaque(true);
l2.setOpaque(true);
l3.setOpaque(true);
l4.setOpaque(true);
l1.setBackground(Color.RED);
l2.setBackground(Color.BLUE);
l3.setBackground(Color.GREEN);
l4.setBackground(Color.ORANGE);
l1.setName("l1");
l2.setName("l2");
l3.setName("l3");
l4.setName("l4");
panel.add(l1);
panel.add(l2);
panel.add(l3);
panel.add(l4);
panel.addMouseListener(this);
this.add(panel);
}
public static void drawArcs(int x1, int y1, int x2, int y2) {
Graphics g = window.getGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.drawLine(x1, y1, x2, y2);
}
private static int x1 = 0;
private static int y1 = 0;
public void mousePressed(MouseEvent e) {
Component square1 = panel.getComponentAt(new Point(e.getX(), e.getY()));
System.out.println( square1.getName() );
x1 = square1.getX() + square1.getWidth() / 2;
y1 = square1.getY() + square1.getHeight() / 2;
}
public void mouseReleased(MouseEvent e) {
Component square2 = panel.getComponentAt(new Point(e.getX(), e.getY()));
System.out.println( square2.getName() );
int x2 = square2.getX() + square2.getWidth() / 2;
int y2 = square2.getY() + square2.getHeight() / 2;
drawArcs(x1, y1, x2, y2);
}
@Override
public void mouseClicked(MouseEvent arg0) {}
@Override
public void mouseEntered(MouseEvent arg0) {}
@Override
public void mouseExited(MouseEvent arg0) {}
public static void main(String[] args) {
window.setVisible(true);
window.setSize(400, 400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
所以,基本问题是,你的组件的位置是相对于 panel
,它被框架的装饰所抵消,但你正在使用框架现有的 Graphics
上下文来绘制行,因此行未对齐。
除了不使用 getGraphics
之外,您可以通过使用框架的 glassPane
来达到预期的效果,例如
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test extends JFrame implements MouseListener {
private JPanel panel = new JPanel();
public Test() {
ConnectTheDots dots = new ConnectTheDots();
setGlassPane(dots);
dots.setVisible(true);
panel.setLayout(new GridLayout(2, 2));
panel.add(createLabel(Color.RED));
panel.add(createLabel(Color.BLUE));
panel.add(createLabel(Color.GREEN));
panel.add(createLabel(Color.ORANGE));
panel.addMouseListener(this);
this.add(panel);
}
private Component pressComponent;
private Component releaseComponent;
public void mousePressed(MouseEvent e) {
pressComponent = panel.getComponentAt(new Point(e.getX(), e.getY()));
}
public void mouseReleased(MouseEvent e) {
releaseComponent = panel.getComponentAt(new Point(e.getX(), e.getY()));
joinTheDots();
}
@Override
public void mouseClicked(MouseEvent arg0) {
}
@Override
public void mouseEntered(MouseEvent arg0) {
}
@Override
public void mouseExited(MouseEvent arg0) {
}
protected void joinTheDots() {
Rectangle bounds = pressComponent.getBounds();
Point startPoint = centerOf(bounds);
bounds = releaseComponent.getBounds();
Point endPoint = centerOf(bounds);
((ConnectTheDots) getGlassPane()).drawLine(startPoint, endPoint);
}
protected Point centerOf(Rectangle bounds) {
return new Point(
bounds.x + (bounds.width / 2),
bounds.y + (bounds.height / 2));
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Test frame = new Test();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected JLabel createLabel(Color background) {
JLabel label = new JLabel() {
@Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
};
label.setOpaque(true);
label.setBackground(background);
return label;
}
public class ConnectTheDots extends JPanel {
private Point startPoint;
private Point endPoint;
public ConnectTheDots() {
setOpaque(false);
}
public void drawLine(Point startPoint, Point endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint;
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (startPoint != null && endPoint != null) {
Graphics2D g2d = (Graphics2D) g.create();
Line2D line = new Line2D.Double(startPoint, endPoint);
g2d.setColor(Color.BLACK);
g2d.draw(line);
g2d.dispose();
}
}
}
}
现在,这仅在内容覆盖 contentPane
的整个可见区域时才有效,虽然您可能会把位置信息从一个组件上下文转换为另一个组件上下文,但更简单的解决方案是使用 JXLayer
.
在这种情况下我会避免重写 paint
的原因是 Swing 组件可以在不需要绘制父组件的情况下更新,这可能会清除父组件上次绘制的内容画...
查看 How to Use Root Panes 了解更多详情