0

I have created a panel with zoom in and out function but the scrollbar don't work, I mean when I zoom in I can't scroll to see the entire panel.
This is my code:

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JScrollPane;
import java.awt.Dimension;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

@SuppressWarnings("serial")
public class PanelWithZoom extends JPanel{

    private JButton btnZoomIn;
    private JButton btnZoomOut;
    private double scale = 1;
    private JPanel innerPanel;
    private JPanel outerPanel;
    public PanelWithZoom() {
        setLayout(new BorderLayout(0, 0));

        JPanel panel = new JPanel();
        add(panel, BorderLayout.NORTH);

        btnZoomIn = new JButton("zoom in");
        btnZoomIn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                scale = scale + 0.1;
                outerPanel.repaint();
                outerPanel.revalidate();
            }
        });
        panel.add(btnZoomIn);

        btnZoomOut = new JButton("zoom out");
        btnZoomOut.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                scale = scale - 0.1;
                outerPanel.repaint();
                outerPanel.revalidate();
            }
        });
        panel.add(btnZoomOut);

        JPanel panel_1 = new JPanel();
        add(panel_1, BorderLayout.WEST);

        JPanel panel_2 = new JPanel();
        add(panel_2, BorderLayout.SOUTH);

        JPanel panel_3 = new JPanel();
        add(panel_3, BorderLayout.EAST);

        outerPanel = new JPanel(){
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);

                Graphics2D g2 = (Graphics2D) g;             
                int w = (int) getWidth();
                int h = (int) getHeight();
                // Translate used to make sure scale is centered
                g2.translate(w/2, h/2);
                g2.scale(scale, scale);
                g2.translate(-w/2, -h/2);
            }


        };
        JScrollPane scroll = new JScrollPane(outerPanel);
        add(scroll, BorderLayout.CENTER);

        innerPanel = new JPanel(){
            @Override
            protected void paintComponent(Graphics g) {
                Graphics2D g2 = (Graphics2D) g;             
                g2.setColor(Color.white);
                g2.fillRect(0, 0, getWidth(), getHeight());
                g2.setColor(Color.black);
                g2.drawString("String for test", 10, 50);
                g2.drawRect(0, 0, getWidth()-1, getHeight()-1);
                setPreferredSize(new Dimension(595, 842));
            }

        };

        outerPanel.add(innerPanel);
    }


    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setContentPane(new PanelWithZoom());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.setVisible(true);
    }

}

you can test it to understand what I mean.

John
  • 2,820
  • 3
  • 30
  • 50
SlimenTN
  • 3,383
  • 7
  • 31
  • 78
  • You have to change the value returned from `getPreferredSize` of the component in the `JScrollPane` – MadProgrammer Aug 03 '15 at 11:55
  • sorry I don't understand you, where shoul I write that ? – SlimenTN Aug 03 '15 at 11:56
  • Use `Graphics#create` to make a copy of the properties of the `Graphics` context and then `Graphics#dispose` when you're done, it's safer when you're manipulating the translation/scale/transformation properties ;) – MadProgrammer Aug 03 '15 at 11:57
  • Sorry still don't understand u, can you please write a simple code to make this clear ? – SlimenTN Aug 03 '15 at 12:00
  • Think about this, in order for a scaling operation to have any meaning, you need to know the original size of what ever you are scaling. `JScrollPane` uses the `preferredSize` property of the component to determine whether it needs to display the scroll bars or not. When you can the scale of the `Graphics`, you are not effecting the physical properties of the component, so the `JScrollPane` doesn't know anything has changed. So you need to scale the "original" size of the component as well and trigger an update using `revalidate` and `repaint` on the scroll pane or viewport – MadProgrammer Aug 03 '15 at 12:05

1 Answers1

1

Override the getPreferredSize method of your component, return the size of the component based on the scale and original size of the component.

Because you want to apply scaling to the child components, you will probably want to override paint instead of paintComponent

outerPanel = new JPanel(){
    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g.create();             
        g2.scale(scale, scale);
        super.paint(g);
        g2.dispose()
    }
}    

BUT, Swing components can be painted independently of their parent containers, meaning the the paint method may not be called when the child component is painted...

The problem with this is, it won't scale the mouse events (or a bunch of other properties).

With an ever mounting list of issues, a better solution is to use something like JXLayer, as demonstrated here

Side notes:

  • Use Graphics#create to copy the properties of the Graphics context and Graphics#dispose, it's safer when dealing with translation/transformations/rendering hints and bunch of other properties, as any changes to the copy won't effect the original
  • Don't call any method from within any paint method which might directly or indirectly cause a repaint event to be triggered, you'll end up in a nasty never ending loop which will eventually consume your CPU

Graphics#create and #Dispose example...

outerPanel = new JPanel(){
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2 = (Graphics2D) g.create();             
        int w = (int) getWidth();
        int h = (int) getHeight();
        // Translate used to make sure scale is centered
        g2.translate(w/2, h/2);
        g2.scale(scale, scale);
        g2.translate(-w/2, -h/2);
        g2.dispose();
    }
}    
Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • You said "Override the getPreferredSize method of your component" I have two panels "innerPanel" and "outerPanel", wich one so you mean ? – SlimenTN Aug 03 '15 at 12:11
  • Technically both, but the one which is applying the scaling operation would be my first recomendation – MadProgrammer Aug 03 '15 at 12:12
  • I used Graphics#create and Graphics#dispose but now the zoomIn function doesn't work anymore ! – SlimenTN Aug 03 '15 at 12:20
  • Did you take the time to have a read of the [JavaDocs](http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html) to find out what the methods do and maybe how to use them? – MadProgrammer Aug 03 '15 at 12:24
  • (ps Sorry, it's late and I have a sick PC I'm trying to fix, so I'm a little distracted ;)) – MadProgrammer Aug 03 '15 at 12:29