无法将我的面板保存为图像

时间:2015-05-09 21:22:00

标签: java swing jframe jpanel bufferedimage

我做了一个简单的绘画程序,但似乎有一些问题。首先,当我运行程序时,组件不会显示,直到我将鼠标拖到每一个上面。其次,我创建的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);
}


}

编辑:

我用paintComponent()替换了paint(),但我仍然不知道如何绘制它,因为g.fillOval ...()什么都不做

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

编辑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);
}
}

1 个答案:

答案 0 :(得分:3)

解决了一个问题:

你没有在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 ......,你真的不想冒这些子组件的绘图的风险。

接下来,要保存图形中的图像,请绘制到BufferedImage,然后在绘图JPanel(附带MouseListener的那个)paintComponent中显示BufferedImage,类似于:

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

因此,在您的鼠标监听器中,您可以使用从图像中获取的图形来调用此图像,方法是调用getGraphics()createGraphics()。前者获取Graphics对象,而后者获取Graphics2D对象。请注意,这与在不推荐的组件上调用getGraphics()不同(根据我的评论)。另请注意,以这种方式获得的任何Graphics对象都应该在您使用它时进行处理,这样您就不会浪费资源。

  

我创建了一个BufferedImage,但是如何将它添加到我的drawPanel / JFrame中。

您的绘图JPanel可以将BufferedImage保存为字段。当你需要绘制它时,你会得到它的Graphics上下文,如上所述 - 请注意,我更喜欢使用createGraphics()来获取Graphics2D对象,这样我就可以使用它的所有东西,比如Strokes。您将BufferedImage的大小调整为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);
       }
   }

}