【问题标题】:Is it possible to paint a JComponent and all its children to another component?是否可以将 JComponent 及其所有子组件绘制到另一个组件?
【发布时间】:2013-08-03 08:00:06
【问题描述】:

我有一个扩展的JPanel 类,名为GridPanel。它允许您将图像从JList 拖放到其中。 GridPanel 让您可以用鼠标拖动图像并根据需要重新排列它们。我感兴趣的是制作GridPanel 组件的缩略图。

如果我理解正确,将JScrollPane 的视图设置为GridPanel 会使GridPanel 成为JViewPort 的子级,而JScrollPane 则成为JScrollPane 的子级。目前GridPanel 已经设置为JScrollPane 的视图,我很确定GridPanel 不能有两个父母。所以我不能让两个组件共享同一个视图,但我真的只需要缩略图视图来绘制GridPanel 的缩放视觉副本。

这引出了我的问题。是否可以复制 GridPanel 绘制的内容,但将其绘制在完全独立的组件上?

这是我尝试过的一个例子,以防我不被理解。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class TestMain {
public static void main(String[] a) {
    Color[] colors = new Color[]{Color.green, Color.red, Color.blue, Color.yellow};


    final JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationByPlatform(true);
    JPanel content = new JPanel();
    frame.setContentPane(content);
    content.setLayout(new FlowLayout());
    //using variable names to try and help relate to my question
    final JPanel gridPanel = new JPanel();
    gridPanel.setPreferredSize(new Dimension(200,200));
    gridPanel.setLayout(new GridLayout(2, 2));
    JLabel label;
    for (Color c: colors){
        label = new JLabel();
        label.setOpaque(true);
        label.setBackground(c);
        gridPanel.add(label);
    }

    JScrollPane gridScroll = new JScrollPane(gridPanel);

    final JScrollPane thumbnailScroll = new JScrollPane();
    thumbnailScroll.setPreferredSize(new Dimension(200,200));

    JButton tryThumbnailView = new JButton("Activate Thumbnail");
    tryThumbnailView.addActionListener(new ActionListener(){
        @Override
        public void actionPerformed(ActionEvent arg0) {
            thumbnailScroll.setViewportView(gridPanel);
            frame.repaint();

        }
    });
    content.add(tryThumbnailView);

    content.add(gridScroll);
    content.add(thumbnailScroll);

    frame.pack();
    frame.setVisible(true);
    }
}

我希望两个组件都显示同一组彩色JLabels,而不重复那些JLabels

【问题讨论】:

标签: java swing graphics jscrollpane jviewport


【解决方案1】:

您所要做的就是“获取一个组件的渲染结果”并重用它。 这需要:

  1. 拦截渲染过程 - 覆盖paint(Graphics)方法
  2. 创建自己的图像进行渲染
  3. 将图像渲染为原始图形
  4. 将您的图像渲染到其他组件 - 覆盖 paint(Graphics) 方法

前三个步骤可以这样完成:

@Override
public void paint(Graphics originalGraphics) {
  GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
  GraphicsConfiguration c = e.getDefaultScreenDevice().getDefaultConfiguration();
  //create own image to paint to
  BufferedImage image = c.createCompatibleImage(getWidth(), getHeight());
  Graphics2D reusableGraphics = image.createGraphics();
  //let it paint into our graphics
  super.paint(reusableGraphics);
  // draw image on this component
  originalGraphics.drawImage(image, 0, 0, null);
  // draw image on other component
  otherComponent.setMirrorImage(image);
}

在 otherComponent 中,您必须保存图像并在需要时对其进行绘制:

@Override
public void paint(Graphics g) {
  if (mirroredImage == null) {
    super.paintAll(g);
  } else {
    g.drawImage(mirroredImage, 0, 0, getWidth() * 3 / 4, getHeight() * 3 / 4, null);
  }
}
public void setMirrorImage(BufferedImage mirroredImage) {
  this.mirroredImage = mirroredImage;
  repaint();
}

您可以查看here 的完整示例

【讨论】:

  • 这看起来比其他答案更复杂,但两个答案都让我感兴趣。您是否愿意讨论使用任何一种方法而不是另一种方法的利弊?
  • 当然可以。如果可能的话,我当然会使用更简单的方法。我的解决方案与@tbodt 解决方案至少有两个不同的特征。首先,您可以保留存储的图像并对其进行操作,而无需再次渲染原始组件。此外,由于绘画是分开的,我不直接调用绘画(重新绘画只是建议摆动绘画),如果您不正确地操作图形(例如忘记缩小),它会更安全。如果渲染非常大的组件,tbodt 解决方案可以节省内存。
  • 很抱歉在这里问另一个问题。在您的代码中,BufferedImage image = c.createCompatibleImage(getWidth(), getHeight());...这行是用图像数据构建BufferedImage 吗?这似乎是一种非常方便的理解方法,但 java doc 有点超出我目前的理解。
  • 你所说的“有图像数据”是什么意思?创建的图像具有一些默认背景颜色,您应该对其进行绘制,但可以按原样绘制。您可以使用其构造函数创建BufferedImage,但出于性能原因,最好使用createCompatibleImage,因为您不知道图像数据的底层表示。
【解决方案2】:

您可以像这样覆盖paintComponent

protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.scale(scaleFactor, scaleFactor);
    gridPanel.paintComponenet(g);
}

这假定gridPanel 要么将paintComponent 覆盖为public,要么该类必须与GridPanel 在同一个包中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-06
    • 2017-12-03
    • 2012-12-22
    • 2017-01-08
    • 2015-07-28
    • 2021-12-21
    • 1970-01-01
    • 2011-02-25
    相关资源
    最近更新 更多