在我的应用程序中,我需要像Photoshop那样绘制网格线 - 例如,用户可以在文档上拖动线条以帮助他对齐图层。现在,问题在于我能够绘制这样的线条(它只是简单的使用Line2D的简单Java2D绘画),但我无法将这些线条放在其他所有内容之上,因为当子组件绘制自己时,我的网格线被删除了。
程序结构是这样的:JFrame - > JPanel - > JScrollPane - > JPanel - > [许多其他JPanels,就像层]
作为测试,我将绘制代码添加到JFrame,它正确显示我的Line2D实例在其他所有内容之上。但是,当我在子组件中执行任何需要该子项重新绘制的内容时,JFrame中绘制的线将被删除。
我知道这是预期的Swing行为 - 也就是说,它只会重绘那些已经改变的区域。但是,我正在寻找一些方法,不断在其他所有方面绘制线网格线。
我能够让它工作的唯一方法是使用Swing Timer每隔10ms在我的根组件上调用repaint(),但它消耗了大量的CPU。
更新
下面是一个例子的工作代码。请注意,在我的实际应用程序中,我有许多可以触发重绘()的不同组件,并且没有一个组件引用了执行网格线绘制的组件(当然我可以将它传递给每个人,但是是最新的选择)
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();
}
}
答案 0 :(得分:5)
如果您要在放置到JComponents
的{{1}}上绘画,那么您可以绘制到JScrollPane
,例如here
编辑:
1)将您的代码绘制到错误的容器,JViewPort
,确保可以绘制到JFrame
,但您必须提取RootPane or GlassPane
2)你必须学习LayoutManagers如何工作,我让你的代码用原始的大小调整,不是很好而且非常糟糕
3)画到JFrame
或GlassPane
JViewPort
编辑:2,如果您期望单行,则在固定边界
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);
}
}
答案 1 :(得分:4)
一种可能的解决方案是覆盖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();
}
});
}
}
答案 2 :(得分:1)
假设父框架已经有一个它必须绘制的所有网格线的列表,你可以做的是让每个子框架绘制它自己的线条的个人位。在伪代码中:
gridlines = getParentsGridLines()
gridlines.offsetBasedOnRelativePosition()
drawStuff()
答案 3 :(得分:1)
Swing在JFrames(和类似组件)中使用JLayeredPane。使用分层窗格,您可以在主要内容上定位仅绘画组件。
This code使用放置在JLayeredPane中的组件来定位(并自动重绘)任何组件主要内容之上的任意装饰,从而避免需要覆盖paint()方法任何给定的组件。
答案 4 :(得分:1)
我知道这是一个老帖子,但我最近也遇到了同样的问题......
您应该覆盖green gpworv1.html 5 1 0 0 1.1kb 575b
green rgspaa1.html 5 1 0 0 1.1kb 575b
green raeeno1.html 5 1 0 0 1.1kb 575b
green nnwrsa1.html 5 1 0 0 1.1kb 575b
green nrrvwo1.html 5 1 0 0 1.1kb 575b
而不是paintChildren
或paint
。
来自JComponent.paint documentation:
由Swing调用以绘制组件。应用程序不应直接调用绘制,而应使用重绘方法来调度组件以进行重绘 此方法实际上将绘制工作委托给三个受保护的方法:paintComponent,paintBorder和paintChildren。 按照列出的顺序调用它们以确保子项显示在组件本身之上。一般来说,组件及其子组件不应在分配给边框的insets区域中绘制。子类可以像往常一样覆盖此方法。一个只想专门化UI(外观)委托的paint方法的子类应该只覆盖paintComponent。
所以,如果你
paintComponent
网格将位于子组件的顶部^^