繁体   English   中英

绘制所有其他组件(Swing,Java)

[英]Paint over all other components (Swing, Java)

在我的应用程序中,我需要像Photoshop那样绘制网格线 - 例如,用户可以在文档上拖动线条以帮助他对齐图层。 现在,问题在于我能够绘制这样的线条(它只是简单的使用Line2D的简单Java2D绘画),但我无法将这些线条放在其他所有内容之上,因为当子组件绘制自己时,我的网格线被删除了。

程序结构是这样的:JFrame - > JPanel - > JScrollPane - > JPanel - > [很多其他JPanels,就像层一样]

作为测试,我将绘制代码添加到JFrame,它正确显示我的Line2D实例在其他所有内容之上。 但是,当我在子组件中执行任何需要该子项重新绘制的内容时,JFrame中绘制的线将被删除。

我知道这是预期的Swing行为 - 也就是说,它只会重绘那些已经改变的区域。 但是,我正在寻找一些方法,不断在其他所有方面绘制线网格线。

我能够让它工作的唯一方法是使用一个Swing Timer,它每隔10ms在我的根组件上调用repaint(),但它消耗了大量的CPU。

UPDATE
下面是一个例子的工作代码。 请注意,在我的实际应用程序中,我有许多可以触发重绘()的不同组件,并且没有一个组件引用了执行网格线绘制的组件(当然我可以将它传递给每个人,但是成为最新的选择)

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class GridTest extends JFrame {
    public static void main(String[] args) {
        new GridTest().run();
    }

    private void run() {
        setLayout(null);
        setPreferredSize(new Dimension(200, 200));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JPanel p = new JPanel();
        p.setBounds(20, 20, 100, 100);
        p.setBackground(Color.white);
        add(p);

        JButton b = new JButton("Refresh");
        b.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // When I call repaint() here, the paint() method of
                // JFrame it's not called, thus resulting in part of the
                // red line to be erased / overridden.

                // In my real application application, I don't have
                // easy access to the component that draws the lines
                p.repaint();
            }
        });
        b.setBounds(0, 150, 100, 30);
        add(b);

        pack();
        setVisible(true);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);

        Graphics2D gg = (Graphics2D)g.create();
        Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
        gg.setStroke(new BasicStroke(3));
        gg.setColor(Color.red);
        gg.draw(line);
        gg.dispose();
    }
}

如果你想绘制放置到JScrollPane JComponents然后你可以绘制到JViewPort ,例如这里

编辑:

1)将您的代码绘制到错误的容器上,对于JFrame ,确定可以绘制到JFrame ,但是您必须提取RootPane或GlassPane

2)你必须学习如何使用LayoutManagers ,我让你的代码用原始的大小调整,不好看也很糟糕

3)绘制到GlassPaneJViewPort

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import javax.swing.*;

public class GridTest extends JFrame {
    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        new GridTest().run();
    }

    private void run() {
        setLayout(null);
        setPreferredSize(new Dimension(200, 200));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JPanel p = new JPanel() {
            private static final long serialVersionUID = 1L;

            @Override
            public void paint(Graphics g) {

                super.paint(g);
                Graphics2D gg = (Graphics2D) g.create();
                Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
                gg.setStroke(new BasicStroke(3));
                gg.setColor(Color.red);
                gg.draw(line);
                //gg.dispose();

            }
        };
        p.setBounds(20, 20, 100, 100);
        p.setBackground(Color.white);
        add(p);

        JButton b = new JButton("Refresh");
        b.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                p.repaint();
            }
        });
        b.setBounds(0, 150, 100, 30);
        add(b);

        pack();
        setVisible(true);
    }
}

编辑:2,如果你期望单行,在固定的界限上

在此输入图像描述在此输入图像描述在此输入图像描述

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import javax.swing.*;
import javax.swing.border.LineBorder;

public class GridTest extends JFrame {
    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        new GridTest().run();
    }

    private void run() {
        setPreferredSize(new Dimension(200, 200));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JPanel p = new JPanel() {
            private static final long serialVersionUID = 1L;

            @Override
            public void paint(Graphics g) {
                super.paint(g);
                Graphics2D gg = (Graphics2D) g.create();
                Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
                gg.setStroke(new BasicStroke(3));
                gg.setColor(Color.red);
                gg.draw(line);
                gg.dispose();
            }
        };
        JPanel p1 = new JPanel();
        p1.setBorder(new LineBorder(Color.black,1));
        JPanel p2 = new JPanel();
        p2.setBorder(new LineBorder(Color.black,1));
        JPanel p3 = new JPanel();
        p3.setBorder(new LineBorder(Color.black,1));
        p.setLayout(new GridLayout(3,0));
        p.add(p1);
        p.add(p2);
        p.add(p3);
        p.setBounds(20, 20, 100, 100);
        p.setBackground(Color.white);
        add(p, BorderLayout.CENTER);

        JButton b = new JButton("Refresh");
        b.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                p.repaint();
            }
        });
        add(b, BorderLayout.SOUTH);

        pack();
        setVisible(true);
    }
}

一种可能的解决方案是覆盖JPanel的repaint方法,以便它调用contentPane的重绘方法。 另一点是你可能不应该直接在JFrame中绘制网格线,而是在其contentPane中绘制。 与我通常推荐的相反,我认为最好覆盖contentPane的paint方法(或其他包含JPanel的方法),而不是paintComponent方法,以便在绘制子项后调用它。 例如:

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.event.ActionEvent;

import javax.swing.*;

@SuppressWarnings("serial")
public class GridTest2 extends JPanel {
   private static final Stroke LINE_STROKE = new BasicStroke(3f);
   private boolean drawInPaintComponent = false;

   public GridTest2() {
      final JPanel panel = new JPanel() {
         @Override
         public void repaint() {
            JRootPane rootPane = SwingUtilities.getRootPane(this);
            if (rootPane != null) {
               JPanel contentPane = (JPanel) rootPane.getContentPane();
               contentPane.repaint();
            }
         }
      };
      panel.setBackground(Color.white);
      panel.setPreferredSize(new Dimension(100, 100));

      JPanel biggerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
      biggerPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 0, 0));
      biggerPanel.setOpaque(false);
      biggerPanel.add(panel);

      JButton resetButton = new JButton(new AbstractAction("Reset") {
         public void actionPerformed(ActionEvent arg0) {
            panel.repaint();
         }
      });
      JPanel btnPanel = new JPanel();
      btnPanel.add(resetButton);

      setLayout(new BorderLayout());
      add(biggerPanel, BorderLayout.CENTER);
      add(btnPanel, BorderLayout.SOUTH);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(300, 300);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (drawInPaintComponent ) {
         drawRedLine(g);
      }
   }

   @Override
   public void paint(Graphics g) {
      super.paint(g);
      if (!drawInPaintComponent ) {
         drawRedLine(g);
      }
   }

   private void drawRedLine(Graphics g) {
      Graphics2D g2 = (Graphics2D) g;
      g2.setStroke(LINE_STROKE);
      g2.setColor(Color.red);
      g2.drawLine(0, 50, getWidth(), 50);
   }

   private static void createAndShowGui() {
      GridTest2 mainPanel = new GridTest2();

      JFrame frame = new JFrame("GridTest2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

我知道这是一个老帖子,但我最近遇到了同样的问题......
您应该覆盖paintChildren而不是paintpaintComponent JComponent.paint文档

由Swing调用以绘制组件。 应用程序不应直接调用绘制,而应使用重绘方法来调度组件以进行重绘。
此方法实际上将绘制工作委托给三个受保护的方法:paintComponent,paintBorder和paintChildren。 它们按列出的顺序调用,以确保子项出现在组件本身之上 一般来说,组件及其子组件不应在分配给边框的insets区域中绘制。 子类可以像往常一样覆盖此方法。 一个只想专门化UI(外观)委托的paint方法的子类应该只覆盖paintComponent。

所以,如果你

@Override
protected void paintChildren(Graphics g){
    super.paintChildren(g);
    paintGrid(g);
}

网格将位于您的子组件^^之上

假设父框架已经有一个它必须绘制的所有网格线的列表,你可以做的是让每个子框架绘制它自己的线条的个人位。 在伪代码中:

gridlines = getParentsGridLines()
gridlines.offsetBasedOnRelativePosition()
drawStuff()

Swing在JFrames(和类似组件)中使用JLayeredPane。 使用分层窗格,您可以在主要内容上定位仅绘制颜色的组件。

此代码使用放置在JLayeredPane中的组件来定位(并自动重新绘制) 任何组件主要内容上方的任意装饰,从而无需覆盖任何给定组件的paint()方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM