2008-09-22 4 views
10

Я пытаюсь реализовать позиционируемое масштабирование внутри JScrollPane. JScrollPane содержит компонент с настраиваемым paint, который будет рисовать себя в любом пространстве, которое он выделяет, поэтому масштабирование так же просто, как с использованием MouseWheelListener, который изменяет размер внутреннего компонента по мере необходимости.Как реализовать позиционное масштабирование внутри JScrollPane?

Но я также хочу масштабировать (или выходить) точку, чтобы сохранить эту точку как можно более центральной в результате масштабированного (или -out) представления (это то, что я называю «позиционно-чувствительным», масштабирование), подобно тому, как масштабирование работает на картах 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(); 
} 

Update

Вот объяснение: точка p является расположение мыши относительно FPanel. Поскольку вы масштабируете размер панели, расположение p (относительно размера панели) будет масштабироваться по тому же коэффициенту. Вычитая текущее местоположение из масштабированного местоположения, вы получаете, сколько точек сдвигается при изменении размера панели. Тогда просто переместить расположение панели в панели прокрутки на ту же величину в противоположном направлении, чтобы вернуть p под курсором мыши.

1

Ваш MouseWheelListener также должен найти курсор, переместить его в центр JScrollPane и настроить XMIN/Ymin и Xmax/утах контента для просмотра.

+0

Да, это правда - это то, что я пытался сделать в первую очередь (курсор находится на 'p', и правильная настройка этих границ - это то, что я не знаю, как это сделать). – tucuxi 2010-04-08 13:50:55

1

Я думаю, смт, как это должно работать ...


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: Изменил пару вещей.

+0

Ваш код не удерживает точку «p» с центром в результирующем представлении (например, попробуйте масштабировать около нижнего правого угла), даже после изменения «int newTop = py - w/2;» на «int newTop = py - h/2; `. Пожалуйста, проверьте код, прежде чем предлагать его в качестве ответа. – tucuxi 2010-04-08 13:48:52

+0

Обновленный код, по-видимому, приближается к верхнему левому углу (java 6 на XP), а не к тому, куда указывает указатель мыши. Я использую ту же программу тестирования, указанную в вопросе. – tucuxi 2010-04-08 21:43:20

2

Вот небольшая рефакторинга решения @Kevin Д.К.:

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); 
} 
Смежные вопросы