Java Swing绘画&鼠标事件闪烁

时间:2011-05-16 12:04:19

标签: java mouse paint flicker

我有一个扩展JPanel的类(下面),这个面板位于JScrollPane中。它侦听(自身)鼠标事件并尝试重新定位自身(在拖动时)并重新缩放自身(在滚轮上)以模拟鼠标移动和缩放。该面板还负责我的应用程序的主要视觉输出。它存储了一个BufferedImage,它在JScrollPane的可视区域中呈现(但在面板的图形上)。保持图像的大小和形状以匹配可视区域。

我的问题是这样的;

1)在鼠标事件中,我得到了大量的闪烁和性能下降 2)如果我用我自己的绘画方法覆盖paint或paintComponent方法,这对于摆脱闪烁和其他绘画问题是可取的,我仍然得到相同的闪烁效果和从加载的图像中绘制的图形,这些图像具有透明区域然后颜色为区域黑色。当我手动调用我的paint方法而不覆盖paint和paintComponent方法时,我仍然会闪烁,但透明区域显示正确。

我是Swing绘画的新手,显然做错了什么,有人能指出我正确的方向来解决这个问题吗?

由于

    import jSim.simulation.Simulation;
    import java.awt.Color;
    import java.awt.Cursor;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.Point;
    import java.awt.Rectangle;
    import java.awt.Toolkit;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseWheelEvent;
    import java.awt.event.MouseWheelListener;
    import java.awt.image.BufferStrategy;
    import java.awt.image.BufferedImage;
    import javax.swing.JPanel;
    import javax.swing.JViewport;
    import javax.swing.event.MouseInputListener;

    public class SimPanel extends JPanel implements MouseWheelListener, MouseInputListener {
        //Simulation

        Simulation sim;
        //Viewer
        JViewport viewport;
        Dimension viewSize;
        BufferStrategy strat;
        //Drawing
        Image renderImage;
        Graphics2D g2d;
        boolean draw = true;
        double scale = 1.0;
        Object drawLock = new Object();
        //Mouse events
        int m_XDifference, m_YDifference;

        public SimPanel(JViewport viewport) {
            this.viewport = viewport;
            this.addMouseListener(this);
            this.addMouseMotionListener(this);
            this.addMouseWheelListener(this);

            //this.setup();
        }

        public SimPanel(Simulation sim, JViewport viewport) {
            this.sim = sim;
            this.viewport = viewport;
            this.addMouseListener(this);
            this.addMouseMotionListener(this);
            this.addMouseWheelListener(this);
            //this.setup();
        }

        //Used to initialise the buffered image once drawing begins
        private void setup() {
            synchronized (drawLock) {
                viewSize = viewport.getExtentSize();
                renderImage = new BufferedImage(viewSize.width, viewSize.height, BufferedImage.TYPE_INT_RGB);
                g2d = (Graphics2D) renderImage.getGraphics();
            }
        }

    //    @Override
    //    public void paint(Graphics g)
    //    {
    //        synchronized(drawLock) {
    //        //super.paintComponent(g);
    //        paintSimulation();
    //        }
    //    }
        //Paint the screen for a specific simulation
        public void paintSimulation(Simulation sim) {
            synchronized (drawLock) {
                setSimulation(sim);
                paintSimulation();
            }
        }

        //Paint the screen with the panels simulation
        public void paintSimulation() {
            synchronized (drawLock) {
                //if no image, then init
                if (renderImage == null) {
                    setup();
                }
                //clear the screen
                resetScreen();
                //draw the simulation if not null, to the image
                if (sim != null) {
                    sim.draw(this);
                }
                //paint the screen with the image
                paintScreen();
            }
        }

        private void resetScreen() {
            Dimension newSize = viewport.getExtentSize();
            if (viewSize.height != newSize.height || viewSize.width != newSize.width || renderImage == null) {
                //System.out.println("Screen Size Changed: " + viewSize + "   " + newSize);
                viewSize = newSize;
                renderImage = new BufferedImage(viewSize.width, viewSize.height, BufferedImage.TYPE_INT_RGB);
                g2d = (Graphics2D) renderImage.getGraphics();
            } else {
                g2d.setBackground(Color.DARK_GRAY);
                g2d.clearRect(0, 0, (int) (viewSize.width), (int) (viewSize.height));
            }
        }

        private void paintScreen() {
            Graphics g;
            Graphics2D g2;
            try {
                //g = viewport.getGraphics();
                g = this.getGraphics();
                g2 = (Graphics2D) g;
                if ((g != null) && (renderImage != null)) {
                    g2.drawImage(renderImage, (int) viewport.getViewPosition().getX(), (int) viewport.getViewPosition().getY(), null);
                }
                Toolkit.getDefaultToolkit().sync();  // sync the display on some systems
                g.dispose();
                g2.dispose();
                this.revalidate();
            } catch (Exception e) {
                System.out.println("Graphics context error: " + e);
            }
        }

        //Simulation makes calls to this method to draw items on the image
        public void draw(BufferedImage image, int x, int y, Color colour) {
            synchronized (drawLock) {
                Rectangle r = viewport.getViewRect();
                if (g2d != null && draw) {
                    Point p = new Point((int) (x * scale), (int) (y * scale));
                    if (r.contains(p)) {
                        if (scale < 1) {
                            Graphics2D g2 = (Graphics2D) image.getGraphics();
                            Image test = image.getScaledInstance((int) (image.getWidth(null) * scale), (int) (image.getHeight(null) * scale), Image.SCALE_FAST);
                            g2d.drawImage(test, (int) ((x * scale - r.x)), (int) ((y * scale - r.y)), null);
                        } else {
                            g2d.drawImage(image, x - r.x, y - r.y, null);
                        }
                    }
                }
            }
        }

        public void setDraw(boolean draw) {
            this.draw = draw;
        }

        public void setSimulation(Simulation sim) {
            synchronized (drawLock) {
                if (!(this.sim == sim)) {
                    this.sim = sim;
                }
            }
        }

        public void mouseWheelMoved(MouseWheelEvent e) {
            synchronized (drawLock) {
                updatePreferredSize(e.getWheelRotation(), e.getPoint());
            }
        }

        private void updatePreferredSize(int wheelRotation, Point stablePoint) {
            double scaleFactor = findScaleFactor(wheelRotation);
            if (scale * scaleFactor < 1 && scale * scaleFactor > 0.05) {
                scaleBy(scaleFactor);
                Point offset = findOffset(stablePoint, scaleFactor);
                offsetBy(offset);
                this.getParent().doLayout();
            }
        }

        private double findScaleFactor(int wheelRotation) {
            double d = wheelRotation * 1.08;
            return (d > 0) ? 1 / d : -d;
        }

        private void scaleBy(double scaleFactor) {
            int w = (int) (this.getWidth() * scaleFactor);
            int h = (int) (this.getHeight() * scaleFactor);
            this.setPreferredSize(new Dimension(w, h));
            this.scale = this.scale * scaleFactor;
        }

        private Point findOffset(Point stablePoint, double scaleFactor) {
            int x = (int) (stablePoint.x * scaleFactor) - stablePoint.x;
            int y = (int) (stablePoint.y * scaleFactor) - stablePoint.y;
            return new Point(x, y);
        }

        private void offsetBy(Point offset) {
            Point location = viewport.getViewPosition();
            //this.setLocation(location.x - offset.x, location.y - offset.y);
            viewport.setViewPosition(new Point(location.x + offset.x, location.y + offset.y));
        }

        public void mouseDragged(MouseEvent e) {
            synchronized (drawLock) {
                //Point p = this.getLocation();
                Point p = viewport.getViewPosition();
                int newX = p.x - (e.getX() - m_XDifference);
                int newY = p.y - (e.getY() - m_YDifference);
                //this.setLocation(newX, newY);
                viewport.setViewPosition(new Point(newX, newY));
                //this.getParent().doLayout();
            }
        }

        public void mousePressed(MouseEvent e) {
            synchronized (drawLock) {
                setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                m_XDifference = e.getX();
                m_YDifference = e.getY();
            }
        }

        public void mouseReleased(MouseEvent e) {
            synchronized (drawLock) {
                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            }
        }

        public void mouseClicked(MouseEvent e) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }

        public void mouseEntered(MouseEvent e) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }

        public void mouseExited(MouseEvent e) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }

        public void mouseMoved(MouseEvent e) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }
    }

2 个答案:

答案 0 :(得分:1)

简而言之,请查看双缓冲。

答案越久......

覆盖paintComponent。创建一个屏幕外的图形对象。你在那个物体上画画。将其复制到传递给paint方法的图形对象。

哦,摆脱所有的同步。你不需要它。

答案 1 :(得分:0)

如果在视口上添加setOpaque(true),则告知Swing您将自己完成所有绘画(尤其是背景)。这可能已经有所帮助了。

修改

我看了一下,认为你应该覆盖paintComponent。

你可以有2张图片和4张参考:

  • imageToPaint = null
  • imageToWriteTo = null
  • bufferImageOne初始化为适当大小的BufferedImage
  • bufferImageTwo初始化为适当大小的BufferedImage

你会覆盖paintComponent来绘制背景,然后是drawImage(imageToPaint)(如果它不是null,它不应该是)

您将拥有一个自定义绘制imageToWriteTo的线程。 最后它交换imageToPaint和imageToWriteTo。

然后你调用repaint()。这需要重新绘制,并且具有额外的优点,即Swing队列上的所有重绘请求被合并在一起并导致单个绘制。请不要重新验证或同步。重新绘制是在第二个线程上自动完成的,即事件调度线程。

这样,更新模拟图像就会与实际绘画分离,并且绘画更新无需等待模拟完成绘图。 它可能是由于略微过时的图像(使用缓冲有点隐含),但它应该会产生更好的结果。

简而言之,对imageToWriteTo进行了昂贵的写作。使用imageToPaint完成绘画。昂贵的写作以交换imageToWriteTo和imageToPaint结束。