2008-09-22 54 views
10

我正試圖在JScrollPane內實現位置敏感縮放。 JScrollPane包含一個具有自定義paint的組件,該組件將在分配的任何空間內自行繪製,因此縮放與使用MouseWheelListener一樣容易,可根據需要調整內部組件的大小。如何在JScrollPane中實現位置敏感縮放?

但是我也想放大(或縮小)一個點,以便在生成的放大(或退出)視圖中將該點保持爲中心點(這就是我所說的「位置敏感」縮放),類似於縮放在Google地圖中的工作方式。我確信之前已經做過很多次了 - 是否有人知道在Java Swing下做「正確」的方法?玩Graphic2D的改造而不是使用JScrollPanes會更好嗎?

示例代碼如下:

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

新增賞金,看看我是否能得到一個完整的答案(理想:該代碼段,上述添加時,回答了這個問題)。 – tucuxi 2010-04-06 23:23:01

回答

8

測試這一點,似乎工作...

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

更新

這裏有一個解釋:點p是鼠標相對於FPanel的位置。由於您要縮放面板的尺寸,所以p的位置(相對於面板的尺寸而定)將按相同的因子縮放。通過從縮放位置中減去當前位置,可以在面板調整大小時獲得點「轉換」的大小。然後,只需要將滾動窗格中的面板位置以相反的方向移動相同的數量,以將p放回鼠標光標下。

1

你的MouseWheelListener還具有將光標定位,將其移動到JScrollPane的中心並調節XMIN/YMIN和要觀看的內容的XMAX/YMAX。

+0

是的,那是真的 - 這也正是我想要做的第一件事(光標位於'p',並正確調整這些邊界是我不知道該怎麼做)。 – tucuxi 2010-04-08 13:50:55

1

我覺得像這樣的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(); 
} 

編輯: 改變了一些事情。

+0

即使在將`int newTop = py - w/2;`更改爲`int newTop ='後,您的代碼不會在結果視圖中保持點'p'居中(例如,嘗試縮放到右下角附近) py - h/2;`。在提出答案之前,請測試代碼。 – tucuxi 2010-04-08 13:48:52

+0

更新後的代碼似乎放大到左上角(XP上的java 6),而不是鼠標指針指向的任何位置。我正在使用問題中列出的相同測試程序。 – tucuxi 2010-04-08 21:43:20

2

這裏是@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); 
}