2012-10-04 12 views
5

Sto cercando di creare una mappa zoomabile con Swing. La mappa è un JPanel in un JScrollPane. Quando ingrandito, la mappa cambia dimensione e paint() dipinge gli elementi in una posizione diversa. Tutto funziona alla grande.Come ingrandire JPanel senza spostare il centro: matematica o swing?

Tuttavia, ScrollPane non ha modificato la vista aumentando le dimensioni dell'immagine, pertanto lo zoom in avanti ha spostato sempre gli elementi che stavo osservando dallo schermo. Ho provato a risolverlo con scrollRectToVisible(), ma non riesco a ottenere le coordinate giuste per il rettangolo, perché non riesco a fare la geometria o perché non capisco perfettamente Swing.

Ecco quello che ho:

public class MapPanel extends JPanel { 
    [...] 

public void setZoom(double zoom) { 
    // get the current viewport rectangle and its center in the scaled coordinate system 
    JViewport vp = (JViewport) this.getParent(); 
    Rectangle rect = vp.getViewRect(); 
    Point middle = getMiddle(rect); 
    Dimension dim = rect.getSize(); 

    // zoom in 
    scaler.setZoom(zoom); 
    setPreferredSize(scaler.transform(dim));  
    this.revalidate(); 

// calculate the full size of the scaled coordinate system 
    Dimension fullDim = scaler.transform(dim); 
    // calculate the non-scaled center of the viewport 
    Point nMiddle = new Point((int) ((double) (middle.x)/fullDim.width*dim.width),(int) ((double) (middle.y)/fullDim.height*dim.height)); 

    // this should do the trick, but is always a bit off towards the origin 
    scrollRectToVisible(getRectangleAroundPoint(nMiddle)); 

    // the below alternative always zooms in perfectly to the center of the map 
    // scrollRectToVisible(getRectangleAroundPoint(new Point(400,300))); 
} 

private Rectangle getRectangleAroundPoint(Point p){ 
    Point newP = scaler.transform(p); 
    Dimension d = railMap.getDimension(); 
    Point corner = new Point(newP.x-d.width/2,newP.y-d.height/2); 
    return new Rectangle(corner,d); 
} 

private Point getMiddle(Rectangle r){ 
    return new Point(r.x+r.width/2,r.y+r.height/2); 
} 
} 

Ed ecco la classe Scaler (che non fa nulla sorprendente, credo):

public class Scaler { 
    private double zoom = 1; 

public void setZoom(double zoom) { 
    this.zoom = zoom; 
} 


public Point transform(Point2D p){ 
    return new Point((int) (p.getX()*zoom), (int) (p.getY()*zoom)); 
} 


public Dimension transform(Dimension d){ 
    return new Dimension((int) (d.width*zoom), (int) (d.height*zoom)); 
} 

} 

Chi può dirmi dove le cose stanno andando male? Mi sembra che abbia fatto un calcolo valido del centro corrente della mappa e con un punto di zoom fisso funziona ...

Modifica: quindi la cosa difficile qui è creare il nuovo rettangolo di visualizzazione basato sul vecchio rettangolo di visualizzazione.

+0

railMap? Non lavoreresti su qualcosa come JMRI, vero? ;-) – geowar

risposta

5

ho appena fatto questo esempio molto veloce, che cerca in sostanza di mantenere il centro riquadro di scorrimento intorno al centro dell'immagine fornita

public class TestZooming { 

    public static void main(String[] args) { 
     new TestZooming(); 
    } 

    public TestZooming() { 

     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 

       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException classNotFoundException) { 
       } catch (InstantiationException instantiationException) { 
       } catch (IllegalAccessException illegalAccessException) { 
       } catch (UnsupportedLookAndFeelException unsupportedLookAndFeelException) { 
       } 

       JFrame frame = new JFrame(); 
       frame = new JFrame(); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setSize(400, 400); 
       frame.setLocationRelativeTo(null); 
       frame.setLayout(new BorderLayout()); 
       final ZoomPane pane = new ZoomPane(); 
       frame.add(new JScrollPane(pane)); 
       frame.setVisible(true); 

       SwingUtilities.invokeLater(new Runnable() { 

        @Override 
        public void run() { 
         pane.centerInViewport(); 
        } 

       }); 

      } 
     }); 

    } 

    protected class ZoomPane extends JPanel { 

     private Image background; 
     private Image scaled; 
     private float zoom = 1f; 

     private Dimension scaledSize; 
     private JViewport con; 

     public ZoomPane() { 

      try { 
       background = ImageIO.read(new File("...")); 
       scaled = background; 
       scaledSize = new Dimension(background.getWidth(this), background.getHeight(this)); 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
      } 

      InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); 
      ActionMap am = getActionMap(); 

      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PLUS, 0), "plus"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.SHIFT_DOWN_MASK), "plus"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, 0), "minus"); 

      am.put("plus", new AbstractAction() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        setZoom(getZoom() + 0.1f); 
       } 
      }); 
      am.put("minus", new AbstractAction() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        setZoom(getZoom() - 0.1f); 
       } 
      }); 

      setFocusable(true); 
      requestFocusInWindow(); 

     } 

     @Override 
     public void addNotify() { 

      super.addNotify(); 

     } 

     public float getZoom() { 
      return zoom; 
     } 

     public void setZoom(float value) { 
      if (zoom != value) { 
       zoom = value; 

       if (zoom < 0) { 
        zoom = 0f; 
       } 

       int width = (int) Math.floor(background.getWidth(this) * zoom); 
       int height = (int) Math.floor(background.getHeight(this) * zoom); 
       scaled = background.getScaledInstance(width, height, Image.SCALE_SMOOTH); 
       scaledSize = new Dimension(width, height); 

       if (getParent() instanceof JViewport) { 

        int centerX = width/2; 
        int centerY = height/2; 

        JViewport parent = (JViewport) getParent(); 
        Rectangle viewRect = parent.getViewRect(); 
        viewRect.x = centerX - (viewRect.width/2); 
        viewRect.y = centerY - (viewRect.height/2); 
        scrollRectToVisible(viewRect); 
       } 

       invalidate(); 
       repaint(); 

      } 
     } 

     @Override 
     public Dimension getPreferredSize() { 

      return scaledSize; 

     } 

     @Override 
     protected void paintComponent(Graphics g) { 

      super.paintComponent(g); 

      if (scaled != null) { 

       g.drawImage(scaled, 0, 0, this); 

      } 

     } 

     protected void centerInViewport() { 

      Container container = getParent(); 
      if (container instanceof JViewport) { 

       JViewport port = (JViewport) container; 
       Rectangle viewRect = port.getViewRect(); 

       int width = getWidth(); 
       int height = getHeight(); 

       viewRect.x = (width - viewRect.width)/2; 
       viewRect.y = (height - viewRect.height)/2; 

       scrollRectToVisible(viewRect); 

      } 

     } 
    } 
} 

Per quanto riguarda il motivo per cui il vostro non funziona, non posso dire, Non riesco ad eseguire l'esempio, ma forse questo almeno ti darà qualche idea ...

+0

Grazie. Tuttavia, se ho capito bene, ingrandisci sempre al centro dell'immagine. Questo ha funzionato per me. Ciò che non era il calcolo del centro del punto di vista corrente e lo zoom in quel centro. Pensi che saresti in grado di adattare il tuo esempio a questo? Penso che dovrebbe assomigliare molto alla funzione centerInViewport(), ad eccezione di 'viewRect.x' e' .y' dovrebbe essere calcolato in modo leggermente diverso. Penso anche di sapere come (vedi il mio esempio), eccetto che non funziona ... – roelandvanbeek

+0

Non importa, l'ho risolto con alcuni suggerimenti del tuo esempio. Pubblicherà la mia soluzione per completezza non appena le otto ore sono finite. – roelandvanbeek

1

Risolto. Sìì. Ancora non sono sicuro di dove sia andato male, ma spostare il rettangolo originale (grazie a @MadProgrammer) piuttosto che crearne uno nuovo e correggere l'arrotondamento dello scaler potrebbe aver fatto il trucco.

private Point getViewportCenter() { 
    JViewport vp = (JViewport) this.getParent(); 
    Point p = vp.getViewPosition(); 
    return new Point(p.x+vp.getWidth()/2,p.y+vp.getHeight()/2); 
} 

private void setViewportCenter(Point p) { 
    JViewport vp = (JViewport) this.getParent(); 
    Rectangle viewRect = vp.getViewRect(); 

    viewRect.x = p.x - viewRect.width/2; 
    viewRect.y = p.y - viewRect.height/2; 

    scrollRectToVisible(viewRect); 
} 

public void setZoom(double zoom) { 
    // determine unscaled center and dimensions 
    Point oCenter = scaler.inverseTransform(getViewportCenter()); 
    Dimension dim = railMap.getDimension(); 

    // zoom 
    scaler.setZoom(zoom); 

    // fix size and viewport 
    setPreferredSize(scaler.transform(dim)); 
    setViewportCenter(scaler.transform(oCenter)); // should be a transformed point 

    // finish 
    invalidate(); 
    repaint(); 
} 
Problemi correlati