Java:在不重新绘制的情况下调整组件大小?

标签 java swing

我正在开发一个在 JScrollPane 中显示 map (继承自 JComponent)的 Java 程序。当 MouseWheelListener 触发时, map 会缩放并且 JScrollPane 的视口(viewport)会调整为以鼠标位置为中心。

一切正常,除了调用 setSize(Dimension d) 会强制 map 在调整 View 之前立即重新绘制,从而导致“卡顿”。但是,在 setSize 完成执行之前我无法调整 View ,否则视口(viewport)“居中”的计算将变得困惑(由于 getHeight()getWidth () 调用,) 因此视口(viewport)调整在使用 invokeLater 调用的可运行对象内进行。

我想找到一种方法,可以直接从以前的 map 大小和视口(viewport)位置移动到新 View ,而不会看到场景重绘两次。

setIgnoreRepaint(boolean) 对我不起作用。还有其他方法吗?

编辑:这是我从您的示例代码中得出的结果,它复制了我的问题,尽管没有那么明显,因为绘图中进行的计算要少得多。如果快速滚动图像,您会发现在将六边形调整为新大小和将视口(viewport)调整为新位置之间存在短暂的停顿。

您可以看到六边形被重新绘制了两次。 (调用 setSize() 方法一次,调用 setViewPosition() 方法一次。)

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class ZoomWithSelectionInViewport implements MouseWheelListener{

private int zoom = 80;

JComponent b;
int hexSize = 3;

public ZoomWithSelectionInViewport() throws Exception{

    b = new JComponent() {

        private static final long serialVersionUID = 1L;

        @Override
        public Dimension getMinimumSize() {
            return new Dimension(700, 700);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = ((Graphics2D) g);

            int vertOffsetX, vertOffsetY, horizOffsetX, horizOffsetY;

            vertOffsetX = (int)((double)hexSize* Math.sqrt(3.0f));
            vertOffsetY = (int)((double)-hexSize-1* Math.sqrt(3.0f)/2.0f);

            horizOffsetX = (int) ((double)hexSize* Math.sqrt(3.0f));
            horizOffsetY = (int) ((double)hexSize+1* Math.sqrt(3.0f)/2.0f);

            for(int x = 0; x < 50; x++)
            {
                for(int y = 0; y < 50; y++)
                {
                    int[] xcoords = new int[6]; int[] ycoords = new int[6];
                     for(int i = 0; i < 6; i++)
                     {
                        xcoords[i] = (int)((hexSize+x * horizOffsetX + y * vertOffsetX) + (double)hexSize * Math.cos(i * 2 * Math.PI / 6));
                        ycoords[i] = (int)(((getSize().height /2 )+ x * horizOffsetY + y * vertOffsetY) + (double)hexSize * Math.sin(i * 2 * Math.PI / 6));
                     }
                     g2d.setStroke(new BasicStroke(hexSize/2.5f));
                     g2d.setColor(Color.GRAY);

                     g2d.drawPolygon(xcoords, ycoords, 6);
                }
            }

        }
    };
    JScrollPane view = new JScrollPane(b);

    b.addMouseWheelListener(this);
    JFrame f = new JFrame();
    f.setLocation(10, 10);
    f.setDefaultCloseOperation(3);
    f.add(view);
    f.setSize(500,500);
    f.setVisible(true);
    view.setWheelScrollingEnabled(false);
}

    public void mouseWheelMoved(MouseWheelEvent e) {
        zoom = 100*-Integer.signum(e.getWheelRotation());
        if(hexSize - Integer.signum(e.getWheelRotation()) > 0)
            hexSize-= Integer.signum(e.getWheelRotation());
        Dimension targetSize = new Dimension(b.getWidth()+zoom,b.getHeight()+zoom);
        b.setPreferredSize(targetSize);
        b.setSize(targetSize);

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JViewport tempView = (JViewport)b.getParent();
                tempView.setViewPosition(new Point(b.getWidth()/2,b.getHeight()/2));            
            }
        });

    }


public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            try {
                ZoomWithSelectionInViewport example = new ZoomWithSelectionInViewport();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}
}

最佳答案

我的好奇心,不知道发生了什么,你能用这个吗 SSCCE在那里添加您的问题并使用代码编辑您的问题

enter image description here enter image description here enter image description here enter image description here

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class ZoomWithSelectionInViewport {

    private Point startPoint = new Point(0, 0);
    private Point rectLocale = new Point();
    private Dimension rectSize = new Dimension();
    private int zoom = 80;
    private BufferedImage capture = null;
    private BufferedImage raw;

    public ZoomWithSelectionInViewport() throws Exception {
        raw = new Robot().createScreenCapture(new Rectangle(
                Toolkit.getDefaultToolkit().getScreenSize()));
        MouseBehavior behavior = new MouseBehavior();
        JPanel b = new JPanel() {

            private static final long serialVersionUID = 1L;

            @Override
            public Dimension getMinimumSize() {
                return new Dimension(500, 500);
            }

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

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = ((Graphics2D) g);
                g2d.drawImage(raw, 0, 0, null);
                if (capture != null) {
                    int width2 = (int) (rectSize.width + rectSize.width * (zoom / 500d));
                    int height2 = (int) (rectSize.height + rectSize.height * (zoom / 500d));
                    int x2 = rectLocale.x - ((width2 - rectSize.width) / 2);
                    int y2 = rectLocale.y - ((height2 - rectSize.height) / 2);
                    Image scaledInstance = capture.getScaledInstance(
                            width2, height2, Image.SCALE_AREA_AVERAGING);
                    g2d.drawImage(scaledInstance, x2, y2, null);
                    g2d.drawRect(x2, y2, width2, height2);
                } else {
                    g2d.draw(new Rectangle(rectLocale, rectSize));
                }
            }
        };
        b.addMouseMotionListener(behavior);
        b.addMouseListener(behavior);
        b.addMouseWheelListener(behavior);
        JFrame f = new JFrame();
        f.setLocation(10, 10);
        f.setDefaultCloseOperation(3);
        f.add(b);
        f.pack();
        f.setVisible(true);
    }

    private class MouseBehavior extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            startPoint = e.getPoint();
            rectLocale = new Point();
            rectSize = new Dimension();
            capture = null;
            if (e.getSource() instanceof JComponent) {
                ((JComponent) e.getSource()).repaint();
            }
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            Point currentPoint = e.getPoint();
            rectSize.width = Math.abs(currentPoint.x - startPoint.x);
            rectSize.height = Math.abs(currentPoint.y - startPoint.y);
            if (e.isShiftDown()) {
                rectSize.width = rectSize.height = Math.min(rectSize.width, rectSize.height);
                int dx = startPoint.x - rectSize.width;
                int dy = startPoint.y - rectSize.height;
                rectLocale.x = startPoint.x < currentPoint.x ? startPoint.x : Math.max(dx, dy);
                rectLocale.y = startPoint.y < currentPoint.y ? startPoint.y : Math.min(dx, dy);
            } else {
                rectLocale.x = Math.min(currentPoint.x, startPoint.x);
                rectLocale.y = Math.min(currentPoint.y, startPoint.y);
            }
            if (e.getSource() instanceof JComponent) {
                ((JComponent) e.getSource()).repaint();
            }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (rectSize.width <= 0 || rectSize.height <= 0) {
                capture = null;
            } else {
                capture = raw.getSubimage(Math.max(0, rectLocale.x),
                        Math.max(0, rectLocale.y), rectSize.width, rectSize.height);
            }
            if (e.getSource() instanceof JComponent) {
                ((JComponent) e.getSource()).repaint();
            }
        }

        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {
            zoom = Math.min(2000, Math.max(0, zoom + e.getUnitsToScroll() * 10));
            if (e.getSource() instanceof JComponent) {
                ((JComponent) e.getSource()).repaint();
            }
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                try {
                    ZoomWithSelectionInViewport example = new ZoomWithSelectionInViewport();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

另一种可能是

import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.awt.geom.*;

public class ZoomDemo {

    private PaintSurface canvas = new PaintSurface();
    private JFrame frame = new JFrame();
    private AffineTransform aT = new AffineTransform();
    private Point2D p1 = null;
    private Point2D p2 = null;

    public ZoomDemo() {
        frame.setSize(500, 500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        ScaleListener scaleListener = new ScaleListener();
        canvas.addMouseWheelListener(scaleListener);
        canvas.addMouseListener(scaleListener);    
        frame.add(canvas);
        frame.setVisible(true);
    }

    public class ScaleListener extends MouseAdapter {

        private double scale = 1;

        @Override
        public void mouseClicked(MouseEvent e) {
            p1 = e.getPoint();
            try {
                p2 = aT.inverseTransform(p1, new Point2D.Double());
                /*
                 * p1 is the point relative to canvas where the user physically
                 * held the mouse.
                 *
                 * Since you may want to deal with a virtual mouse location
                 * relative to an untransformed canvas, you inverse transform p1
                 * to p2.
                 *
                 * For example: when the user held the mouse over, let's say,
                 * the displayed left upper corner of the red rectangle.
                 *
                 * p2 now will point to the upper left corner of the red
                 * rectangle in an untransformed canvas.
                 */
                applyScale();
            } catch (NoninvertibleTransformException e1) {
                e1.printStackTrace();
            }
            canvas.repaint();
        }

        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {
            if (p1 != null && p2 != null) {
                scale -= (0.05 * e.getWheelRotation());
                if (scale > 5) {
                    scale = 5;
                }
                if (scale < 1) {
                    scale = 1;
                    aT.setToIdentity();
                } else {
                    applyScale();
                }
                canvas.repaint();
            }
        }

        private void applyScale() {
            aT.setToIdentity();
            // *** variation one (your implementation)
            aT.translate(p1.getX(), p1.getY());
            aT.scale(scale, scale);
            aT.translate(-p2.getX(), -p2.getY());
            // *** variation two
            // aT.translate(p1.getX(), p1.getY());
            // aT.scale(scale, scale);
            // aT.translate(-p1.getX(), -p1.getY());
            // *** variation three
            // aT.translate(p2.getX(), p2.getY());
            // aT.scale(scale, scale);
            // aT.translate(-p2.getX(), -p2.getY());
        }
    }

    public class PaintSurface extends JComponent {

        private static final long serialVersionUID = 1L;

        {
            this.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        }

        /*
         * Override paintComponent, not paint!!!
         */
        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g.create();
            try {
                g2.setColor(Color.black);
                g2.fillRect(0, 0, getWidth(), getHeight());
                // g2.setTransform(aT); <<<<<<<<< !!!!!!!
        /*
                 * A transform (translation for example) may already have been
                 * applied to the Graphics object by a parent. This is removed
                 * by setTransform.
                 */
                g2.transform(aT); // <<<<<<<<<< !!!!!!!
                g2.setColor(Color.red);
                g2.drawRect(50, 50, 100, 100);
                g2.setColor(Color.blue);
                g2.drawRect(200, 200, 150, 50);
                if (p2 != null) {
                    g2.setColor(Color.green);
                    g2.fill(new Rectangle2D.Double(p2.getX() - 4, p2.getY() - 4, 8, 8));
                }
            } finally {
                g2.dispose();
            }
        }
    }

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

            @Override
            public void run() {
                ZoomDemo zoomDemo = new ZoomDemo();
            }
        });
    }
}

同样的问题,

import java.awt.BorderLayout;
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.RenderingHints;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;

//http://stackoverflow.com/questions/6819243/jscrollpane-jumping-when-scrollbars-start-being-used

public class LockViewPortToPoint extends JFrame {

    private static final long serialVersionUID = 1L;

    public static void main(String[] arg) {
        LockViewPortToPoint lockViewPortToPoint = new LockViewPortToPoint();
    }

    public LockViewPortToPoint() {
        initComponents();
        setVisible(true);
    }

    private void initComponents() {
        setLayout(new BorderLayout());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(600, 600);
        setPreferredSize(new Dimension(600, 600));
        add(new TopPanel());
    }

    private class TopPanel extends JPanel {

        private static final long serialVersionUID = 1L;
        private JScrollPane scrollPane;

        TopPanel() {
            setPreferredSize(new Dimension(500, 500));
            scrollPane = new JScrollPane(new InteriorPanel());
            scrollPane.setPreferredSize(new Dimension(500, 500));
            scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(10, 490));
            scrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(490, 10));
            scrollPane.setWheelScrollingEnabled(false);
            scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
            scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            add(scrollPane);
        }
    }

    private class InteriorPanel extends JPanel {

        private static final long serialVersionUID = 1L;
        private double scale = 10.0;
        private final double scaleModifier = 0.1;
        private final int width = 10;
        private Point loc = new Point(0, 0);
        private final int SIZE = 10;
        private Point orig = new Point(250, 250);

        InteriorPanel() {
            super(true);
            setPreferredSize(new Dimension((int) (scale * width * SIZE), (int) (scale * width * SIZE)));
            this.addMouseWheelListener(new MapMouseWheelListener());
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2D = (Graphics2D) g;
            g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2D.scale(scale, scale);
            for (int row = 0; row <= SIZE; row++) {
                for (int col = 0; col < SIZE; col++) {
                    if ((col + row) % 2 == 0) {
                        g2D.setColor(Color.white);
                    } else {
                        g2D.setColor(Color.black);
                    }
                    g2D.fillRect(col * width, row * width, width, width);
                }
            }
        }

        private void incrementScale(int notches) {
            double modifier = 0;
            final double prevScale = scale;
            if (notches != 0) {
                modifier = 1.0 + -notches / Math.abs(notches) * scaleModifier;
            }
            scale *= Math.pow(modifier, Math.abs(notches));
            /*if (scale * width < 1) {
            scale = 1.0/width;
            } else if (scale * width * 3 > parentHeight || scale * width * 3 > parentWidth) {
            if (parentHeight > parentWidth) {
            scale = parentWidth / 3.0 / width;
            } else {
            scale = parentHeight / 3.0 / width;
            }
            } else if (scale * width * SIZE < parentWidth) {
            scale = parentWidth / (double)SIZE / width;
            } else if (scale * width * SIZE < parentHeight) {
            scale = parentHeight / (double)SIZE / width;
            }*/
            setPreferredSize(new Dimension((int) (scale * width * SIZE), (int) (scale * width * SIZE)));
            orig = new Point(((int) (scale * width * SIZE)) / 2, ((int) (scale * width * SIZE) / 2));
            final JViewport viewport = ((JViewport) (getParent().getParent().getComponent(0)));
            javax.swing.SwingUtilities.invokeLater(new Runnable() {

                public void run() {
                    viewport.setViewPosition(new Point(
                            orig.x - (int) Math.round(loc.x * (1 - scale / prevScale)),
                            orig.y - (int) Math.round(loc.y * (1 - scale / prevScale))));
                }
            });
            /*viewport.scrollRectToVisible(new Rectangle(new Point(
            orig.x - (int) Math.round(loc.x * (1 - scale / prevScale)),
            orig.y - (int) Math.round(loc.y * (1 - scale / prevScale))))); */
            System.out.println(orig + "\n  " + loc + "\n  " + (1 - scale / prevScale));
            revalidate();
            repaint();
        }

        private class MapMouseWheelListener implements MouseWheelListener {

            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                loc = e.getPoint();
                incrementScale(e.getWheelRotation());
            }
        }
    }
}

另一个例子

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

//http://stackoverflow.com/questions/115103/how-do-you-implement-position-sensitive-zooming-inside-a-jscrollpane
public class FPanel extends javax.swing.JPanel {

    private static final long serialVersionUID = 1L;
    private Dimension preferredSize = new Dimension(400, 400);
    private Rectangle2D[] rects = new Rectangle2D[50];

    public static void main(String[] args) {
        JFrame jf = new JFrame("test");
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setSize(400, 400);
        jf.add(new JScrollPane(new FPanel()));
        jf.setVisible(true);
    }

    public FPanel() {
        // generate rectangles with pseudo-random coords
        for (int i = 0; i < rects.length; i++) {
            rects[i] = new Rectangle2D.Double(
                    Math.random() * .8, Math.random() * .8,
                    Math.random() * .2, Math.random() * .2);
        }
        // mouse listener to detect scrollwheel events
        addMouseWheelListener(new MouseWheelListener() {

            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                updatePreferredSize(e.getWheelRotation(), e.getPoint());
            }
        });
    }

    private void updatePreferredSize(int wheelRotation, Point stablePoint) {
        double scaleFactor = findScaleFactor(wheelRotation);
        scaleBy(scaleFactor);
        Point offset = findOffset(stablePoint, scaleFactor);
        offsetBy(offset);
        getParent().doLayout();
        revalidate();
        repaint();
    }

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

    private void scaleBy(double scaleFactor) {
        int w = (int) (getWidth() * scaleFactor);
        int h = (int) (getHeight() * scaleFactor);
        preferredSize.setSize(w, h);
    }

    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 = getLocation();
        setLocation(location.x - offset.x, location.y - offset.y);
    }

    @Override
    public Dimension getPreferredSize() {
        return preferredSize;
    }
    private Rectangle2D r = new Rectangle2D.Float();

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.red);
        int w = getWidth();
        int h = getHeight();
        for (Rectangle2D rect : rects) {
            r.setRect(rect.getX() * w, rect.getY() * h,
                    rect.getWidth() * w, rect.getHeight() * h);
            ((Graphics2D) g).draw(r);
        }
    }
}

关于Java:在不重新绘制的情况下调整组件大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8873525/

相关文章:

java - RestRepositoryResource 的自动配置

java - 套接字不发送数据

java - 如何在 JTextPane 中将每个字符设置为不同的颜色/背景颜色?

java - 将 JLabel 的二维数组打印到 GridLayout

java - 如何使用按钮操作事件来调用其他类?

java - Spring: WebServiceTransportException: Not Found [404] 对于非常简单的 WS 客户端

java - 如何在 Controller 中处理同一页面上的两个表单来执行不同的任务和端点? if语句的格式是什么?

java - 如何使用 servlet 响应打开新选项卡?

java.net.SocketException : permission denied is arising

java - 具有许多 ActionListeners 的 MVC 模式