Java 如何在JComponent上永久绘制

Java 如何在JComponent上永久绘制,java,swing,arraylist,graphics,shape,Java,Swing,Arraylist,Graphics,Shape,我正在尝试用Java创建一个绘画应用程序 我有一个扩展JComponent的“canvas”类 我有一个Shape类型的ArrayList,它保存了整个图形的所有形状 在paintComponent()方法中: 每个循环都会被清除 ArrayList中的每个形状都使用g.draw()或g.fill()进行绘制 如果要添加形状或绘制形状,请将其添加到ArrayList 问题是,在ArrayList中有相当多的形状之后,paintComponent()方法的执行速度会减慢 例如,自定义笔刷

我正在尝试用Java创建一个绘画应用程序

  • 我有一个扩展JComponent的“canvas”类
  • 我有一个Shape类型的ArrayList,它保存了整个图形的所有形状
在paintComponent()方法中:

  • 每个循环都会被清除
  • ArrayList中的每个形状都使用g.draw()或g.fill()进行绘制
  • 如果要添加形状或绘制形状,请将其添加到ArrayList
问题是,在ArrayList中有相当多的形状之后,paintComponent()方法的执行速度会减慢

例如,自定义笔刷

  • 在画布上拖动笔刷时,我必须向ArrayList添加一个类型为“CustomBrush extends Shape”的新形状
  • 因此,只需一个笔划,我就可以在ArrayList中找到数百个形状
问题是:

  • 如何将100个形状对象“打包”为一个,使单个笔刷笔划成为ArrayList中的单个对象
  • 但是,最终目标是加快paintComponent()方法的速度,以便更快地绘制所有绘制的形状
谢谢大家!

下面是一个示例代码:

public class GraphicPanel extends JComponent{
  private ArrayList<Shape> shapeBuffer;

  public void paintComponent( Graphics gPlain ){
       Graphics2D g = (Graphics2D)gPlain;
       for( Shape s : shapeBuffer ){
        if( filled.next() ){
            g.fill( s );
        }
        else{
            g.draw( s );
        }
  }
}
公共类GraphicPanel扩展JComponent{
私人ArrayList shapeBuffer;
公共组件(图形gPlain){
Graphics2D g=(Graphics2D)gPlain;
用于(形状s:shapeBuffer){
if(filled.next()){
g、 填充;
}
否则{
g、 抽签;
}
}
}

使用
g.drawImage(…)
将背景绘制到BuffereImage,然后使用
g.drawImage(…)
将BuffereImage绘制到您的GrahpicPanel中。您可以通过调用
getGraphics()
createGraphics()
(对于Graphics2D对象)来获取BuffereImage的图形上下文。不要忘记处理以这种方式获得的图形对象(但永远不要处理JVM提供给您的图形对象)

另外,不要忘记调用覆盖中的
super.paintComponent(g)

例如:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.swing.*;

public class MyPaint extends JComponent {
   private static final int PREF_W = 600;
   private static final int PREF_H = PREF_W;
   private static final Stroke STROKE = new BasicStroke(4f);
   private static final Color[] COLORS = { Color.RED, Color.GREEN,
         Color.yellow, Color.orange, Color.blue, Color.cyan };
   private BufferedImage img = new BufferedImage(PREF_W, PREF_H,
         BufferedImage.TYPE_INT_ARGB);
   private Rectangle rect = null;

   public MyPaint() {
      MyMouse myMouse = new MyMouse();
      addMouseListener(myMouse);
      addMouseMotionListener(myMouse);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (img != null) {
         g.drawImage(img, 0, 0, null);
      }
      if (rect != null) {
         g.setColor(Color.LIGHT_GRAY);
         ((Graphics2D) g).draw(rect);
      }
   }

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

   private class MyMouse extends MouseAdapter {
      private Random random = new Random();
      private Point p;

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

      @Override
      public void mouseReleased(MouseEvent e) {
         if (p != null) {
            Rectangle rect2 = createRect(e.getPoint());
            Graphics2D g2 = img.createGraphics();
            g2.setStroke(STROKE);
            Color c = COLORS[random.nextInt(COLORS.length)];
            g2.setColor(c);
            g2.fill(rect2);
            g2.setColor(c.darker());
            g2.draw(rect2);
            g2.dispose();
         }

         p = null;
         rect = null;
         repaint();
      }

      @Override
      public void mouseDragged(MouseEvent e) {
         rect = createRect(e.getPoint());
         repaint();
      }

      private Rectangle createRect(Point p2) {
         int x = Math.min(p.x, p2.x);
         int y = Math.min(p.y, p2.y);
         int width = Math.abs(p.x - p2.x);
         int height = Math.abs(p.y - p2.y);
         Rectangle rect2 = new Rectangle(x, y, width, height);
         return rect2;
      }

   }

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

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

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

将背景绘制到BuffereImage,然后使用
g.drawImage(…)
paintComponent(…)
方法中将BuffereImage绘制到GrahpicPanel中。您可以通过调用
getGraphics()
createGraphics()
(对于Graphics2D对象)来获取BuffereImage的图形上下文。不要忘记处理以这种方式获得的图形对象(但永远不要处理JVM提供给您的图形对象)

另外,不要忘记调用覆盖中的
super.paintComponent(g)

例如:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.swing.*;

public class MyPaint extends JComponent {
   private static final int PREF_W = 600;
   private static final int PREF_H = PREF_W;
   private static final Stroke STROKE = new BasicStroke(4f);
   private static final Color[] COLORS = { Color.RED, Color.GREEN,
         Color.yellow, Color.orange, Color.blue, Color.cyan };
   private BufferedImage img = new BufferedImage(PREF_W, PREF_H,
         BufferedImage.TYPE_INT_ARGB);
   private Rectangle rect = null;

   public MyPaint() {
      MyMouse myMouse = new MyMouse();
      addMouseListener(myMouse);
      addMouseMotionListener(myMouse);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (img != null) {
         g.drawImage(img, 0, 0, null);
      }
      if (rect != null) {
         g.setColor(Color.LIGHT_GRAY);
         ((Graphics2D) g).draw(rect);
      }
   }

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

   private class MyMouse extends MouseAdapter {
      private Random random = new Random();
      private Point p;

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

      @Override
      public void mouseReleased(MouseEvent e) {
         if (p != null) {
            Rectangle rect2 = createRect(e.getPoint());
            Graphics2D g2 = img.createGraphics();
            g2.setStroke(STROKE);
            Color c = COLORS[random.nextInt(COLORS.length)];
            g2.setColor(c);
            g2.fill(rect2);
            g2.setColor(c.darker());
            g2.draw(rect2);
            g2.dispose();
         }

         p = null;
         rect = null;
         repaint();
      }

      @Override
      public void mouseDragged(MouseEvent e) {
         rect = createRect(e.getPoint());
         repaint();
      }

      private Rectangle createRect(Point p2) {
         int x = Math.min(p.x, p2.x);
         int y = Math.min(p.y, p2.y);
         int width = Math.abs(p.x - p2.x);
         int height = Math.abs(p.y - p2.y);
         Rectangle rect2 = new Rectangle(x, y, width, height);
         return rect2;
      }

   }

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

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

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

笔触和颜色是形状的属性。您可以这样定义自己的形状类。这样,您就可以在列表中维护形状

package com.ggl.testing;

import java.awt.Color;
import java.awt.Shape;

public class MyShape {

    private int brushSize;

    private Color interiorColor;

    private Shape shape;

    public MyShape() {
        this.brushSize = 1;
        this.interiorColor = Color.blue;
    }

    public MyShape(int brushSize, Color interiorColor, Shape shape) {
        this.brushSize = brushSize;
        this.interiorColor = interiorColor;
        this.shape = shape;
    }

    public int getBrushSize() {
        return brushSize;
    }

    public void setBrushSize(int brushSize) {
        this.brushSize = brushSize;
    }

    public Color getInteriorColor() {
        return interiorColor;
    }

    public void setInteriorColor(Color interiorColor) {
        this.interiorColor = interiorColor;
    }

    public Shape getShape() {
        return shape;
    }

    public void setShape(Shape shape) {
        this.shape = shape;
    }

}

如果我遗漏了任何属性,请随意将它们添加到您的形状类中。

笔刷和颜色是形状的属性。您可以这样定义自己的形状类。这样,您就可以在列表中维护形状

package com.ggl.testing;

import java.awt.Color;
import java.awt.Shape;

public class MyShape {

    private int brushSize;

    private Color interiorColor;

    private Shape shape;

    public MyShape() {
        this.brushSize = 1;
        this.interiorColor = Color.blue;
    }

    public MyShape(int brushSize, Color interiorColor, Shape shape) {
        this.brushSize = brushSize;
        this.interiorColor = interiorColor;
        this.shape = shape;
    }

    public int getBrushSize() {
        return brushSize;
    }

    public void setBrushSize(int brushSize) {
        this.brushSize = brushSize;
    }

    public Color getInteriorColor() {
        return interiorColor;
    }

    public void setInteriorColor(Color interiorColor) {
        this.interiorColor = interiorColor;
    }

    public Shape getShape() {
        return shape;
    }

    public void setShape(Shape shape) {
        this.shape = shape;
    }

}

如果我遗漏了任何属性,请随意将它们添加到您的shape类中。

“画笔和颜色是形状的属性”
--也许我误解了您的答案,但这不应该是“不是形状的属性”?请为我澄清。@HovercraftFullOfEels:当你绘制或填充一个形状时,你可以在绘制或填充一个形状之前设置颜色和笔划。这使形状具有颜色和笔划属性。我理论上同意,但我是从字面上阅读它的形状接口及其具体类,它们没有机制来实现这一点。我明白你的意思了。1+
“笔触和颜色是形状的属性”
——也许我误解了你的答案,但这不应该是“不是形状的属性”?请为我澄清。@HovercraftFullOfEels:当你绘制或填充一个形状时,你可以在绘制或填充一个形状之前设置颜色和笔划。这使形状具有颜色和笔划属性。我理论上同意,但我是从字面上阅读它的形状接口及其具体类,它们没有机制来实现这一点。我明白你的意思了。1+检查。它比较了两种方法1)从列表中绘制和2)从缓冲区图像绘制,并包含了这两种方法的工作示例。@camickr感谢装满鳗鱼的气垫船,我可以实现这两种检查。它比较了两种方法1)从列表中绘制和2)从缓冲区图像绘制和继续这两种方法都是很好的例子。@camickr多亏了装满鳗鱼的气垫船,我才能够实现我想要的东西!谢谢你,先生!还有,在mouseDragged()中,为什么不仅仅是rect.setSize()呢?为垃圾箱浪费更少的内存和更少的工作collector@Fresco:那可能更好。试试看。这正是我想要的!谢谢你,先生!还有,在mouseDragged()中,为什么不只是rect.setSize()?为垃圾浪费更少的内存和更少的工作collector@Fresco:那很可能更好。去做吧。