将组件绘制到BufferedImage会导致显示损坏

Ada*_*ski 7 java swing bufferedimage awt java-2d

我正在使用这里JScrollNavigator描述的组件,以便为我嵌入的大型"画布式"CAD组件提供导航窗口.JScrollPane

我已经尝试调整JScrollNavigator绘制画布的缩略图以向用户提供一些额外的上下文.但是,执行此操作会导致应用程序主框架的呈现损坏.具体来说,它是调用paint(Graphics)视口组件(即我的主画布)的动作,传入由此Graphics创建的对象BufferedImage导致后续显示损坏; 如果我评论这一行,一切正常.

以下是JScrollNavigator被覆盖的paintComponent方法:

@Override
protected void paintComponent(Graphics g) {
    Component view = jScrollPane.getViewport().getView();
    BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = img.createGraphics();

    // Paint JScrollPane view to off-screen image and then scale.
    // It is this action that causes the display corruption!
    view.paint(g2d);
    g2d.drawImage(img, 0, 0, null);
    Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);

    super.paintComponent(g);
    g.drawImage(scaled, 0, 0, null);
}
Run Code Online (Sandbox Code Playgroud)

有没有人对腐败的原因有任何建议?我原本以为绘画到幕外图像应该对现有的绘画操作没有影响.

编辑

提供一些额外的细节:在JScrollNavigatora的左侧形成一个子面板JSplitPane.在JScrollPane与导航相关的是在右边."损坏"导致拆分器不再被渲染,滚动条不可见(它们显示为白色).如果我调整大小JFrame,该JMenu部分也会变白.如果我尝试使用导航器或与滚动条交互,它们将变为可见,但分离器仍为白色.就好像各种组件的不透明设置已经受到视口视图渲染到屏幕外图像的影响.

此外,如果我使JScrollNavigator一个完全独立的出现JDialog,一切正常.

编辑2

我可以通过执行以下操作一致地重现问题:

添加JMenuBarmFrame:

JMenuBar bar = new JMenuBar();
bar.add(new JMenu("File"));
mFrame.setJMenuBar(bar);
Run Code Online (Sandbox Code Playgroud)

在替换main()方法中JScrollNavigator:

jsp.setViewportView(textArea);
Run Code Online (Sandbox Code Playgroud)

......用:

jsp.setViewportView(new JPanel() {
  {
    setBackground(Color.GREEN);
    setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
  }
});
Run Code Online (Sandbox Code Playgroud)

确保将其JScrollNavigator作为面板嵌入mFrame,而不是单独显示JDialog:

mFrame.add(jsp, BorderLayout.CENTER);
mFrame.add(nav, BorderLayout.NORTH);
Run Code Online (Sandbox Code Playgroud)

现在,当应用程序运行时,JMenuBar 不再可见 ; 绘制视图(即JPanel带有黑色粗边框的绿色)Graphics2D的行为BufferedImage.createGraphics()实际上似乎是在屏幕上渲染它,可能是从JFrame的左上角渲染,从而遮挡了其他组件.这似乎只如果发生JPanel被用作口的视图,而不是另一种组分如JTextArea,JTable

编辑3

看起来这个人有同样的问题(虽然没有发布解决方案):http://www.javaworld.com/community/node/2894/

编辑4

以下是导致编辑2中描述的可重现错误的方法mainpaintComponent方法:

public static void main(String[] args) {
    JScrollPane jsp = new JScrollPane();
    jsp.setViewportView(new JPanel() {
        {
            setBackground(Color.GREEN);
            setBorder(BorderFactory.createLineBorder(Color.BLACK, 5));
        }
    });

    JScrollNavigator nav = new JScrollNavigator();
    nav.setJScrollPane(jsp);

    JFrame mFrame = new JFrame();

    JMenuBar bar = new JMenuBar();
    bar.add(new JMenu("File"));
    mFrame.setJMenuBar(bar);

    mFrame.setTitle("JScrollNavigator Test");

    mFrame.setSize(800, 600);

    mFrame.setLayout(new GridLayout(1, 2));

    mFrame.add(jsp);
    mFrame.add(nav);
    Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
    mFrame.setLocation((screenDim.width - mFrame.getSize().width) / 2, (screenDim.height - mFrame.getSize().height) / 2);

    mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mFrame.setVisible(true);
}

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    Component view = jScrollPane.getViewport().getView();

    if (img == null) {
        GraphicsConfiguration gfConf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    }

    Graphics2D g2d = img.createGraphics();
    view.paint(g2d);

    Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0);

    g.drawImage(scaled, 0, 0, null);
}
Run Code Online (Sandbox Code Playgroud)

编辑5

似乎其他人在重新创建确切问题时遇到了麻烦.我会请人们运行粘贴在这里的代码.当我第一次运行此示例时,我看到以下内容:

腐败的形象1

JScrollNavigator或JMenuBar都没有被绘制过; 这些框架区域是透明的.

调整大小后,我看到以下内容:

腐败的形象2

JMenuBar仍未画和它看来,JPanel在(0,0)呈现一些点(这里JMenuBar应该是).view.paint内在的呼唤paintComponent是这个的直接原因.

tra*_*god 9

简介:原始JScrollNavigator函数使用Swing opacity属性NavBox在相邻组件的缩放缩略图上呈现方便的绿色JScrollPane.因为它扩展了JPanel,(共享)UI委托使用opacity与可滚动组件的冲突.在上面的编辑5中看到的图像代表了相关的渲染伪像,这里也示出.解决方案是让NavBox,JScrollNavigator并且可滚动组件扩展JComponent,如下面的第二个附录所示.然后,每个组件都可以单独管理它自己的属性.

在此输入图像描述

我发现在我的平台Mac OS X,Java 1.6上发布代码没有异常的渲染工件.对不起,我没有看到任何明显的可移植性违规行为.

图像一

一些可能不相关但可能有用的观察结果.

附录:正如这里所建议的那样,我使用了更灵活的结果,RenderingHints而不是getScaledInstance()如下所示.添加一些图标可以更轻松地查看对图像和文本的不同影响.

图像二

editPane.insertIcon(UIManager.getIcon("OptionPane.errorIcon"));
editPane.insertIcon(UIManager.getIcon("OptionPane.warningIcon"));
...
@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Component view = jScrollPane.getViewport().getView();
    BufferedImage img = new BufferedImage(view.getWidth(),
        view.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D off = img.createGraphics();
    off.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    off.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
        RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    view.paint(off);
    Graphics2D on = (Graphics2D)g;
    on.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    on.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
        RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    on.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}
Run Code Online (Sandbox Code Playgroud)

附录secundum:看起来JPanelUI代表不合作.一种解决方法是扩展,JComponent以便您可以控制不透明度.管理它只需要稍微多一点的工作backgroundColor.NavBox并且JScrollNavigator也是类似治疗的候选人.

在此输入图像描述

jsp.setViewportView(new JComponent() {

    {
        setBackground(Color.red);
        setBorder(BorderFactory.createLineBorder(Color.BLACK, 16));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(300, 300);
    }
});
Run Code Online (Sandbox Code Playgroud)