覆盖所有其他组件(Swing、Java)
在我的应用程序中,我需要像Photoshop那样绘制网格线——例如,用户可以在文档上拖动线条来帮助他对齐图层。现在的问题是,我能够画出这样的线(这只是使用Line2D的简单Java2D绘画),但我不能将这些线放在其他一切之上,因为当子组件自己绘制时,我的网格线被擦除 程序结构是这样的:JFrame->JPanel->JScrollPane->JPanel->[许多其他类似于层的JPanel] 作为测试,我向JFrame添加了绘图代码,它正确地显示了我的Line2D实例。但是,当我在子组件中执行任何需要该子组件重新绘制自身的操作时,JFrame中绘制的线将被擦除 我知道这是预期的Swing行为——也就是说,它只会重新绘制那些已更改的区域。然而,我正在寻找一种方法,这种方法可以在所有其他方法的基础上连续绘制线网格线 我能够让它工作的唯一方法是使用一个Swing计时器,它每隔10毫秒对我的根组件调用repaint(),但它会消耗大量CPU 更新覆盖所有其他组件(Swing、Java),java,swing,drawing,java-2d,Java,Swing,Drawing,Java 2d,在我的应用程序中,我需要像Photoshop那样绘制网格线——例如,用户可以在文档上拖动线条来帮助他对齐图层。现在的问题是,我能够画出这样的线(这只是使用Line2D的简单Java2D绘画),但我不能将这些线放在其他一切之上,因为当子组件自己绘制时,我的网格线被擦除 程序结构是这样的:JFrame->JPanel->JScrollPane->JPanel->[许多其他类似于层的JPanel] 作为测试,我向JFrame添加了绘图代码,它正确地显示了我的Line2D实例。但是,当我在子组件中执行任
下面是一个示例的工作代码。请注意,在我的实际应用程序中,我有几十个不同的组件可以触发重绘(),但没有一个组件引用绘制网格线的组件(当然我可以将其传递给所有人,但这似乎是最新的选项)
如果要在放置到
JScrollPane
的JComponents
上进行绘制,则可以在JViewPort
上进行绘制,例如
编辑:
1) 由于您的代码绘制到了错误的容器,绘制到了JFrame
,因此确实可以绘制到JFrame
,但您必须提取
2) 你必须学会如何工作,我让你的代码与原来的大小,不好,非常糟糕
3) 在玻璃窗格
或JViewPort
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,如果需要单行,则在固定边界上
假设父帧已经有了它必须绘制的所有网格线的列表,那么您可以让每个子帧绘制它自己的网格线位。在伪代码中:
gridlines = getParentsGridLines()
gridlines.offsetBasedOnRelativePosition()
drawStuff()
一种可能的解决方案是重写JPanel的
repaint
方法,以便它调用contentPane的repaint方法。另一点是,您可能不应该直接在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();
}
});
}
}
Swing在JFrames(和类似组件)中使用JLayeredPane。使用“分层”窗格,可以将仅绘制组件放置在主要内容上
使用放置在JLayeredPane中的组件将任意装饰放置(并自动重新绘制)在任何组件的主要内容上方,从而避免了重写任何给定组件的paint()方法的需要 我知道这是一篇老帖子,但我最近也遇到了同样的问题……
您应该重写
paintChildren
,而不是paint
或paintComponent
。
从:
由Swing调用以绘制组件。应用程序不应直接调用paint,而应使用repaint方法来安排组件重新绘制。此方法实际上将绘制工作委托给三种受保护的方法:paintComponent、paintBorder和paintChildren按列出的顺序调用它们,以确保子项显示在组件本身的顶部。一般来说,组件及其子组件不应在分配给边界的插图区域中绘制。子类可以像往常一样重写此方法。只想专门化UI(外观)委托的绘制方法的子类应该重写paintComponent 所以如果你
@Override
protected void paintChildren(Graphics g){
super.paintChildren(g);
paintGrid(g);
}
网格将位于子组件的顶部^^为什么不在paintComponent方法的末尾而不是开始处绘制网格线?另外,为了更具体的帮助,考虑创建和发布一个显示您问题的AN。我这样做了,但问题是当子组件重新绘制自己时,父组件(在层次结构中的最远的地方,如果没有对GePARTANCE(未知)的未知调用,就不能到达它)。我将尝试获取一个工作代码来显示在这里。@Rafael Steil请查看我的edit@RafaelSteil,您在下面的评论中一直在谈论滚动窗格,但您的SSCCE没有滚动窗格,因此它如何准确反映您试图解决的问题?我尝试过,但(至少在我的测试中),当滚动窗格的子组件重新绘制自身时,不会调用视口的stageChanged(属于ChangeListener)(仅当我执行其他操作时,如使用滚动条)。如果我的子组件固定在它们的位置上,它会工作,但问题是用户可能会将它们拖到绘图上area@RafaelSteil这个理论性的问题,请以演示您的问题的形式发布代码,我创建了一个非常小且简单的例子来说明这个问题。我理解布局管理器和其他一切是如何工作的,你不能要求SSCCE并期望它遵守所有的最佳实践和模式,而这些实践和模式根本无法解释问题。无论如何,我在这里找到了我的解决方案,在这里我绘制到滚动窗格,并将它的引用传递给触发重绘事件的任何人。虽然没有我想要的那么圆滑,但效果不错。(请不要误会我的意思,我感谢您的帮助。这里的讨论帮助我找到了一个替代解决方案。):-)等一下,我在JPanel上放置了一些带有红线的JComponents
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();
}
});
}
}
@Override
protected void paintChildren(Graphics g){
super.paintChildren(g);
paintGrid(g);
}