2008-09-22 42 views
10

JScrollPane içine konum duyarlı yakınlaştırma uygulamak çalışıyorum. JScrollPane, kendisine ayrılan alanın içine kendini çekecek özelleştirilmiş bir paint ürününe sahip bir bileşen içerir; böylece yakınlaştırma, iç bileşeni gerektiği gibi yeniden boyutlandıran bir MouseWheelListener kullanmak kadar kolaydır.JScrollPane içinde konuma duyarlı yakınlaştırma nasıl yapılır?

Ayrıca, bu noktaya yakınlaştırılmış (veya -out) görünümde bu noktayı olabildiğince merkezi tutmak için bir noktaya yakınlaştırmak veya uzaklaştırmak istiyorum (bu, 'konum duyarlı' olarak adlandırıyorum) yakınlaştırma, google haritalarda yakınlaştırma işlemine benzer. Bunun daha önce birçok kez yapıldığından eminim - Java Swing'in altında "doğru" yolu bilen var mı? JScrollPanes yerine Graphic2D 's dönüşümleri ile oynamak daha iyi olur mu?

Örnek kod aşağıda:

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

Eklendi kelle görmek için (ideal: Yukarıdaki kod eklendiğinde, soru cevaplar, o pasajı). – tucuxi

cevap

8

, İşte

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

Güncelleme

... iş gibi görünüyor bir açıklaması var: nokta pFPanel fare göreli konumudur. Panelin boyutunu ölçeklediğiniz için, p (panelin boyutuna göre) konumu aynı faktöre göre ölçeklenecektir. Geçerli konumu ölçeklendirilmiş konumdan çıkararak, panel yeniden boyutlandırıldığında ne kadar noktaya geçiş yaparsınız. Ardından, kaydırma bölmesindeki panel konumunu, fare imlecinin altına geri p geri koymak için ters yönde aynı miktarda kaydırmak meselesidir.

+0

Tam olarak aradığım şey. Teşekkürler! – tucuxi

1

Sizin MouseWheelListener da, imlecin yerini JScrollPane merkezine taşımak ve xmin/ymin ayarlamak zorundadır ve içeriğin xmax/ymax görüntülenmesine. birkaç şey değişti:

+0

Evet, bu doğru - aynı zamanda ilk etapta yapmaya çalıştığım şey de (imleç 'p' konumunda bulunuyor ve bu sınırları doğru bir şekilde ayarlamak, ne yapacağımı bilmiyorum). – tucuxi

1

böyle smt


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

DÜZENLEME

... çalışma gerektiğini düşünüyorum. Bu test

+0

Kodunuz "p" noktasını ortaya çıkarılan görünümde ortalamıyor (bir örnek için, sağ alt köşeye yakın bir yakınlaştırma yapmayı deneyin), int sonra "int newTop = py - w/2;" - int newTop = py - h/2; '. Lütfen cevap olarak teklif etmeden önce kodu test edin. – tucuxi

+0

Güncellenen kod, fare imlecinin işaret ettiği yere değil, sol üst köşeye (XP'de java 6) zoom yapıyor gibi görünüyor. Soruda listelenen aynı test programını kullanıyorum. – tucuxi

2

İşte @Kevin K'nin çözümün küçük üstlenmeden var: Ben tam cevap alabilirsiniz eğer

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); 
} 
İlgili konular