Swing 绘制JPanel时java.awt.Window.access$700(Window.java:132)处的NullPointerException
我正在尝试在Swing 绘制JPanel时java.awt.Window.access$700(Window.java:132)处的NullPointerException,swing,java-2d,Swing,Java 2d,我正在尝试在paint(Graphics)方法JPanel中绘制组件。 下面的代码片段工作正常,在myJPanel中很好地绘制了JButton: @Override public void paint(Graphics g) { super.paint(g); JButton btn = new JButton("hello"); Dimension dim = btn.getPreferre
paint(Graphics)
方法JPanel
中绘制组件。
下面的代码片段工作正常,在myJPanel
中很好地绘制了JButton
:
@Override
public void paint(Graphics g) {
super.paint(g);
JButton btn = new JButton("hello");
Dimension dim = btn.getPreferredSize();
btn.setSize(dim.width, dim.height);
btn.paint(g); // paint the button
}
除了JPanel
,该代码片段还适用于其他组件(JLabel
,JTree
,…)。
下面的代码将在java.awt.Window.access$700(Window.java:132)处导致非常奇怪的NullPointerException
这里是完整的stacktrace:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at java.awt.Window.access$700(Window.java:132)
at java.awt.Window$1.isOpaque(Window.java:3458)
at javax.swing.RepaintManager.getVolatileOffscreenBuffer(RepaintManager.java:983)
at javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1395)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:294)
at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
at javax.swing.JComponent.paint(JComponent.java:1015)
at test.paintcontainer.TestPaintContainerMain$TestContentPane.paint(TestPaintContainerMain.java:48)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paint(JComponent.java:1038)
at javax.swing.JLayeredPane.paint(JLayeredPane.java:567)
at javax.swing.JComponent.paintChildren(JComponent.java:862)
at javax.swing.JComponent.paintToOffscreen(JComponent.java:5131)
at javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:278)
at javax.swing.RepaintManager.paint(RepaintManager.java:1224)
at javax.swing.JComponent.paint(JComponent.java:1015)
at java.awt.GraphicsCallback$PaintCallback.run(GraphicsCallback.java:21)
at sun.awt.SunGraphicsCallback.runOneComponent(SunGraphicsCallback.java:60)
at sun.awt.SunGraphicsCallback.runComponents(SunGraphicsCallback.java:97)
at java.awt.Container.paint(Container.java:1780)
at java.awt.Window.paint(Window.java:3375)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:796)
at javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:713)
at javax.swing.RepaintManager.seqPaintDirtyRegions(RepaintManager.java:693)
at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(SystemEventQueueUtilities.java:125)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:641)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:602)
at java.awt.EventQueue$1.run(EventQueue.java:600)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:611)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
你知道怎么解决这个问题吗?我需要绘制JPanel
内部绘制(图形)
方法。
我编写了一个简单的测试应用程序,您可以复制粘贴以复制上述异常:
package test.paintcontainer;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class TestPaintContainerMain extends JFrame {
public static void main(String[] args) {
TestPaintContainerMain test = new TestPaintContainerMain();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setBounds(0, 0, 300, 200);
test.setContentPane(new TestContentPane());
test.setVisible(true);
}
static class TestContentPane extends JPanel {
JRadioButton paintButtonCheck;
JRadioButton paintPanelCheck;
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", true);
paintPanelCheck = createRadioButton("paint panel", false);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
createButton().paint(g);
} else {
createPanel().paint(g);
}
}
private JButton createButton() {
JButton button = new JButton("button");
button.setSize(button.getPreferredSize().width, button.getPreferredSize().height);
return button;
}
private JPanel createPanel() {
JPanel panel = new JPanel();
panel.setBackground(Color.GREEN);
panel.add(createButton());
panel.setSize(panel.getPreferredSize().width, panel.getPreferredSize().height);
return panel;
}
private JRadioButton createRadioButton(String title, boolean selected) {
JRadioButton radio = new JRadioButton(title, selected);
radio.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
TestContentPane.this.repaint();
}
});
return radio;
}
}
}
我刚刚找到了解决办法。
我的问题中对示例代码的唯一修改是我试图绘制的JPanel上的panel.setDoubleBuffered(false)
但是,我仍然认为这个骗局是个大错误。如果双缓冲应该通过设计关闭,那么您不应该得到NullPointerException
,而应该得到一些其他更有意义的异常来解释这种情况
下面是一个固定的示例应用程序:
package test.paintcontainer;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class TestPaintContainerMain extends JFrame {
public static void main(String[] args) {
TestPaintContainerMain test = new TestPaintContainerMain();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setBounds(0, 0, 300, 200);
test.setContentPane(new TestContentPane());
test.setVisible(true);
}
static class TestContentPane extends JPanel {
JRadioButton paintButtonCheck;
JRadioButton paintPanelCheck;
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", false);
paintPanelCheck = createRadioButton("paint panel", true);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
createButton().paint(g);
} else {
createPanel().paint(g);
}
}
private JButton createButton() {
JButton button = new JButton("button");
button.setSize(button.getPreferredSize().width, button.getPreferredSize().height);
return button;
}
private JPanel createPanel() {
JPanel panel = new JPanel();
panel.setBackground(Color.GREEN);
panel.add(createButton());
// --------------------------------
panel.setDoubleBuffered(false); // <-- TURN OFF DOUBLE BUFFERING
// --------------------------------
panel.setSize(panel.getPreferredSize().width, panel.getPreferredSize().height);
return panel;
}
private JRadioButton createRadioButton(String title, boolean selected) {
JRadioButton radio = new JRadioButton(title, selected);
radio.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
TestContentPane.this.repaint();
}
});
return radio;
}
}
}
package test.paintcontainer;
导入java.awt.Color;
导入java.awt.Graphics;
导入java.awt.event.ActionEvent;
导入java.awt.event.ActionListener;
导入javax.swing.ButtonGroup;
导入javax.swing.JButton;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JRadioButton;
公共类TestPaintContainerMain扩展了JFrame{
公共静态void main(字符串[]args){
TestPaintContainerMain test=新的TestPaintContainerMain();
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
测试立根(0,0,300,200);
setContentPane(新的TestContentPane());
test.setVisible(真);
}
静态类TestContentPane扩展了JPanel{
JRadioButton油漆钮扣;
JRadioButton paintPanelCheck;
公共TestContentPane(){
paintButtonCheck=createRadioButton(“绘制按钮”,false);
paintPanelCheck=createRadioButton(“绘制面板”,真);
ButtonGroup ButtonGroup=新建ButtonGroup();
buttonGroup.add(paintButtonCheck);
按钮组。添加(paintPanelCheck);
添加(油漆钮扣);
添加(paintPanelCheck);
}
@凌驾
公共空间涂料(图g){
超级油漆(g);
g、 翻译(100100);
if(paintButtonCheck.isSelected()){
createButton().paint(g);
}否则{
createPanel().paint(g);
}
}
私有JButton createButton(){
JButton按钮=新JButton(“按钮”);
button.setSize(button.getPreferredSize().width,button.getPreferredSize().height);
返回按钮;
}
私有JPanel createPanel(){
JPanel面板=新的JPanel();
面板.立根背景(颜色.绿色);
panel.add(createButton());
// --------------------------------
panel.setDoubleBuffered(假);//这很可能不是Swing中的一个bug,而是一个更大的问题,因为您正在尝试绘制一个尚未实现的组件,这意味着它没有活动的图形上下文。您可以通过将组件添加到已经实现的组件(如JFrame)中来实现该组件,而JFrame本身是通过setVisible(true)实现的
此外,您可能永远也不应该手动调用JComponent.paint(Graphics)
,因为这是Swing(更准确地说是事件调度程序线程)的工作-它甚至在paint
方法的文档中这样说:
应用程序不应直接调用paint,而应使用repaint方法来安排组件重新绘制
您可以调用的是方法printAll(Graphics g)
,它绘制组件及其所有子组件。同样,在Swing中,也不应该覆盖paint
,而应该覆盖paintComponent
下面是一个测试代码:
JButton button = createButton();
JPanel panel = createPanel();
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", true);
paintPanelCheck = createRadioButton("paint panel", false);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
//Hack, just prove something (realize both components)
add(panel);
add(button);
}
...
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
button.paintAll(g);
} else {
panel.paintAll(g);
}
g.translate(-100, -100);
}
这应该是可行的(尽管屏幕上显然有两个您不想要的组件)。还要注意“重置”图形对象,因为Swing之后仍然会使用它
这是理论,但还不是实际的解决方案
我对你问题的解决办法是:“不要这样做”
组件与图像不同,因为它们在任何地方看起来都不一样。paintAll
调用的输出将不同,具体取决于组件的实现方式(或位置)
因此,一个建议是显示实际组件。创建工具提示框,添加面板和按钮,让它们自己绘制。您甚至可以对这些组件进行子类化,并覆盖它们的paintComponent()
方法,添加透明度等等。这需要一些工作,但Swing从来都不容易。绘制按钮/面板/…而不仅仅是添加它背后的想法是什么?我没有测试它,但这不会导致绘制的按钮无法单击,因为它们不是Swing层次结构的一部分吗?想法:绘制工具提示中JPanel的“预览”,带有一些淡出和其他效果。谢谢你的回答。我会试试你的建议。
JButton button = createButton();
JPanel panel = createPanel();
public TestContentPane() {
paintButtonCheck = createRadioButton("paint button", true);
paintPanelCheck = createRadioButton("paint panel", false);
ButtonGroup buttonGroup = new ButtonGroup();
buttonGroup.add(paintButtonCheck);
buttonGroup.add(paintPanelCheck);
add(paintButtonCheck);
add(paintPanelCheck);
//Hack, just prove something (realize both components)
add(panel);
add(button);
}
...
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.translate(100, 100);
if (paintButtonCheck.isSelected()) {
button.paintAll(g);
} else {
panel.paintAll(g);
}
g.translate(-100, -100);
}