texto ejemplo como agregar java swing user-interface zoom

java - ejemplo - ¿Cómo se implementa el zoom sensible a la posición dentro de un JScrollPane?



scrollbar java ejemplo (4)

Estoy tratando de implementar el zoom sensible a la posición dentro de un JScrollPane . El JScrollPane contiene un componente con una paint personalizada que se dibujará dentro de cualquier espacio que esté asignado, por lo que el zoom es tan fácil como usar un MouseWheelListener que redimensiona el componente interno según sea necesario.

Pero también quiero acercarme (o alejarme) de un punto para mantener ese punto lo más central posible dentro de la vista ampliada (o salida) resultante (esto es a lo que me refiero como zoom ''sensible a la posición''), similar a cómo funciona el zoom en google maps. Estoy seguro de que esto ya se ha hecho muchas veces antes: ¿alguien sabe la forma "correcta" de hacerlo en Java Swing ?. ¿Sería mejor jugar con las transformaciones de Graphic2D lugar de usar JScrollPanes ?

El código de muestra sigue:

package test; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; public class FPanel extends javax.swing.JPanel { 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() { public void mouseWheelMoved(MouseWheelEvent e) { updatePreferredSize(e.getWheelRotation(), e.getPoint()); } }); } private void updatePreferredSize(int n, Point p) { double d = (double) n * 1.08; d = (n > 0) ? 1 / d : -d; int w = (int) (getWidth() * d); int h = (int) (getHeight() * d); preferredSize.setSize(w, h); getParent().doLayout(); // Question: how do I keep ''p'' centered in the resulting view? } public Dimension getPreferredSize() { return preferredSize; } private Rectangle2D r = new Rectangle2D.Float(); public void paint(Graphics g) { super.paint(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); } } }


Su MouseWheelListener también tiene que ubicar el cursor, moverlo al centro de JScrollPane y ajustar xmin / ymin y xmax / ymax del contenido que se va a visualizar.


Creo que SMT debería estar funcionando ...

private void updatePreferredSize(int n, Point p) { double d = (double) n * 1.08; d = (n > 0) ? 1 / d : -d; int w = (int) (getWidth() * d); int h = (int) (getHeight() * d); preferredSize.setSize(w, h); // Question: how do I keep ''p'' centered in the resulting view? int parentWdt = this.getParent( ).getWidth( ) ; int parentHgt = this.getParent( ).getHeight( ) ; int newLeft = p.getLocation( ).x - ( p.x - ( parentWdt / 2 ) ) ; int newTop = p.getLocation( ).y - ( p.y - ( parentHgt / 2 ) ) ; this.setLocation( newLeft, newTop ) ; getParent().doLayout(); }

EDITAR: Cambió un par de cosas.


Aquí hay una refactorización menor de la solución de @Kevin K:

private void updatePreferredSize(int wheelRotation, Point stablePoint) { double scaleFactor = findScaleFactor(wheelRotation); scaleBy(scaleFactor); Point offset = findOffset(stablePoint, scaleFactor); offsetBy(offset); 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) (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); }


Probado esto, parece funcionar ...

private void updatePreferredSize(int n, Point p) { double d = (double) n * 1.08; d = (n > 0) ? 1 / d : -d; int w = (int) (getWidth() * d); int h = (int) (getHeight() * d); preferredSize.setSize(w, h); int offX = (int)(p.x * d) - p.x; int offY = (int)(p.y * d) - p.y; setLocation(getLocation().x-offX,getLocation().y-offY); getParent().doLayout(); }

Actualizar

Aquí hay una explicación: el punto p es la ubicación del mouse en relación con FPanel . Como está escalando el tamaño del panel, la ubicación de p (en relación con el tamaño del panel) se escalará por el mismo factor. Al restar la ubicación actual de la ubicación escalada, obtienes cuánto cambia el punto cuando se cambia el tamaño del panel. Entonces es simplemente una cuestión de desplazar la ubicación del panel en el panel de desplazamiento en la misma cantidad en la dirección opuesta para poner p nuevo debajo del cursor del mouse.