最好处理两个重叠的实时更新面板?

6
在基本层面上,我有一个面板正在实时逐像素绘制一条线。在这之上,我想要另一个面板,在当前像素周围绘制一个框,但稍后清除自身。两者都是实时的。
我的现状是一个包装JPanel,它具有 OverlayLayout。底部面板具有使用从其 JPanel 获取的 Graphics2D 对象绘制的线条。顶部面板则具有绘制跟随该线的框的 Graphics2D 对象,也是从其JPanel获取的。
该系统中存在许多问题。由于我只是单独地在 Graphics2D 对象上绘图,而不是覆盖 JPanel 的 paint(),因此当面板需要重新绘制时,所有线条都会丢失。我认为我违反了 Swing 的线程模型,因为只有一个线程更新屏幕。我还没有使顶部面板正确工作,它只是不断地清除屏幕,不允许底部面板绘制线条。
如何解决这种情况最好的方法是什么?我对图像处理和 Swing 中的低级图像显示几乎没有经验,只知道基础知识。我听说过 BufferedImage,但不知道应该在哪里放置该图像,如果稍后更改是否会更新该图像,以及执行此操作的效率和其缓冲的事实让我感到害怕。我不确定要使用什么。
有人可以指导我应该使用什么来完成这个任务吗?

你是否正在尝试在两个组件之间绘制一条线?如果是,请参见https://dev59.com/SFjUa4cB1Zd3GeqPWf-_#6610064。 - mre
@mre 一个玻璃窗格,虽然很有趣,但我不知道它的存在,因为你无法清除面板而不清除下面的内容。当你在某物上绘制时,被覆盖的信息就会丢失。 - TheLQ
还要考虑使用 [tag:jfreechart],例如:(https://dev59.com/5m445IYBdhLWcg3wE2Tw#5048863)。 - trashgod
2个回答

4
你可以在适当的Shape中累积你的点,例如LissajousPanel所示的GeneralPath。使用javax.swing.Timer进行定期更新,并用周围的矩形突出显示最近添加的点。

请参阅如何使用Swing计时器 - trashgod

3

我建议:

  • 不要通过getGraphics获取JPanel的Graphics或Graphics2D对象,因为该对象不稳定且一旦组件由于任何原因被重绘,它将无法工作。
  • 相反,您应该在单个JPanel的paintComponent方法中完成所有绘图操作,但首先在该方法中调用super的方法。
  • 再次强调,只在一个JPanel中进行绘制,而不是两个。
  • 使用BufferedImage绘制线条,因为它是图像的更持久部分。
  • 通过其createGraphics方法获得BufferedImage的Graphics2D对象。
  • 当您使用完BufferedImage的Graphics对象时,请正确处理以节省资源。
  • 在您的JPanel的paintComponent方法中显示BufferedImage(首先调用super方法)。
  • 直接在paintComponent中绘制框,这是图像的短暂部分,使用int类变量告诉paintComponent在哪里绘制,可能使用类布尔变量告诉是否绘制。
  • 最重要的是,您需要查阅Swing图形教程,以便正确实现此功能,您将不得不从头开始学习正确的方法(就像我们所有人都必须做的那样)。

例如:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

@SuppressWarnings("serial")
public class BufferedImageEg extends JPanel {
   private static final int BI_WIDTH = 700;
   private static final int BI_HEIGHT = 500;
   private static final Color BACKGROUND = new Color(255, 255, 240);
   private static final int THIS_PT_WIDTH = 12;
   private static final int THIS_PT_HEIGHT = THIS_PT_WIDTH;
   private static final float THIS_PT_STROKE_WIDTH = 2f;
   private static final Color THIS_PT_BORDER_COLOR = Color.red;
   private static final Color THIS_PT_FILL_COLOR = new Color(250, 250, 0, 125);
   private static final int TIMER_DELAY = 30;
   private static final int X_MIN = 0;
   private static final int X_MAX = 100;
   private static final double X_STEP = 0.1;
   private static final double X_SCALE = (double) BI_WIDTH
         / ((double) X_MAX - X_MIN);
   private static final double Y_SCALE = 8;
   private static final float LINE_WIDTH = 4;
   private static final Color LINE_COLOR = Color.blue;

   private Point lastPoint = null;
   private Point thisPoint = null;

   private BufferedImage bImage = new BufferedImage(BI_WIDTH, BI_HEIGHT,
         BufferedImage.TYPE_INT_RGB);
   private double xValue = X_MIN;

   public BufferedImageEg() {
      Graphics biG = bImage.getGraphics();
      biG.setColor(BACKGROUND);
      biG.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
      setBackground(BACKGROUND);
      new Timer(TIMER_DELAY, new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            timerActionPerformed(e);
         }
      }).start();
   }

   private void timerActionPerformed(ActionEvent e) {
      if (xValue <= X_MAX) {
         lastPoint = thisPoint;
         double tempX = xValue;
         double yValue = function(xValue);

         tempX *= X_SCALE;
         yValue *= Y_SCALE;
         yValue = BI_HEIGHT / 2.0 - yValue;

         thisPoint = new Point((int) tempX, (int) yValue);

         if (lastPoint != null) {
            drawInBufferedImage();
         }

         xValue += X_STEP;
      } else {
         ((Timer) e.getSource()).stop();
         thisPoint = null;
      }
      repaint();
   }

   private void drawInBufferedImage() {
      Graphics2D g2 = bImage.createGraphics();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setStroke(new BasicStroke(LINE_WIDTH, BasicStroke.CAP_ROUND,
            BasicStroke.JOIN_ROUND));
      g2.setColor(LINE_COLOR);
      int x1 = lastPoint.x;
      int y1 = lastPoint.y;
      int x2 = thisPoint.x;
      int y2 = thisPoint.y;
      g2.drawLine(x1, y1, x2, y2);
      g2.dispose();
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(BI_WIDTH, BI_HEIGHT);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      g.drawImage(bImage, 0, 0, null);
      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

      if (thisPoint != null) {
         drawThisPoint(g2);
      }
   }

   private void drawThisPoint(Graphics2D g2) {
      int x = thisPoint.x - THIS_PT_WIDTH / 2;
      int y = thisPoint.y - THIS_PT_HEIGHT / 2;
      Graphics2D g2b = (Graphics2D) g2.create();
      g2b.setStroke(new BasicStroke(THIS_PT_STROKE_WIDTH));
      g2b.setColor(THIS_PT_FILL_COLOR);
      g2b.fillOval(x, y, THIS_PT_WIDTH, THIS_PT_HEIGHT);
      g2b.setColor(THIS_PT_BORDER_COLOR);
      g2b.drawOval(x, y, THIS_PT_WIDTH, THIS_PT_HEIGHT);
      g2b.dispose();
   }

   private double function(double x) {
      return 24 * Math.sin(x / 12.0) * Math.sin(x);
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("BufferedImage Example");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new BufferedImageEg());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

+1 同意,但是 JPanel 的默认缓冲可能已经足够了。 - trashgod
@trash:谢谢你,对于Shape和GeneralPath的出色建议我点赞了。 - Hovercraft Full Of Eels
哇,+200,这是一个非常有用的例子。它给了我一个很好的起点,尽管需要对我的程序进行一般性的重构(目前点是在两个嵌套循环中生成的,您的计算每个点)。非常感谢! - TheLQ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接