在玻璃窗格上绘画,而无需重新绘画其他组件 [英] Paint on a glass pane without repainting other components

查看:78
本文介绍了在玻璃窗格上绘画,而无需重新绘画其他组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个名为 content JPanel,其paintComponent(g)被覆盖.该面板通常可以很快地绘制,但是有时(说明:,用户生成了一个可以具有数千个点的图形对象,这会减慢渲染速度.方式,这不是我的问题的一部分),因为其中包含所有图形内容,因此可能需要一秒钟的时间来重新绘制.

Let's say I have a JPanel called content with its paintComponent(g) overridden. This panel usually paints very quickly, but sometimes (Clarification: the user generates a graphics object that can have thousands of points, which slows down the rendering. It needs to be this way and it's not part of my question) it may take a whole second to repaint due to all of the graphics stuff going on in it.

我想在鼠标的位置附近绘制一个点,但是我希望它可以快速更新,而不必每次移动鼠标时都重新绘制 content .

I want to paint a dot near the location of the mouse, but I want this to update quickly and not have to repaint content every time the mouse is moved.

我试图做的是将它画在玻璃板上,像这样:

What I tried to do was paint it on a glass pane, like this:

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()时,它仍会重新绘制小程序中的所有其他组件.

My issue is that even though I now only update this glass pane when the mouse is moved, it's repainting all of the other components in the applet when I call repaint() on it.

如何在不绘画下面的组件的情况下在此玻璃窗格上绘画(或使用另一种绘画方法)?

How can I paint to this glass pane (or have another way of painting) without it painting the components below?

谢谢.

这是一个简单的示例.鼠标移动时jp应该不会更新,但是会更新.

Here is a simple example. jp should not update when the mouse is moved, but it is.

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() {}

}

推荐答案

问题是Swing无法知道它应该只重绘LabelGlassPane的一小部分,也可能只是重绘您的LabelGlassPane的一小部分GraphicsPanel.

The problem is that Swing has no way to know that it should only repaint a small part of your LabelGlassPane and possibly a small part or no part of your GraphicsPanel.

这是由于您在组件中执行自定义绘制,并且尽管您只绘制了面板的一小部分,但也可以在整个面板上很好地绘制.

This is due to the fact that you perform custom painting in your components and that, although you only paint a small area of your panel's, you could also very well paint over the entire panel.

现在,关于为什么每次重新绘制LabelGlassPane时都会重新绘制GraphicsPanel的原因,这是由于LabelGlassPane不是不透明的,这意味着为了正确显示未绘制的区域,它会需要涂在玻璃板下方"的任何东西.

Now, as to why your GraphicsPanel gets repainted every time your LabelGlassPane is repainted, this is due to the fact that LabelGlassPane is not opaque, meaning that, in order to properly display not painted areas, it needs to paint whatever is "under" the glasspane.

这两个方面的组合会迫使Swing每次调用重绘时都对两个面板进行完整的绘制.

The combination of those 2 aspects forces Swing to paint entirely both panels everytime you call repaint.

但是有一些方法可以避免这种不良组合.为此,您需要通过以下方法使您的绘画方法更聪明:

But there are ways to avoid this bad combination. For this to work, you need to make your painting methods more clever by:

  1. 仅要求重新绘制已变脏的矩形(即旧椭圆和新椭圆的并集)
  2. 在绘画组件中,仅当Graphics的剪辑与计划绘画的区域相交时才进行重新绘画.如果没有路口,则可以跳过绘画操作.
  1. Only ask to repaint the rectangle that has been made dirty (ie, the union of the old ellipse and the new one)
  2. In your painting components, only do the repaint if the clip of the Graphics intersects the area where you plan to paint. If there are no intersection, then you can just skip the paint operation.

显示以下内容的小型演示代码:

Small demo code showing that:

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

}

注意:这不是我最喜欢的应用程序方法,但是它可以工作.不幸的是,我没有您的应用程序的完整介绍来提供替代方法.

Note: this wouldn't be my favourite approach to your application, but it will work. Unfortunately, I don't have the full picture of your application to provide an alternate approach to this.

这篇关于在玻璃窗格上绘画,而无需重新绘画其他组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆