在另一个 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;
        }
        
    }

}