Frage

Ich versuche, positionsempfindliche Zoom innerhalb eines JScrollPane zu implementieren. Die JScrollPane enthält eine Komponente mit einem angepassten paint, die in mich ziehen werden, was auch immer Raum zugeordnet wird -. So Zoomen so einfach ist es, ein MouseWheelListener als verwenden, die die innere Komponente paßt die Größe je nach Bedarf

Aber ich mag auch das Zoomen in (oder aus) einem Punkt, Punkt zu halten, so zentral wie möglich innerhalb des resultierenden gezoomt-in (oder -out) sehen (das ist, was ich als ‚positionsempfindlichen‘ Zoomen ), ähnlich wie Arbeiten in google Maps zoomen. Ich bin sicher, dass dies schon viele Male zuvor getan wurde - weiß jemand, den „richtigen“ Weg, um es unter Java Swing zu tun ?. Wäre es besser, mit Graphic2D Verwandlungen zu spielen, anstatt JScrollPanes verwenden?

Beispielcode folgt:

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);
    }       
  }
}
War es hilfreich?

Lösung

Diese Getestet, scheint zu funktionieren ...

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

Aktualisieren

Hier ist eine Erklärung: der Punkt p zum FPanel die Position der Maus relativ ist. Da Sie die Größe des Panels werden die Skalierung wird die Lage von p (bezogen auf die Größe der Platte) um den gleichen Faktor skaliert werden. Durch den aktuellen Standort aus der skalierten Position subtrahiert, erhalten Sie wie viel der Punkt ‚verschiebt‘, wenn das Panel der Größe verändert wird. Dann ist es nur eine Frage der Verschiebung der Plattenstelle im Bildlauffenster um den gleichen Betrag in der entgegengesetzten Richtung p zurück unter dem Mauszeiger zu setzen.

Andere Tipps

Hier ist eine kleine Refactoring von @ Kevin K-Lösung:

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

Ihre MouseWheelListener haben auch die Cursor zu finden, um es in der Mitte des JScrollPane bewegen und die xmin / ymin und xmax / ymax des Inhalts zu betracht anzupassen.

Ich denke, smt wie dies sollte arbeiten ...


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

EDIT: Changed ein paar Dinge.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top