在另一个 Line 组件上绘制自定义 Line 组件,我做错了什么?
Drawing a custom Line component over another Line component, what am I doing wrong?
我正在从事一个项目,该项目需要一个自定义 Line 组件(以便以后可以包含其他功能 - 即,可以删除和移动 Lines),在 JPanel 上绘制一条新 Line (即 LinePanel)在用户按下鼠标(起点)并拖动到(终点)的任何地方。这个想法是用户可以单击并拖动到面板上的任何点以创建一条线(我在代码中包含了一个边框以显示有界矩形 - 以供反馈)。当直接点击 LinePanel 时,下面的代码工作正常,但是如果用户点击另一个 Line 组件,开始位置就会改变——这是让我发疯的部分(我怀疑问题出在 mousePressed() 逻辑内部. 我对使用 Java 的图形还比较陌生,非常希望得到一些反馈。我相信有更好的方法可以做到这一点。
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class LineFun {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new LinePanel());
frame.pack();
frame.setVisible(true);
}
}
class LinePanel extends JPanel {
Point point1 = null, point2 = null;
Line line = null;
public LinePanel() {
LineListener listener = new LineListener();
addMouseListener(listener);
addMouseMotionListener(listener);
setLayout(null);
setBackground(Color.white);
setPreferredSize(new Dimension(400, 400));
}
public void paintComponent(Graphics page) {
super.paintComponent(page);
page.setColor(Color.BLACK);
}
private class LineListener extends MouseAdapter {
/***
* It feels like this is where the problem is occuring - it works fine when I don't click on another Line
* component.
* @param e - event.
*/
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
point1 = e.getPoint();
if ((e.getSource() instanceof Line)) {
Line lineComponent = (Line)e.getSource();
Point componentPoint = lineComponent.point2;
Point newStart = new Point(point1.x + componentPoint.x, point1.y + componentPoint.y);
line = new Line(newStart, newStart);
line.addMouseListener(new LineListener());
line.addMouseMotionListener(new LineListener());
add(line);
}
else {
line = new Line(point1, point1);
line.addMouseListener(new LineListener());
line.addMouseMotionListener(new LineListener());
add(line);
}
}
@Override
public void mouseDragged(MouseEvent e) {
super.mouseDragged(e);
point2 = e.getPoint();
line.setPoint2(point2);
if ((point2.x - point1.x) >= 0 && (point2.y - point1.y) >= 0) {
line.setBounds(point1.x, point1.y, point2.x - point1.x, point2.y - point1.y);
} else if ((point2.x - point1.x) < 0 && (point2.y - point1.y) < 0) {
line.setBounds(point2.x, point2.y, point1.x - point2.x, point1.y - point2.y);
} else if ((point2.x - point1.x) < 0) {
line.setBounds(point2.x, point1.y, point1.x - point2.x, point2.y - point1.y);
} else {
line.setBounds(point1.x, point2.y, point2.x - point1.x, point1.y - point2.y);
}
repaint();
}
}
}
/***
* Line class - I've created a border so that I can see the Bounded Rectangle.
*/
class Line extends JComponent {
Point point1 = null, point2 = null;
public Line(Point point, Point point2) {
point1 = point;
this.point2 = point2;
setBorder(BorderFactory.createEtchedBorder());
}
public void setPoint2(Point point) {
point2 = point;
}
/***
* The below logic is so that the line originates from the origin (where the mouse press occurs).
* I feel like there must be a better way to do this.
* @param page
*/
public void paintComponent(Graphics page) {
super.paintComponent(page);
page.setColor(Color.black);
if (point2.x >= point1.x && point2.y >= point1.y) {
page.drawLine(0,0, point2.x - point1.x,point2.y - point1.y);
} else if ((point2.x - point1.x) < 0 && (point2.y - point1.y) < 0){
page.drawLine(0,0, point1.x - point2.x, point1.y - point2.y);
} else if (point2.x < point1.x){
page.drawLine(0, point2.y - point1.y, point1.x - point2.x, 0);
} else {
page.drawLine(point2.x - point1.x, 0, 0, point1.y - point2.y);
}
}
}
It feels like this is where the problem is occuring - it works fine when I don't click on another Line component.
正确。鼠标点是相对于您单击的组件生成的,因为您正在将鼠标侦听器添加到所有组件。
因此,如果组件是 Line
的一个实例,我猜想您需要将鼠标点转换为相对于您的 LinePanel
,这将是Line
个组件。
您可以使用SwingUtiltities.convertPoint(...)
方法进行转换。
并且您可以使用 Container.getParent()
方法获取对您的 LinePanel
的引用
我继续创建了以下 GUI。
“诀窍”是创建一个应用程序模型来保存线段。
我创建了一个 LineSegment
plain Java getter / setter class 来保存构成一行的两个 java.awt.Point
实例分割。这个class以后可以扩充来保存线条颜色和线条粗细。
我创建了一个 LineSegments
plain java getter/setter class 来保存一个临时线段和一个 java.util.List
线段。
应用程序模型由一个或多个普通Java getter / setter classes.
视图是 JFrame
和绘图 JPanel
。绘图 JPanel
绘制了应用程序模型中的任何内容。当你重画一幅画 JPanel
时,你必须 重画一切 。 “内存”在应用程序模型中,而不是在视图中。
图片中看不到,但在拖动鼠标时GUI会画一条红线。
这是完整的可运行代码。我把所有 classes 都放在 classes 里面,所以我可以 post 它们作为一个块。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LineDrawingGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LineDrawingGUI());
}
private final DrawingPanel drawingPanel;
private final LineSegments lineSegments;
public LineDrawingGUI() {
this.lineSegments = new LineSegments();
this.drawingPanel = new DrawingPanel(this, lineSegments);
}
@Override
public void run() {
JFrame frame = new JFrame("Line Drawing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public void repaint() {
drawingPanel.repaint();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private LineSegments lineSegments;
public DrawingPanel(LineDrawingGUI lineDrawingGUI,
LineSegments lineSegments) {
this.lineSegments = lineSegments;
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(400, 400));
LineListener listener = new LineListener(lineDrawingGUI, lineSegments);
this.addMouseListener(listener);
this.addMouseMotionListener(listener);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
LineSegment lineSegment = lineSegments.getTemporaryLineSegment();
if (lineSegment != null) {
g.setColor(Color.RED);
drawLine(g, lineSegment);
}
g.setColor(Color.BLACK);
for (LineSegment segment : lineSegments.getLineSegments()) {
drawLine(g, segment);
}
}
private void drawLine(Graphics g, LineSegment lineSegment) {
Point startPosition = lineSegment.getStartPosition();
Point endPosition = lineSegment.getEndPosition();
g.drawLine(startPosition.x, startPosition.y,
endPosition.x, endPosition.y);
}
}
public class LineListener extends MouseAdapter {
private LineDrawingGUI lineDrawingGUI;
private LineSegments lineSegments;
private Point startPosition;
public LineListener(LineDrawingGUI lineDrawingGUI,
LineSegments lineSegments) {
this.lineDrawingGUI = lineDrawingGUI;
this.lineSegments = lineSegments;
}
@Override
public void mousePressed(MouseEvent event) {
this.startPosition = event.getPoint();
}
@Override
public void mouseReleased(MouseEvent event) {
lineSegments.setTemporaryLineSegment(null);
LineSegment lineSegment = new LineSegment();
lineSegment.setStartPosition(startPosition);
lineSegment.setEndPosition(event.getPoint());
lineSegments.addLineSegment(lineSegment);
lineDrawingGUI.repaint();
}
@Override
public void mouseDragged(MouseEvent event) {
LineSegment lineSegment = new LineSegment();
lineSegment.setStartPosition(startPosition);
lineSegment.setEndPosition(event.getPoint());
lineSegments.setTemporaryLineSegment(lineSegment);
lineDrawingGUI.repaint();
}
}
public class LineSegments {
private LineSegment temporaryLineSegment;
private final List<LineSegment> lineSegments;
public LineSegments() {
this.lineSegments = new ArrayList<>();
}
public LineSegment getTemporaryLineSegment() {
return temporaryLineSegment;
}
public void setTemporaryLineSegment(LineSegment temporaryLineSegment) {
this.temporaryLineSegment = temporaryLineSegment;
}
public void addLineSegment(LineSegment lineSegment) {
this.lineSegments.add(lineSegment);
}
public List<LineSegment> getLineSegments() {
return lineSegments;
}
}
public class LineSegment {
private Point startPosition;
private Point endPosition;
public Point getStartPosition() {
return startPosition;
}
public void setStartPosition(Point startPosition) {
this.startPosition = startPosition;
}
public Point getEndPosition() {
return endPosition;
}
public void setEndPosition(Point endPosition) {
this.endPosition = endPosition;
}
}
}
我正在从事一个项目,该项目需要一个自定义 Line 组件(以便以后可以包含其他功能 - 即,可以删除和移动 Lines),在 JPanel 上绘制一条新 Line (即 LinePanel)在用户按下鼠标(起点)并拖动到(终点)的任何地方。这个想法是用户可以单击并拖动到面板上的任何点以创建一条线(我在代码中包含了一个边框以显示有界矩形 - 以供反馈)。当直接点击 LinePanel 时,下面的代码工作正常,但是如果用户点击另一个 Line 组件,开始位置就会改变——这是让我发疯的部分(我怀疑问题出在 mousePressed() 逻辑内部. 我对使用 Java 的图形还比较陌生,非常希望得到一些反馈。我相信有更好的方法可以做到这一点。
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class LineFun {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new LinePanel());
frame.pack();
frame.setVisible(true);
}
}
class LinePanel extends JPanel {
Point point1 = null, point2 = null;
Line line = null;
public LinePanel() {
LineListener listener = new LineListener();
addMouseListener(listener);
addMouseMotionListener(listener);
setLayout(null);
setBackground(Color.white);
setPreferredSize(new Dimension(400, 400));
}
public void paintComponent(Graphics page) {
super.paintComponent(page);
page.setColor(Color.BLACK);
}
private class LineListener extends MouseAdapter {
/***
* It feels like this is where the problem is occuring - it works fine when I don't click on another Line
* component.
* @param e - event.
*/
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
point1 = e.getPoint();
if ((e.getSource() instanceof Line)) {
Line lineComponent = (Line)e.getSource();
Point componentPoint = lineComponent.point2;
Point newStart = new Point(point1.x + componentPoint.x, point1.y + componentPoint.y);
line = new Line(newStart, newStart);
line.addMouseListener(new LineListener());
line.addMouseMotionListener(new LineListener());
add(line);
}
else {
line = new Line(point1, point1);
line.addMouseListener(new LineListener());
line.addMouseMotionListener(new LineListener());
add(line);
}
}
@Override
public void mouseDragged(MouseEvent e) {
super.mouseDragged(e);
point2 = e.getPoint();
line.setPoint2(point2);
if ((point2.x - point1.x) >= 0 && (point2.y - point1.y) >= 0) {
line.setBounds(point1.x, point1.y, point2.x - point1.x, point2.y - point1.y);
} else if ((point2.x - point1.x) < 0 && (point2.y - point1.y) < 0) {
line.setBounds(point2.x, point2.y, point1.x - point2.x, point1.y - point2.y);
} else if ((point2.x - point1.x) < 0) {
line.setBounds(point2.x, point1.y, point1.x - point2.x, point2.y - point1.y);
} else {
line.setBounds(point1.x, point2.y, point2.x - point1.x, point1.y - point2.y);
}
repaint();
}
}
}
/***
* Line class - I've created a border so that I can see the Bounded Rectangle.
*/
class Line extends JComponent {
Point point1 = null, point2 = null;
public Line(Point point, Point point2) {
point1 = point;
this.point2 = point2;
setBorder(BorderFactory.createEtchedBorder());
}
public void setPoint2(Point point) {
point2 = point;
}
/***
* The below logic is so that the line originates from the origin (where the mouse press occurs).
* I feel like there must be a better way to do this.
* @param page
*/
public void paintComponent(Graphics page) {
super.paintComponent(page);
page.setColor(Color.black);
if (point2.x >= point1.x && point2.y >= point1.y) {
page.drawLine(0,0, point2.x - point1.x,point2.y - point1.y);
} else if ((point2.x - point1.x) < 0 && (point2.y - point1.y) < 0){
page.drawLine(0,0, point1.x - point2.x, point1.y - point2.y);
} else if (point2.x < point1.x){
page.drawLine(0, point2.y - point1.y, point1.x - point2.x, 0);
} else {
page.drawLine(point2.x - point1.x, 0, 0, point1.y - point2.y);
}
}
}
It feels like this is where the problem is occuring - it works fine when I don't click on another Line component.
正确。鼠标点是相对于您单击的组件生成的,因为您正在将鼠标侦听器添加到所有组件。
因此,如果组件是 Line
的一个实例,我猜想您需要将鼠标点转换为相对于您的 LinePanel
,这将是Line
个组件。
您可以使用SwingUtiltities.convertPoint(...)
方法进行转换。
并且您可以使用 Container.getParent()
方法获取对您的 LinePanel
我继续创建了以下 GUI。
“诀窍”是创建一个应用程序模型来保存线段。
我创建了一个 LineSegment
plain Java getter / setter class 来保存构成一行的两个 java.awt.Point
实例分割。这个class以后可以扩充来保存线条颜色和线条粗细。
我创建了一个 LineSegments
plain java getter/setter class 来保存一个临时线段和一个 java.util.List
线段。
应用程序模型由一个或多个普通Java getter / setter classes.
视图是 JFrame
和绘图 JPanel
。绘图 JPanel
绘制了应用程序模型中的任何内容。当你重画一幅画 JPanel
时,你必须 重画一切 。 “内存”在应用程序模型中,而不是在视图中。
图片中看不到,但在拖动鼠标时GUI会画一条红线。
这是完整的可运行代码。我把所有 classes 都放在 classes 里面,所以我可以 post 它们作为一个块。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LineDrawingGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LineDrawingGUI());
}
private final DrawingPanel drawingPanel;
private final LineSegments lineSegments;
public LineDrawingGUI() {
this.lineSegments = new LineSegments();
this.drawingPanel = new DrawingPanel(this, lineSegments);
}
@Override
public void run() {
JFrame frame = new JFrame("Line Drawing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public void repaint() {
drawingPanel.repaint();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private LineSegments lineSegments;
public DrawingPanel(LineDrawingGUI lineDrawingGUI,
LineSegments lineSegments) {
this.lineSegments = lineSegments;
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(400, 400));
LineListener listener = new LineListener(lineDrawingGUI, lineSegments);
this.addMouseListener(listener);
this.addMouseMotionListener(listener);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
LineSegment lineSegment = lineSegments.getTemporaryLineSegment();
if (lineSegment != null) {
g.setColor(Color.RED);
drawLine(g, lineSegment);
}
g.setColor(Color.BLACK);
for (LineSegment segment : lineSegments.getLineSegments()) {
drawLine(g, segment);
}
}
private void drawLine(Graphics g, LineSegment lineSegment) {
Point startPosition = lineSegment.getStartPosition();
Point endPosition = lineSegment.getEndPosition();
g.drawLine(startPosition.x, startPosition.y,
endPosition.x, endPosition.y);
}
}
public class LineListener extends MouseAdapter {
private LineDrawingGUI lineDrawingGUI;
private LineSegments lineSegments;
private Point startPosition;
public LineListener(LineDrawingGUI lineDrawingGUI,
LineSegments lineSegments) {
this.lineDrawingGUI = lineDrawingGUI;
this.lineSegments = lineSegments;
}
@Override
public void mousePressed(MouseEvent event) {
this.startPosition = event.getPoint();
}
@Override
public void mouseReleased(MouseEvent event) {
lineSegments.setTemporaryLineSegment(null);
LineSegment lineSegment = new LineSegment();
lineSegment.setStartPosition(startPosition);
lineSegment.setEndPosition(event.getPoint());
lineSegments.addLineSegment(lineSegment);
lineDrawingGUI.repaint();
}
@Override
public void mouseDragged(MouseEvent event) {
LineSegment lineSegment = new LineSegment();
lineSegment.setStartPosition(startPosition);
lineSegment.setEndPosition(event.getPoint());
lineSegments.setTemporaryLineSegment(lineSegment);
lineDrawingGUI.repaint();
}
}
public class LineSegments {
private LineSegment temporaryLineSegment;
private final List<LineSegment> lineSegments;
public LineSegments() {
this.lineSegments = new ArrayList<>();
}
public LineSegment getTemporaryLineSegment() {
return temporaryLineSegment;
}
public void setTemporaryLineSegment(LineSegment temporaryLineSegment) {
this.temporaryLineSegment = temporaryLineSegment;
}
public void addLineSegment(LineSegment lineSegment) {
this.lineSegments.add(lineSegment);
}
public List<LineSegment> getLineSegments() {
return lineSegments;
}
}
public class LineSegment {
private Point startPosition;
private Point endPosition;
public Point getStartPosition() {
return startPosition;
}
public void setStartPosition(Point startPosition) {
this.startPosition = startPosition;
}
public Point getEndPosition() {
return endPosition;
}
public void setEndPosition(Point endPosition) {
this.endPosition = endPosition;
}
}
}