【问题标题】:Custom JComponent (A Line) Doesn't Show Up On JPanel自定义 JComponent(一行)不显示在 JPanel 上
【发布时间】:2020-06-01 09:05:00
【问题描述】:

它在jframe上显示没有jpanel的行,但是当我将它添加到jpanel时它没有。我尝试将 jpanel 的布局管理器设置为 null 但没有结果。我想使用 JComponents 来绘制线条,因为我希望它们可以点击。

Main.java 文件:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

class Main {
  public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
    frame.setSize(500, 500);

    //Parent Panel
    JPanel panel = new JPanel();
    panel.setBackground(Color.YELLOW);
    panel.setLayout(null);

    //Add Line To Panel
    Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));

    panel.add(line);
    panel.repaint();

    frame.add(panel);
    frame.setVisible(true);
  }
}

class Line extends JComponent {

   private final Point2D start, end;

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.BLUE);
        g2.setStroke(new BasicStroke(2.0F));
        g2.draw(new Line2D.Double(start,end));
    }

    public Line( Point2D start, Point2D end){
        this.start = start;
        this.end = end;
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("mouse clicked");
            }
        });
    }
}

【问题讨论】:

    标签: java swing user-interface java-2d


    【解决方案1】:

    它在jframe上显示没有jpanel的行,但是当我将它添加到jpanel时它没有

    Swing 组件负责确定自己喜欢的尺寸。

    当您将组件添加到面板时,布局管理器将根据布局管理器的规则设置组件的大小/位置。

    当您将组件添加到框架时,您实际上将其添加到框架的内容窗格中,即Jpanel,默认情况下使用BorderLayout。因此,组件的大小可以填充框架中的可用空间。

    panel.setLayout(null);
    

    然后,您将该组件添加到具有空布局的面板中。现在您负责设置组件的大小/位置。如果你不知道大小是 (0, 0) 所以没有什么可以画的。

    您应该覆盖类的getPreferredSize() 方法以返回组件的首选大小。然后布局管理器就可以完成他们的工作了。

    如果你真的需要一个空布局,那么组件的大小应该在应用程序代码中设置,而不是 Line 类本身。

    但现在我的线路有一个大容器,可以监听任何点击,

    如果您想要命中检测,则覆盖 contains(...) 方法。

    以下是实现上述建议的基本示例:

    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.Line2D;
    import java.awt.geom.Point2D;
    import java.awt.Rectangle;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Line extends JComponent
    {
       private Line2D.Double line;
    
        public Line( Point2D start, Point2D end)
        {
            line = new Line2D.Double(start, end);
    
            addMouseListener(new MouseAdapter()
            {
                @Override
                public void mouseClicked(MouseEvent e)
                {
                    System.out.println("mouse clicked");
                }
            });
        }
    
        @Override
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g);
    
            Graphics2D g2 = (Graphics2D) g;
            g2.setColor( Color.BLUE );
            g2.setStroke( new BasicStroke(2.0F) );
            g2.draw( line );
        }
    
        @Override
        public Dimension getPreferredSize()
        {
            Rectangle bounds = line.getBounds();
    
            int width = bounds.x + bounds.width;
            int height = bounds.y + bounds.height;
    
            return new Dimension(width, height);
        }
    
        @Override
        public boolean contains(int x, int y)
        {
            double distance = line.ptSegDist( new Point2D.Double(x, y) );
    
            return distance < 2;
        }
    
        public static void main(String[] args)
        {
            JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
            frame.setSize(500, 500);
    
            //Parent Panel
            JPanel panel = new JPanel();
            panel.setBackground(Color.YELLOW);
    
            //Add Line To Panel
            Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
    
            panel.add(line);
            panel.repaint();
    
            frame.add(panel);
            frame.setVisible(true);
        }
    }
    

    【讨论】:

    • 这很有帮助。所以contains() 方法就是我要找的东西。虽然组件的大小仍然很大,但没关系吧?
    • "如果你真的需要一个空布局,那么组件的大小应该在应用程序代码中设置,而不是Line类本身。" 请问一下该怎么做?
    • 在您的应用程序代码中,当您创建 Line 实例时,您将调用 line.setSize( line.getPreferredSize() )
    【解决方案2】:

    在 Line 构造函数中添加自定义大小。

    public Line( Point2D start, Point2D end){ ...
    this.setSize(200, 200); }

    更新后也适合painted Graph

    建议从JComponent 更改为JPanel 以查看background

    import java.awt.BasicStroke;
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Rectangle;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.Line2D;
    import java.awt.geom.Point2D;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    class Main {
      public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
        frame.setSize(500, 500);
    
        //Parent Panel
        JPanel panel = new JPanel();
        panel.setSize(300,300);
        frame.add(panel);
        panel.setBackground(Color.YELLOW);
        panel.setLayout(null);
    
        //Add Line To Panel
        Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
        panel.add(line);
    
        frame.setVisible(true);
      }
    }
    
    class Line extends JPanel {
    
       private final Point2D start, end;
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setBackground(Color.RED);
            g2.setColor(Color.BLUE);
            g2.setStroke(new BasicStroke(2.0F));
            g2.draw(new Line2D.Double(start,end));
            Rectangle r = g2.getClipBounds();
            System.out.println(r.x+":"+r.y);
        }
    
        public Line( Point2D start, Point2D end){
    
            this.start = start;
            this.end = end;
            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    System.out.println("mouse clicked at "+e.getX()+":"+e.getY());
    
                }
            });
            int max_x = (int) Math.max(start.getX(), end.getX());
            int max_y = (int) Math.max(start.getY(), end.getY());
            System.out.println("max x="+max_y+",y="+max_y);
            setSize(max_x,max_y);
            setVisible(true);
            setBackground(Color.GREEN);
        } 
    }
    

    注意:只允许 inside_green 点击!

    【讨论】:

    • 组件不应设置自身的 size()。组件应仅确定其首选大小。然后布局管理器将使用此信息来设置组件的大小/位置。如果使用空布局,则应用程序将确定组件的大小/位置。
    • @TraianGEICU 我明白了。尽管如此,我们还是指出了一个错误。由于这段代码应该可以帮助许多用户,因此指出代码中的错误很重要。
    • @TraianGEICU 很抱歉,不是刻薄,但您的代码还有另一个问题:它在构造函数中调用非最终方法(例如,行中的 setSize)。这是一个巨大的危险信号,表示运行它时可能会出现的模糊问题。 docs.oracle.com/javase/tutorial/java/IandI/final.html
    • @Modus Tollens ,请注意,很好指出! ......我的跑步很好......但很高兴知道!
    • 当然,解决问题的方法总是不止一种。然而,正如 ModusTollens 所指出的,本论坛的答案旨在鼓励最佳实践。最佳实践的存在是有原因的。它们现在可能不明显,但可以防止将来出现潜在问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多