首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >绘制所有其他组件(Swing,Java)

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

Stack Overflow用户
提问于 2011-11-24 17:11:33
回答 4查看 10.3K关注 0票数 3

在我的应用程序中,我需要像Photoshop那样绘制网格线-例如,用户可以将线条拖到文档上以帮助他对齐层。现在,问题是我能够画出这样的线(这只是简单的使用Java2D绘制Line2D),但是我不能把这样的线放在所有其他的东西上面,因为当子组件自己画的时候,我的网格线就会被擦除。

程序结构是这样的: JFrame -> JPanel -> JScrollPane -> JPanel ->许多其他的JPanels,它们类似于层

作为一个测试,我将抽签代码添加到JFrame中,它正确地显示了我的Line2D实例。但是,当我在子组件中执行任何要求该子组件重新绘制自身的操作时,JFrame中绘制的线将被擦除。

我知道这是预期的摇摆行为--也就是说,它只会重新绘制那些已经改变的区域。然而,我正在寻找一些方法,不断绘制线网格线之上的一切。

我能够让它工作的唯一方法是使用一个Swing计时器,它每10 my在我的根组件上调用重新绘制(),但是它消耗了大量的CPU。

更新

示例的工作代码如下所示。请注意,在我的实际应用程序中,我有几十个不同的组件可以触发一个重新绘制(),而且它们都没有对绘制网格线的组件的引用(当然,我可以将它传递给每个人,但这似乎是最新的选项)。

代码语言:javascript
复制
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();
    }
}
EN

回答 4

Stack Overflow用户

发布于 2011-11-24 19:33:26

一个可能的解决方案是重写JPanel的repaint方法,以便它调用contentPane的重新绘制方法。另一点是,您可能不应该在JFrame中直接绘制网格线,而应该在它的contentPane中绘制网格线。与我通常推荐的方法相反,我认为最好是重写contentPane的can方法(或者其他包含JPanel的方法),而不是它的paintComponent方法,以便在绘制完子元素之后调用它。例如:

代码语言:javascript
复制
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();
         }
      });
   }
}
票数 4
EN

Stack Overflow用户

发布于 2011-11-24 17:16:20

假设父框架已经有了它必须绘制的所有网格线的列表,那么您可以做的就是让每个子帧绘制自己的个人行。伪码:

代码语言:javascript
复制
gridlines = getParentsGridLines()
gridlines.offsetBasedOnRelativePosition()
drawStuff()
票数 1
EN

Stack Overflow用户

发布于 2012-02-17 18:26:51

Swing在JLayeredPane (和类似的组件)中使用JFrames。使用分层窗格,您可以将只使用油漆的组件定位到您的主要内容上。

这段代码使用放置在JLayeredPane中的组件将任意装饰放置在任何组件的主要内容之上(并自动重新绘制),从而避免了重写任何给定组件的paint()方法的需要。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8260567

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档