【问题标题】:Multiple calls of paintComponent()多次调用paintComponent()
【发布时间】:2013-03-31 05:16:38
【问题描述】:

我有这两个类:

public class Pencil extends JComponent implements MouseListener, MouseMotionListener{
Plansa plansa;
Graphics g;
public Pencil(Plansa newCanvas){
    this.plansa = newCanvas;
    this.plansa.setFlagShape(false);
}
@Override
public void mouseDragged(MouseEvent arg0) {
    plansa.setMouseDragged(arg0);
    this.plansa.setFlagShape(false);
    plansa.paintComponent(plansa.getGraphics());
}

@Override
public void mouseClicked(MouseEvent e) {
}

@Override
public void mousePressed(MouseEvent arg0) {
    plansa.setMousePressed(arg0);
}

@Override
public void mouseReleased(MouseEvent arg0) {
    // TODO Auto-generated method stub
    plansa.setMouseReleased(arg0);
    this.plansa.setFlagShape(true);
    plansa.paintComponent(plansa.getGraphics());

}

@Override
public void mouseEntered(MouseEvent e) {
}

@Override
public void mouseExited(MouseEvent e) {
}

@Override
public void mouseMoved(MouseEvent e) {
}

}

还有这个:

public class Plansa extends JPanel{
Image image;
Pencil pencil;
 //...
 void init(){
    this.setShape("freeLine");
    this.setColor(Color.BLACK);
    this.size=1;
    this.type="round";
    this.fill=false;
    this.setBackground(Color.WHITE);
    pencil = new Pencil(this);
    addMouseListener(pencil);
    addMouseMotionListener(pencil);
    flagPaint = true;
    flagShape = false;
}
public Plansa(){
    this.setSize(800, 600);
    init();
}

//...

       @Override
    public void paintComponent(Graphics g) {
    g.setColor(currentColor);
    Graphics2D g2d = (Graphics2D) g;
    g2d.setStroke(setBrush(size,type));
    super.paintComponent(g);
    switch(shape){
        default: break;
        case "freeLine":{ 
            g.drawLine(xDragged, yDragged, xCurrent, yCurrent); 
            break;
        }
            case "rectangle":{
                if(flagShape == true){
            g.drawRect(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
            if(fill == true) g.fillRect(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
                }
            break;
        }
        case "circle":{
            if(flagShape == true){
            int radius = (int)Math.sqrt(Math.max(xCurrent-xPressed,xPressed-xCurrent)*Math.max(xCurrent-xPressed,xPressed-xCurrent)+Math.max(yCurrent-yPressed,yPressed-yCurrent)*Math.max(yCurrent-yPressed,yPressed-yCurrent));
            g.drawOval(xPressed, yPressed, radius, radius);
            if(fill == true) g.fillOval(xPressed, yPressed, radius, radius);
            }
            break;
        }
        case "oval":{
            if(flagShape == true){
            g.drawOval(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
            if(fill == true) g.fillOval(xPressed, yPressed, Math.max(xCurrent-xPressed,xPressed-xCurrent), Math.max(yCurrent-yPressed,yPressed-yCurrent));
            }
            break;
        }
        case "line":{
            if(flagShape == true){
            g.drawLine(xPressed, yPressed, xCurrent, yCurrent);
            }
            break;
        }
    }

}

 //...
}

我的问题是,每次我调用paintComponent() 方法时,JPanel 都会清除,剩下的唯一项目就是我刚刚绘制的项目。有什么办法可以避免吗?

【问题讨论】:

  • 你不应该自己打电话给paintComponent。这是绘制链的一部分,由 RepaintManager 代表您调用(通过事件调度线程)。相反,您应该使用repaint,它会向 RepaintManager 发出请求,以便在未来某个时间安排更新。你也不应该使用getGraphics。此方法可能返回 null 并且只是上一个绘制周期的快照。它将在下一个绘画周期中被删除
  • 我想到的问题是,为什么不直接将Plansa 添加到父组件并使用null/absolute 布局管理器来布局组件。剩下的就交给你了。另外,请记住,绘画是无国籍的。也就是说,您想在每个绘制周期上绘制的内容必须在其中一种适当的绘制方法中重新绘制
  • 我会考虑的,谢谢。
  • 对于渐进式绘图来说,一个更好的主意是使用BufferedImage 作为画布,如here 所示。
  • @Andrew Thompson 我试过了,效果很好,谢谢。

标签: java swing awt java-2d


【解决方案1】:

仔细检查代码后,您似乎正在尝试将Components 用作“画家”,这有点像将跑车用作卡丁车,很多额外的东西却收效甚微。

相反,您应该为自己定义一些 kine 的接口,该接口提供您想要绘制/绘制的基本要求,然后定义实现该功能的具体类实现。

然后,您将维护一个List,其中包含某种可以绘制的所有图纸。

虽然简单,但下面的示例提供了一个起点,可以对其进行增强,允许选择图纸、重新排序和删除(如果您需要的话)。

这本质上与 Doorknob (+1) 提出的概念相同

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestDraw {

    public static void main(String[] args) {
        new TestDraw();
    }

    public TestDraw() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new Pencil());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Pencil extends JPanel implements MouseListener, MouseMotionListener {

        private List<Drawable> drawables;
        private Drawable activeDrawable;

        private Point clickPoint;

        public Pencil() {
            drawables = new ArrayList<>(5);
            addMouseListener(this);
            addMouseMotionListener(this);
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (Drawable drawable : drawables) {
                drawable.paint(g2d);
            }
            g2d.dispose();
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (activeDrawable != null) {
                Point p = e.getPoint();
                Rectangle bounds = activeDrawable.getBounds();

                int x = bounds.x;
                int y = bounds.y;
                int width = p.x - clickPoint.x;
                int height = p.y - clickPoint.y;
                if (width < 0) {
                    width *= -1;
                    x = p.x;
                }
                if (height < 0) {
                    height *= -1;
                    y = p.y;
                }
                bounds = new Rectangle(x, y, width, height);
                System.out.println(bounds);
                activeDrawable.setBounds(bounds);
                repaint();
            }
        }

        @Override
        public void mouseClicked(MouseEvent e) {
        }

        protected Drawable createActiveShape(MouseEvent e) {

            System.out.println("Anchor = " + e.getPoint());
            Drawable drawable = new FreeLine(e.getPoint());
            drawable.setLocation(e.getPoint());
            return drawable;

        }

        @Override
        public void mousePressed(MouseEvent e) {
            // You could also check to see if the clicked on a drawable...
            clickPoint = e.getPoint();
            activeDrawable = createActiveShape(e);
            drawables.add(activeDrawable);
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (activeDrawable != null) {
                Rectangle bounds = activeDrawable.getBounds();
                if (bounds.width == 0 || bounds.height == 0) {
                    drawables.remove(activeDrawable);
                }
            }
            clickPoint = null;
            activeDrawable = null;
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }

        @Override
        public void mouseMoved(MouseEvent e) {
        }
    }

    public interface Drawable {

        public void setLocation(Point p);

        public void setSize(Dimension dim);

        public void setBounds(Rectangle bounds);

        public Rectangle getBounds();

        public void paint(Graphics2D g2d);
    }

    public abstract class AbstractDrawable implements Drawable {

        private Rectangle bounds;

        public AbstractDrawable() {
            bounds = new Rectangle();
        }

        @Override
        public void setLocation(Point p) {
            bounds.setLocation(p);
        }

        @Override
        public void setBounds(Rectangle bounds) {
            this.bounds = bounds;
        }

        @Override
        public void setSize(Dimension dim) {
            bounds.setSize(dim);
        }

        @Override
        public Rectangle getBounds() {
            return bounds;
        }
    }

    public class FreeLine extends AbstractDrawable {

        private Point anchor;

        public FreeLine(Point anchor) {
            this.anchor = anchor;
        }

        @Override
        public void paint(Graphics2D g2d) {
            Rectangle bounds = getBounds();
            Point p1 = new Point(anchor);
            Point p2 = new Point(bounds.getLocation());
            if (p1.x > p2.x) {
                p2.x = p1.x - bounds.width;
            } else {
                p2.x = p1.x + bounds.width;
            }
            if (p1.y > p2.y) {
                p2.y = p1.y - bounds.height;
            } else {
                p2.y = p1.y + bounds.height;
            }
            g2d.draw(new Line2D.Float(p1, p2));
        }
    }

【讨论】:

  • 好的,现在我明白了。这个例子非常有用。非常感谢您的帮助!
  • @Doorknob 你还是给了我这个主意
【解决方案2】:

将所有对象存储在ArrayList 中,并在绘制时对其进行迭代。

您可以让它们都实现一个自定义接口,例如Drawable,然后存储在ArrayList&lt;Drawable&gt;

【讨论】:

    【解决方案3】:

    对于渐进式绘图,一个更好的主意是使用BufferedImage 作为画布,如here 所示。

    【讨论】:

      【解决方案4】:

      不要使用/注释掉:

      super.paintComponent(g);
      

      那条线正在清理。

      【讨论】:

      • 我认为应该进一步强调,这个建议只是为了观察代码之间的差异,所有现实世界的实现应该 致电super.paintComponent(g);
      • 我也想过这个,但是不明白怎么做,对象使用什么数据类型。我应该创建一个新类,还是使用默认值?
      猜你喜欢
      • 2020-12-15
      • 1970-01-01
      • 1970-01-01
      • 2016-02-04
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多