Zoomfunktion auf JPanel mittels JViewport


#1

Hiho,

ich habe ein JPanel mit NullLayout auf dem ich JLabels platziere und diese Labels mittels selbstgezeichneten Linien verbinde (die Linien werden in paintComponent des JPanels gezeichnet).

Nun will ich eine Zoomfunktion realisieren. Der Benutzer klickt einen Button und es wird rein- oder rausgezoomt.
Eine Google-Recherche brachte mich auf JViewport, mit dem sowas leicht zu relasieren sein sollte.
Allerdings fand ich kein passendes Beispiel das mich weiterbrachte. Bin mir auch nicht sicher mit welchen Methoden ich den Viewport richtig verändere

Hier mal Beispielcode mit dem ichs versucht habe

zoomIn.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e){
				JViewport vp = scrollpane.getViewport();
				vp.setViewSize(new Dimension(getSize().width + zoomChange,
						getSize().height + zoomChange));
			}
		});

scrollpane ist ein JScrollPane auf dem sich das JPanel befindet in das hinein/herausgezoomt werden soll. Der obige Code steht im Konstruktor meiner JPanel-Klasse.
Nach der Initialisierung des JPanels führe ich

scrollpane.setViewportView(center);

aus, wobei center das JPanel ist


#2

Kannst du die Labels nicht auch selbst zeichnen? Dann ließe sich das einfacher zoomen.


#3

Sollen die Labels unabhängig vom Zoomfaktor immer gleich groß sein? Wenn nein, würde ich diese Labels durch eigenen Objekte ersetzen und selbst zeichnen.
Der Viewport ist doch nur ein Ausschnitt / eine Sicht auf eine Komponente vgl. mit dem Sucher einer Kamera, wenn man dessen Größe ändert, ändert man höchstens den sichtbaren Ausschnitt auf die Komponente.
Wenn dann muss man die Größe der Komponente im Viewport ändern. Das alleine reicht aber nicht aus. (Ist wie bei einem Teller auf einem Tisch, wenn man den Tisch auszieht, wird der Teller deswegen ja auch nicht größer…) Du musst zusätzlich noch die Postion und die Größe der angezeigten Objekte (egal ob Label oder selbst gezeichnet) neu berechnen - im Falle einer Komponente auf NullLayout müssen deren Attribute manipuliert werden, beim selbst zeichnen reicht es aus beim Zeichnen die Lage und Dimension anzupassen.


#4

[QUOTE=Crian;64279]Kannst du die Labels nicht auch selbst zeichnen? Dann ließe sich das einfacher zoomen.[/QUOTE]

Was meinst du damit genau? Dass ich anstatt Labels auf dem JPanel zu halten, die Labels bzw. deren Inhalt “von Hand” in der paintComponent() zeichne?

Ich dachte mir, dass ich mittels des Viewports das selber zeichnen sparen könnte und so die “native” Möglichkeit zu zoomen nutze.

Mein bisheriger Ansatz war so wie das [MENTION=1562]_Michael[/MENTION] angesprochen hat: Beim Zoomen die Bounds der Labels verändern und anschließend die Linien die die Labels verbinden neu berechnen und auch neu zeichnen. Was mir an der Stelle aber nicht klar ist, wie beim Reinzoomen dann das JScrollPane greift, also die Scrollbalken am rechten und unteren Rand erscheinen. Da muss ich doch dann wieder am Viewport rumspielen oder nicht?
Vllt sollte ich noch dazu sagen, dass die JPanels als einzige (center-) Komponente auf InternalFrames liegen.

*** Edit ***

Ich hab das jetzt mal so durchgezogen, also beim Reinzoomen Labels größer zeichnen, neu zeichnen, Start-/Endpunkte der Verbindungen neu berechnen, und auch diese neu zeichnen.
Dazu vergrößere ich auch die Size des JPanels damit das JScrollpane “anspringt” und mir die Scrollbalken zur Verfügung stellt.
Schaut bisher alles ganz gut aus.

2 Dinge muss ich aber noch ändern. Meine Buttons über die gezoomt wird befinden sich auch auf dem JPanel (mit nullLayout). Scrolle ich im reingezoomten JPanel sind die Buttons nicht mehr im Viewport
und
Position des Viewports beim Reinzoomen noch anpassen, im Moment zeigt der Viewport immer auf die Ecke links oben, da ja das JPanel nach rechts und nach unten ausgedehnt wird.

Sollte aber machbar sein


#5

Wie das mit Viewport gehen soll weis ich nicht, hier jedenfalls ein Demo wie ich das mal gelöst habe:

import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.net.MalformedURLException;
import java.net.URL;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;


public class ZoomDemo extends JFrame
{
	
	private JPanel contentPane;
	private JScrollPane scrollPane;
	private JPanel panel;
	private float zoomFaktor=1.5f;
	private JLabel lblHalloWelt;
	private JLabel lblImage;
	
	/**
	 * Launch the application.
	 */
	public static void main(String[] args)
	{
		EventQueue.invokeLater(new Runnable()
			{
				public void run()
				{
					try
					{
						ZoomDemo frame = new ZoomDemo();
						frame.setVisible(true);
					} catch (Exception e)
					{
						e.printStackTrace();
					}
				}
			});
	}
	
	/**
	 * Create the frame.
	 */
	public ZoomDemo()
	{
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		scrollPane = new JScrollPane();
		contentPane.add(scrollPane, BorderLayout.CENTER);
		
		panel = new JPanel()
		{
			@Override
			public void paintComponent(Graphics g)
			{
				super.paintComponent(g);
				Graphics2D g2=(Graphics2D)g;
				g2.scale(zoomFaktor,zoomFaktor);
			}
			
			public Dimension getPreferredSize()
			{
				Dimension d=super.getPreferredSize();
				d.height=(int) (d.height*zoomFaktor);
				d.width=(int) (d.width*zoomFaktor);
				return d;
			}
		};
		FlowLayout flowLayout = (FlowLayout) panel.getLayout();
		flowLayout.setAlignment(FlowLayout.LEFT);
		panel.setBorder(new LineBorder(new Color(0, 0, 0)));
		
		panel.addMouseWheelListener(new MouseWheelListener()
		{

			@Override
			public void mouseWheelMoved(MouseWheelEvent e)
			{
				Rectangle r=scrollPane.getViewport().getViewRect();
				//r.translate(e.getX()/2,e.getY()/2);
				
				zoomFaktor+=e.getPreciseWheelRotation()/10;
				panel.revalidate();
				System.out.println(r);
				
				scrollPane.getViewport().scrollRectToVisible(r);
			}
			
		});
		scrollPane.setViewportView(panel);
		
		lblHalloWelt = new JLabel("Hallo Welt");
		panel.add(lblHalloWelt);
		
		lblImage = new JLabel();
		try
		{
			lblImage.setIcon(new ImageIcon(new URL("http://www.java-forum.org/images/misc/java_forum_org.gif")));
		} catch (MalformedURLException e1)
		{
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		panel.add(lblImage);
	}
	
}

Wenn du übrigtens ein Null Layout in ein Scrollpane packen willst kann ich dir das DragLayout empfehlen.