在玻璃窗格上绘画而不重新绘制其他组件

时间:2014-08-14 00:31:34

标签: java swing jpanel graphics2d

假设我的JPanel名为内容,其paintComponent(g)被覆盖。这个面板通常非常快速地绘制,但有时候(澄清:用户生成的图形对象可以有数千个点,这会减慢渲染速度。它需要是这个方式,这不是我的问题的一部分)由于其中的所有图形内容可能需要一秒钟重新绘制。

我想在鼠标位置附近绘制一个点,但我想快速更新,而不是每次移动鼠标时都不需要重新绘制内容

我试图将它画在玻璃窗格上,如下所示:

class LabelGlassPane extends JComponent {
    public LabelGlassPane(JApplet frame) {
        this.frame = frame;
    }
    public JApplet frame;
    public void paintComponent(Graphics g) {
        if(ell != null){
            g2.setColor(Vars.overDot);
            g2.fill(ell); //this is an ellipse field that was created in the mouesmoved() listener
        }
    }
}

我的问题是,即使我现在只在移动鼠标时更新此玻璃窗格,但当我在其上调用repaint()时,它会重新绘制applet中的所有其他组件。

如果不对下面的组件进行绘画,我如何在这个玻璃窗格上绘画(或者有另一种绘画方式)?

感谢。

编辑:这是一个简单的例子。移动鼠标时jp不应该更新,但它是。

public class Glasspanetest extends JApplet implements Runnable{

public void init() {
    setLayout(new BorderLayout());
    GraphicsPanel jp = new GraphicsPanel();
    add(jp, BorderLayout.CENTER);
    glass = new LabelGlassPane(this);
    this.setGlassPane(glass);
    glass.setVisible(true);
}


class GraphicsPanel extends JPanel{
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.setColor(Color.red);
        g.fillOval(50, 50, 50, 50);
        System.err.println("Painting bottom panel");
    }
}

public LabelGlassPane glass = null;
public Ellipse2D.Double ell = null;

class LabelGlassPane extends JComponent {
    public LabelGlassPane(JApplet frame) {
        this.frame = frame;
        this.addMouseMotionListener(new MoveInfoListener());
    }
    public JApplet frame;
    public void paintComponent(Graphics g) {
        //g.setColor(Color.red);
        //Container root = frame.getRootPane();
        //          g.setColor(new Color(255,100,100,100));
        //          g.fillRect(100, 100, 500, 500);
        if(ell != null){
            super.paintComponent(g);
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setColor(Color.black);
            g2.fill(ell);
            System.out.println("Painted glass pane");
        }

        //rPaint(root,g);
    }
}

public class MoveInfoListener implements MouseMotionListener{
    public void mouseMoved(MouseEvent e) {
        ell = new Ellipse2D.Double(e.getX()-3, e.getY()-3, 6, 6);
        glass.repaint();
    }

    public void mouseDragged(MouseEvent arg0) {}
}

public void run() {}

}

1 个答案:

答案 0 :(得分:1)

问题在于,Swing无法知道它应该只重新绘制LabelGlassPane的一小部分,可能只展示您GraphicsPanel的一小部分内容。

这是因为您在组件中执行自定义绘制,尽管您只绘制了面板的一小块区域,但您也可以在整个面板上进行绘制。

现在,为什么每次重新绘制GraphicsPanel时,LabelGlassPane都会重新粉饰,这是因为LabelGlassPane不是不透明的,这意味着,为了正确显示没有涂漆的区域,它需要在玻璃板下“涂抹”任何东西。

这两个方面的组合迫使Swing在每次调用重绘时都完全绘制两个面板。

但是有办法避免这种糟糕的组合。为此,您需要通过以下方式使您的绘画方法更加巧妙:

  1. 只要求重新绘制已变脏的矩形(即旧椭圆和新椭圆的结合)
  2. 在您的绘画组件中,仅在Graphics的剪辑与您计划绘制的区域相交时才进行重绘。如果没有交叉点,那么您可以跳过绘制操作。
  3. 小型演示代码显示:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Rectangle;
    import java.awt.RenderingHints;
    import java.awt.Shape;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseMotionListener;
    import java.awt.geom.Ellipse2D;
    import java.awt.geom.Ellipse2D.Double;
    
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class Glasspanetest extends JFrame {
    
        public JFrame init() {
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setLayout(new BorderLayout());
            GraphicsPanel jp = new GraphicsPanel();
            add(jp, BorderLayout.CENTER);
            LabelGlassPane glass = new LabelGlassPane();
            this.setGlassPane(glass);
            glass.setVisible(true);
            setSize(400, 400);
            return this;
        }
    
        class GraphicsPanel extends JPanel {
    
            private Shape oval = new Ellipse2D.Double(50, 50, 50, 50);
    
            @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (g.getClip().intersects(oval.getBounds2D())) {
                    g.setColor(Color.red);
                    ((Graphics2D) g).fill(oval);
                    System.out.println("Painting bottom panel");
                }
            }
        }
    
        public Ellipse2D.Double ell = null;
    
        class LabelGlassPane extends JComponent {
            public LabelGlassPane() {
                this.addMouseMotionListener(new MoveInfoListener());
            }
    
            public class MoveInfoListener implements MouseMotionListener {
                @Override
                public void mouseMoved(MouseEvent e) {
                    Ellipse2D.Double oldEll = ell;
                    ell = new Ellipse2D.Double(e.getX() - 3, e.getY() - 3, 6, 6);
                    LabelGlassPane.this.repaint(oldEll, ell);
                }
    
                @Override
                public void mouseDragged(MouseEvent arg0) {
                }
            }
    
            public void repaint(Double oldEll, Double ell) {
                Rectangle rect = ell.getBounds();
                if (oldEll != null) {
                    rect = rect.union(oldEll.getBounds());
                }
                // repaint(rect);
                repaint();
            }
    
            @Override
            public void paintComponent(Graphics g) {
                // g.setColor(Color.red);
                // Container root = frame.getRootPane();
                // g.setColor(new Color(255,100,100,100));
                // g.fillRect(100, 100, 500, 500);
                if (ell != null) {
                    super.paintComponent(g);
                    Graphics2D g2 = (Graphics2D) g;
                    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                    g.setColor(Color.black);
                    g2.fill(ell);
                    System.out.println("Painted glass pane");
                }
    
                // rPaint(root,g);
            }
    
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new Glasspanetest().init().setVisible(true);
                }
            });
        }
    
    }
    

    注意:这不是我最喜欢的应用程序方法,但它会起作用。不幸的是,我没有完整的应用程序图片来提供替代方法。