2

我正在为一个多用户绘图程序开发一个项目,但我遇到了我的 GUI 锁定问题。

除了这个错误之外,我已经完成了大部分程序,它是一个相当大的程序,所以我试图在一个较小的程序中重现该错误。

在这个较小的程序中,我有 2 个 JFrame。两者都可以通过单击和拖动鼠标来绘制。辅助 JFrame 是一个休眠 10 秒的线程,然后将您绘制的内容发送到另一个要显示的帧。

但是,一旦主框架接收到来自辅助框架的图像,GUI 就会锁定,并且无法再绘制主框架。

我目前正在使用 SwingUtilities.invokeLater() 方法。在寻找答案时,我找到了 SwingWorker 类,但我想看看是否有一个简单的解决方案,然后再对我的代码进行大量重写以尝试使其与 SwingWorker 一起使用。

谢谢阅读。我的代码如下。另外,这是我第一次在这里发帖。我似乎在格式化代码时遇到了一些问题,所以如果出现错误,我提前道歉。我会尽力修复它。

package gui_thread_test;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JComponent;

public class DrawPanel extends JComponent
{
    private Image canvas;
    private int x, y, prevX, prevY;
    Graphics2D g2;

    public DrawPanel()
    {
        setDoubleBuffered(false);
        addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent e )
            {
                prevX = e.getX();
                prevY = e.getY();
            }
        });

        addMouseMotionListener(new MouseMotionAdapter()
        {
            @Override
            public void mouseDragged(MouseEvent e)
            {
                x = e.getX();
                y = e.getY();
                g2.drawLine(prevX, prevY, x, y);
                repaint();
                prevX = x;
                prevY = y;
            }
        });
    }

    @Override
    public void paintComponent(Graphics g)
    {
        if (canvas == null)
        {
            canvas = createImage(getSize().width, getSize().height);
            g2 = (Graphics2D) canvas.getGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        }
        g.drawImage(canvas, 0, 0, null);
    }

    public synchronized void updateCanvas(Image _canvas)
    {
        canvas = _canvas;
        repaint();
    }

    public Image getImage()
    {
        return canvas;
    }
}

-

package gui_thread_test;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class ThreadTest extends JFrame implements Runnable
{
    private Image canvas;
    private Graphics2D g2;
    private DrawPanel panel;
    private DrawPanel threadPanel;

    public ThreadTest(DrawPanel _panel)
    {
        super("Secondary");
        panel = _panel;

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setPreferredSize(new Dimension(500,500));
        this.setMinimumSize(new Dimension(500,500));
        this.setVisible(true);

        threadPanel = new DrawPanel();
        this.add(threadPanel);
    }


    public void paintComponent(Graphics g)
    {
        if (canvas == null)
        {
            canvas = createImage(getSize().width, getSize().height);
            g2 = (Graphics2D) canvas.getGraphics();
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        }
        g.drawImage(canvas, 0, 0, null);        
    }

    @Override
    public void run() {
        //Sleep thread for 10 seconds to give time to draw on the image
        try {
            Thread.sleep(10000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ThreadTest.class.getName()).log(Level.SEVERE, null, ex);
        }

        SwingUtilities.invokeLater(
                new Runnable() {
                    @Override
                    public void run() {
                        //Posts the message to the server chat window
                        panel.updateCanvas(threadPanel.getImage());
                    }
                });
    }

}

-

package gui_thread_test;

import java.awt.Dimension;
import javax.swing.JFrame;


public class GUI_Thread_Test {

    public static void main(String[] args) 
    {
        JFrame window = new JFrame("Main");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setPreferredSize(new Dimension(500,500));
        window.setMinimumSize(new Dimension(500,500));

        DrawPanel draw = new DrawPanel();
        window.add(draw);

        Thread testThread = new Thread(new ThreadTest(draw));
        testThread.start();


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

}
4

3 回答 3

3

每 10 秒的传输使用 Swing Timer 而不是线程。使用 BufferedImages 保存图纸并传输图纸。

例如:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class GuiDoubleDraw extends JPanel {
   private static final int BI_WIDTH = 500;
   private static final int BI_HEIGHT = BI_WIDTH;
   public static final Color PEN_COLOR = Color.black;
   private BufferedImage backgroundImg = new BufferedImage(BI_WIDTH, BI_HEIGHT,
         BufferedImage.TYPE_INT_ARGB);

   public GuiDoubleDraw() {
      MyMouseAdapter mouseAdapter = new MyMouseAdapter();
      addMouseListener(mouseAdapter);
      addMouseMotionListener(mouseAdapter);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (backgroundImg != null) {
         g.drawImage(backgroundImg, 0, 0, this);
      }
   }

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

   public BufferedImage getBackgroundImage() {
      BufferedImage copyImg = new BufferedImage(BI_WIDTH, BI_HEIGHT,
            BufferedImage.TYPE_INT_ARGB);
      Graphics g = copyImg.getGraphics();
      g.drawImage(backgroundImg, 0, 0, null);
      g.dispose();
      return copyImg;
   }

   public void setBackgroundImage(BufferedImage bImg) {
      this.backgroundImg = bImg;
      repaint();
   }

   private class MyMouseAdapter extends MouseAdapter {
      Point previousPt = null;

      @Override
      public void mousePressed(MouseEvent mEvt) {
         previousPt = mEvt.getPoint();
      }

      @Override
      public void mouseDragged(MouseEvent mEvt) {
         drawPt(mEvt);
      }

      @Override
      public void mouseReleased(MouseEvent mEvt) {
         drawPt(mEvt);
      }

      private void drawPt(MouseEvent mEvt) {
         Graphics g = backgroundImg.getGraphics();
         Point nextPt = mEvt.getPoint();
         g.setColor(PEN_COLOR);
         g.drawLine(previousPt.x, previousPt.y, nextPt.x, nextPt.y);
         g.dispose();
         previousPt = nextPt;
         repaint();
      }

   }

   private static void createAndShowGui() {
      final GuiDoubleDraw guiDoubleDraw1 = new GuiDoubleDraw();
      final GuiDoubleDraw guiDoubleDraw2 = new GuiDoubleDraw();

      JFrame frame = new JFrame("Draw 1");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(guiDoubleDraw1);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);

      JDialog dialog = new JDialog(frame, "Draw 2", ModalityType.MODELESS);
      dialog.getContentPane().add(guiDoubleDraw2);
      dialog.pack();
      dialog.setLocationRelativeTo(null);
      dialog.setVisible(true);

      int timerDelay = 10 * 1000;
      new Timer(timerDelay, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            guiDoubleDraw1.setBackgroundImage(guiDoubleDraw2
                  .getBackgroundImage());
         }
      }).start();
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}
于 2013-04-05T03:12:57.230 回答
3

您的应用程序没有锁定,您Thread正在退出。

这里没有什么可以阻止run方法完成,通过终止Thread,这意味着不会有更多的更新。

public void run() {
    //Sleep thread for 10 seconds to give time to draw on the image
    try {
        Thread.sleep(10000);
    } catch (InterruptedException ex) {
        Logger.getLogger(ThreadTest.class.getName()).log(Level.SEVERE, null, ex);
    }

    SwingUtilities.invokeLater(
            new Runnable() {
                @Override
                public void run() {
                    System.out.println("Post...");
                    //Posts the message to the server chat window
                    panel.updateCanvas(threadPanel.getImage());
                }
            });
}

HovercraftFullOfEels 是正确的,请javax.swing.Timer改用。它将继续定期滴答作响。

于 2013-04-05T03:22:56.370 回答
1

您将希望在您的代码中实现 SwingWorker,这将允许您在事件调度线程上运行代码。即使您应该在这种情况下找到解决方法,SwingWorker 也是使用线程更新 UI 的正确方法。您会发现使用各种线程并尝试在没有 SwingWorker 的情况下更新 UI 通常会“将自己逼入绝境”。我会重构并将其用作积极的学习体验。

于 2013-04-05T03:14:53.257 回答