Java Can';无法将我的面板另存为图像

Java Can';无法将我的面板另存为图像,java,swing,jframe,jpanel,bufferedimage,Java,Swing,Jframe,Jpanel,Bufferedimage,我做了一个简单的绘画程序,但似乎有一些问题。首先,当我运行程序时,直到我将鼠标拖动到每个组件上,组件才会显示。其次,我创建的drawPanel图像只保存面板的背景,而不保存在上面绘制的内容。另一个小问题是,当我通过下拉菜单改变点的大小时,它下面画的任何东西都会被擦除。任何关于清理我的代码或解决我的问题的建议都会很好 import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.even

我做了一个简单的绘画程序,但似乎有一些问题。首先,当我运行程序时,直到我将鼠标拖动到每个组件上,组件才会显示。其次,我创建的drawPanel图像只保存面板的背景,而不保存在上面绘制的内容。另一个小问题是,当我通过下拉菜单改变点的大小时,它下面画的任何东西都会被擦除。任何关于清理我的代码或解决我的问题的建议都会很好

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class MyPaint extends JFrame implements MouseListener, MouseMotionListener, ActionListener {

//initialize coordinates somewhere offscreen
int myX = -100, myY = -100;
private JPanel bucket = new JPanel(new GridLayout(7, 2));
private JPanel northPanel = new JPanel(new GridBagLayout());
private JPanel drawPanel = new JPanel();
GridBagConstraints c = new GridBagConstraints();
private String[] pencilSize = {"1", "5", "10", "15", "20"};
private JComboBox sizeList = new JComboBox(pencilSize);
private JButton[] buttons = new JButton[11];
private Color[] colorList = {Color.RED, Color.BLUE, Color.ORANGE, Color.CYAN, Color.YELLOW, Color.GREEN, 
                             Color.WHITE, Color.MAGENTA, Color.GRAY, Color.PINK, Color.BLACK};
private Color currentColor = Color.BLACK;
int radius = 5;
private JLabel thickness = new JLabel("Thickness");
private JButton clear = new JButton("Clear");
private JButton save = new JButton("Save");
private JButton open = new JButton("Open");
private JLabel image = new JLabel(" ");
JFileChooser fc = new JFileChooser();

// *******************  MyPaint **************

public MyPaint(){
    super("MyPaint");
    setSize(1000, 1000);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    addMouseListener(this);
    addMouseMotionListener(this);
    setVisible(true);
    setResizable(false);
    sizeList.setEditable(true);
    drawPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));

    for(int i = 0; i < buttons.length; i++){
        buttons[i] = new JButton();
        buttons[i].setBackground(colorList[i]);
        buttons[i].addActionListener(this);
        bucket.add(buttons[i]);
    }

    northPanel.add(clear);
    clear.addActionListener(this);
    northPanel.add(thickness);
    northPanel.add(sizeList);
    sizeList.addActionListener(this);
    northPanel.add(open);
    open.addActionListener(this);
    northPanel.add(save);
    save.addActionListener(this);

    add(drawPanel, BorderLayout.CENTER);
    add(bucket, BorderLayout.WEST);
    add(northPanel, BorderLayout.NORTH);
}

// *******************  Override of paint **************

//paint the oval at the current location
public void paint(Graphics g){
    g.setColor(currentColor);
    if(myX > drawPanel.getX() + 10 && myY > drawPanel.getY() + 25)
        g.fillOval(myX - radius, myY - radius, 2 * radius, 2 * radius);
}

// *******************  Mouse Events **************

public void mouseClicked(MouseEvent e){
    myX = e.getX();
    myY = e.getY();
    repaint();
}

public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}

public void mouseDragged(MouseEvent e){
    myX = e.getX();
    myY = e.getY();
    repaint();
}

public void mouseMoved(MouseEvent e){

}

// *******************  Actions **************

public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
    for(int i = 0; i < buttons.length; i++)
        if(source == buttons[i])
            currentColor = colorList[i];

    if(source == clear)
        super.paint(getGraphics());

    else if(source == sizeList)
        radius = Integer.parseInt((String) sizeList.getSelectedItem());

    //open a file
    else if(source == open){
         int returnValue = fc.showOpenDialog(null);
            if (returnValue == fc.APPROVE_OPTION) {
              File sf = fc.getSelectedFile();
              try {
                image.setIcon(new ImageIcon(ImageIO.read(sf)));
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
              super.paint(getGraphics());
              drawPanel.add(image);
              drawPanel.revalidate();
              drawPanel.repaint();
            }
    }

    //save a file
    else if(source == save){
        fc.setDialogTitle("Specify a file to save");   

        int userSelection = fc.showSaveDialog(drawPanel);

        if (userSelection == JFileChooser.APPROVE_OPTION) {
            File fileToSave = fc.getSelectedFile();
            try{
                BufferedImage image = new BufferedImage(drawPanel.getWidth(),
                        drawPanel.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
                Graphics g = image.getGraphics();
                drawPanel.printAll(g);
                g.dispose();
                ImageIO.write(image, "png", new File("pic.png"));
            } catch (Exception e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }

    }
}

// *******************  Main **************

public static void main(String[] args){
    MyPaint frame = new MyPaint();
    frame.setVisible(true);
}


}
编辑2:

小版本

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class MySmallPaint extends JPanel implements MouseListener, MouseMotionListener, ActionListener{

int myX = 0, myY = 0;
int radius = 5;
JPanel drawPanel = new JPanel();
Dimension d = drawPanel.getPreferredSize();
BufferedImage img = new BufferedImage(d.width,d.height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();

public MySmallPaint(){
    super();
    setSize(1000, 1000);
    addMouseListener(this);
    addMouseMotionListener(this);
    //add(drawPanel, BorderLayout.CENTER);
}

public void paintComponent(Graphics g) {
    super.paintComponents(g);
    g.fillOval(myX - radius, myY - radius, 2 * radius, 2 * radius);
    if(img != null)
        g.drawImage(img, 0, 0, null);
}

@Override
public void actionPerformed(ActionEvent arg0) {
    // TODO Auto-generated method stub
}

@Override
public void mouseDragged(MouseEvent e) {
    // TODO Auto-generated method stub
    myX = e.getX();
    myY = e.getY();
    repaint();
}

@Override
public void mouseMoved(MouseEvent arg0) {
    // TODO Auto-generated method stub
}

@Override
public void mouseClicked(MouseEvent e) {
    myX = e.getX();
    myY = e.getY();
    repaint();
}

@Override
public void mouseEntered(MouseEvent arg0) {
    // TODO Auto-generated method stub
}

@Override
public void mouseExited(MouseEvent arg0) {
    // TODO Auto-generated method stub
}

@Override
public void mousePressed(MouseEvent arg0) {
    // TODO Auto-generated method stub
}

@Override
public void mouseReleased(MouseEvent arg0) {
    // TODO Auto-generated method stub
}

public static void main(String[] args){
     JFrame f = new JFrame("Swing Paint Demo");
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.add(new MySmallPaint());
     f.pack();
     f.setVisible(true);
}
}
解决了一个问题:

您没有在paint方法覆盖内部调用super的paint方法,这样做不允许GUI绘制自己的组件。换句话说,你没有这样做:

public void paint(Graphics g) {
   super.paint(g);  // *************** missing ************
   g.setColor(currentColor);
   if (myX > drawPanel.getX() + 10 && myY > drawPanel.getY() + 25)
      g.fillOval(myX - radius, myY - radius, 2 * radius, 2 * radius);
}
但是说到这里,我也强烈建议你不要像现在这样画画,而是覆盖JPanel的paintComponent方法,在那里画画。通过直接在JFrame上绘制,您可能会弄乱它如何绘制自身、其组件、其边界等等。。。正如你所发现的。JFrames是复杂的组件,包含许多子组件,包括JRootPanes、contentPanes、JLayeredPanes、glasspanes等等。。。你真的不想冒险弄乱这些子组件的图纸

接下来,要保存图形中的图像,请绘制到BuffereImage,然后将该BuffereImage显示在图形JPanel的(连接了鼠标侦听器的)paintComponent中,如:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (myImage != null) {
        g.drawImage(myImage, 0, 0, null);
    }
}
因此,在鼠标侦听器中,您可以使用通过调用图像上的
getGraphics()
createGraphics()
从图像中获取的图形来绘制此图像。前者获取图形对象,而后者获取Graphics2D对象。请注意,这与对不推荐的组件调用
getGraphics()
不同(根据我的评论)。还请注意,以这种方式获得的任何图形对象都应该在使用完毕后进行处理,以免浪费资源



我创建了一个BuffereImage,但如何将其添加到drawPanel/JFrame中

图形JPanel可以将BuffereImage作为字段保存。当您需要在它上面绘制时,您可以获得上面提到的图形上下文——请注意,我更喜欢使用
createGraphics()
来获得Graphics2D对象,这样我就可以使用它的所有优点,例如笔划。将BuffereImage的大小调整为JPanel的首选大小,然后按照上面所示绘制它

我也不确定如何绘制缓冲图像

如上所述。还请注意,如果要创建连接图形,通常最好绘制直线,而不是椭圆或椭圆

举个简单的例子,一个没有你想做的所有事情的人

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class SimpleDrawMain extends JPanel {
   private static final int PREF_W = 600;
   private static final int PREF_H = PREF_W;
   private SimpleDrawPanel simpleDrawPanel = new SimpleDrawPanel(PREF_W, PREF_H);
   private MyMouse myMouse = new MyMouse();

   // drawStroke: thickness of lines drawn. Can change this as needed
   private Stroke drawStroke = new BasicStroke(6f);
   // drawColor -- change this as needed
   private Color drawColor = Color.BLUE; 

   public SimpleDrawMain() {
      simpleDrawPanel.addMouseListener(myMouse);
      simpleDrawPanel.addMouseMotionListener(myMouse);
      simpleDrawPanel.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));

      JPanel topPanel = new JPanel();
      topPanel.add(new JButton(new SaveImageAction("Save", KeyEvent.VK_S)));
      topPanel.add(new JButton(new ClearImageAction("Clear", KeyEvent.VK_C)));

      setLayout(new BorderLayout());
      add(topPanel, BorderLayout.PAGE_START);
      add(simpleDrawPanel, BorderLayout.CENTER);
   }

   private class MyMouse extends MouseAdapter {
      private Graphics2D g2;
      private Point point; // point to draw a line with

      @Override
      public void mousePressed(MouseEvent e) {
         if (e.getButton() != MouseEvent.BUTTON1) {
            return;
         }

         // get our Graphics object to draw with
         g2 = simpleDrawPanel.getMyImage().createGraphics();
         point = e.getPoint();  // get the first point
         g2.setStroke(drawStroke);  // set stroke and color
         g2.setColor(drawColor);
      }

      @Override
      public void mouseDragged(MouseEvent e) {
         if (point == null) {
            return;
         }
         drawOnImage(e);
      }

      @Override
      public void mouseReleased(MouseEvent e) {
         if (point == null) {
            return;
         }
         drawOnImage(e);

         // clean up things
         g2.dispose();
         g2 = null;
         point = null;
      }

      private void drawOnImage(MouseEvent e) {
         // better to draw a line between two points rather than an oval
         // get 2nd point, and then using 2 points, create line to draw
         Point p2 = e.getPoint();
         int x1 = point.x;
         int y1 = point.y;
         int x2 = p2.x;
         int y2 = p2.y;
         g2.drawLine(x1, y1, x2, y2);

         // reset the original point to the new point
         point = p2;

         simpleDrawPanel.repaint();
      }

   }

   private class SaveImageAction extends AbstractAction {
      public SaveImageAction(String name, int mnemonioc) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonioc);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         BufferedImage img = simpleDrawPanel.getMyImage();
         // TODO write code to save img to file         
      }
   }

   private class ClearImageAction extends AbstractAction {
      public ClearImageAction(String name, int mnemonioc) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonioc);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         simpleDrawPanel.clearImage();
      }
   }

   private static void createAndShowGui() {
      SimpleDrawMain mainPanel = new SimpleDrawMain();

      JFrame frame = new JFrame("SimpleDraw");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setResizable(false);
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

class SimpleDrawPanel extends JPanel {
   // preferred size dimensions for this JPanel
   private int prefW;
   private int prefH;
   // image to draw on
   private BufferedImage myImage;

   public SimpleDrawPanel(int prefW, int prefH) {
      this.prefW = prefW;
      this.prefH = prefH;
      myImage = new BufferedImage(prefW, prefH, BufferedImage.TYPE_INT_ARGB);
   }

   public BufferedImage getMyImage() {
      return myImage;            
   }

   public void clearImage() {
      // simply create a new BufferedImage
      myImage = new BufferedImage(prefW, prefH, BufferedImage.TYPE_INT_ARGB);
      repaint();
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(prefW, prefH);
   }

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

}

欢迎来到StackOverflow。请理解,如果您提出一个具体且可回答的问题,而不是一个含糊不清的要求“帮助我清理”,我们将能够更好地提供帮助。请查看,并找出如何改进此问题并提高获得正确答案的几率。请注意,您不希望直接在JFrame中绘制,而是希望在JPanel的paintComponent方法中绘制,并且您希望在重写中首先调用super的方法。请阅读绘画教程。您可以在此处找到指向Swing教程和其他Swing资源的链接:@HovercraftFullOfEels那么您的意思是每个JPanel都有其特定的绘制组件?我是否仍然需要JPanel边界的if语句,这样我就不会在其他组件上绘制了?我看到您直接调用了paint--永远不要这样做。您还可以通过
getGraphics()
获取组件的图形对象,如果这样做,您将得到一个短暂的对象,它可能导致NullPointerException或图像消失,因此您也不想这样做。你看过Swing图形教程了吗?如果没有,您真的希望尽快完成此操作。请检查:1)、Swing graphics入门教程和2)、Swing graphics高级教程。我创建了BuffereImage,但如何将其添加到drawPanel/JFrame中。我也不确定如何在缓冲图像上绘制;然后重新绘制();直接在mouseClicked中什么也看不到。@Alexarkenzon:最好的办法是,如果你有问题,创建一个小的可编译和可运行的程序,向我们演示你的问题。我添加了一个较小的版本top@AlexMarkenzon当前位置您没有按照我的建议进行操作,似乎您仍然直接在JFrame中进行绘制,一个甚至没有要重写的paintComponent方法的类(这就是为什么编译器不允许您调用超级方法,而是调用完全不相关的paintComponents方法)。请重新阅读我的建议,请重新阅读我的代码示例。你可以解决这个问题,但这需要一些努力。
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class SimpleDrawMain extends JPanel {
   private static final int PREF_W = 600;
   private static final int PREF_H = PREF_W;
   private SimpleDrawPanel simpleDrawPanel = new SimpleDrawPanel(PREF_W, PREF_H);
   private MyMouse myMouse = new MyMouse();

   // drawStroke: thickness of lines drawn. Can change this as needed
   private Stroke drawStroke = new BasicStroke(6f);
   // drawColor -- change this as needed
   private Color drawColor = Color.BLUE; 

   public SimpleDrawMain() {
      simpleDrawPanel.addMouseListener(myMouse);
      simpleDrawPanel.addMouseMotionListener(myMouse);
      simpleDrawPanel.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));

      JPanel topPanel = new JPanel();
      topPanel.add(new JButton(new SaveImageAction("Save", KeyEvent.VK_S)));
      topPanel.add(new JButton(new ClearImageAction("Clear", KeyEvent.VK_C)));

      setLayout(new BorderLayout());
      add(topPanel, BorderLayout.PAGE_START);
      add(simpleDrawPanel, BorderLayout.CENTER);
   }

   private class MyMouse extends MouseAdapter {
      private Graphics2D g2;
      private Point point; // point to draw a line with

      @Override
      public void mousePressed(MouseEvent e) {
         if (e.getButton() != MouseEvent.BUTTON1) {
            return;
         }

         // get our Graphics object to draw with
         g2 = simpleDrawPanel.getMyImage().createGraphics();
         point = e.getPoint();  // get the first point
         g2.setStroke(drawStroke);  // set stroke and color
         g2.setColor(drawColor);
      }

      @Override
      public void mouseDragged(MouseEvent e) {
         if (point == null) {
            return;
         }
         drawOnImage(e);
      }

      @Override
      public void mouseReleased(MouseEvent e) {
         if (point == null) {
            return;
         }
         drawOnImage(e);

         // clean up things
         g2.dispose();
         g2 = null;
         point = null;
      }

      private void drawOnImage(MouseEvent e) {
         // better to draw a line between two points rather than an oval
         // get 2nd point, and then using 2 points, create line to draw
         Point p2 = e.getPoint();
         int x1 = point.x;
         int y1 = point.y;
         int x2 = p2.x;
         int y2 = p2.y;
         g2.drawLine(x1, y1, x2, y2);

         // reset the original point to the new point
         point = p2;

         simpleDrawPanel.repaint();
      }

   }

   private class SaveImageAction extends AbstractAction {
      public SaveImageAction(String name, int mnemonioc) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonioc);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         BufferedImage img = simpleDrawPanel.getMyImage();
         // TODO write code to save img to file         
      }
   }

   private class ClearImageAction extends AbstractAction {
      public ClearImageAction(String name, int mnemonioc) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonioc);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         simpleDrawPanel.clearImage();
      }
   }

   private static void createAndShowGui() {
      SimpleDrawMain mainPanel = new SimpleDrawMain();

      JFrame frame = new JFrame("SimpleDraw");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setResizable(false);
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

class SimpleDrawPanel extends JPanel {
   // preferred size dimensions for this JPanel
   private int prefW;
   private int prefH;
   // image to draw on
   private BufferedImage myImage;

   public SimpleDrawPanel(int prefW, int prefH) {
      this.prefW = prefW;
      this.prefH = prefH;
      myImage = new BufferedImage(prefW, prefH, BufferedImage.TYPE_INT_ARGB);
   }

   public BufferedImage getMyImage() {
      return myImage;            
   }

   public void clearImage() {
      // simply create a new BufferedImage
      myImage = new BufferedImage(prefW, prefH, BufferedImage.TYPE_INT_ARGB);
      repaint();
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(prefW, prefH);
   }

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

}