Home > database >  Issues with paint after zooming in and zooming out of JPanel
Issues with paint after zooming in and zooming out of JPanel

Time:12-26

I have a scrollable JPanel, with some number drawn through the "drawString" method. When I click the zoom in button, the repaint method is invoked and the number becomes larger, as expected. But when I try to scroll to the right, part of the number is either missing or is painted in its original size instead of the new zoomed size. Below is the minimal reproducible code.

package sometest;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JViewport;

public class SomeTest implements Runnable
{
    public static JButton in;
    public static JButton out;
    
    public static void main(String[] args) 
    {
        SomeTest st = new SomeTest();
        st.run();
    }  
    
    @Override
    public void run() 
    {
        JMenuBar mb = new JMenuBar();
        in = new JButton("Zoom In");
        out = new JButton("Zoom Out");
        JFrame f = new JFrame("Example");
        Visualization v = new Visualization();
        JScrollPane sp = new JScrollPane(v);

        MouseAdapter mouseAdapter = new MouseAdapter() 
        {
            private Point origin;
            
            @Override
            public void mousePressed(MouseEvent e) 
            {
                origin = new Point(e.getPoint());
            }
             
            @Override
            public void mouseDragged(MouseEvent e) 
            {
                JViewport vp = null;
                
                if (origin != null) 
                {
                    vp = sp.getViewport();
                }
                
                if (vp != null) 
                {
                    int deltaX = origin.x - e.getX();
                    int deltaY = origin.y - e.getY();

                    Rectangle view = vp.getViewRect();
                    view.x  = deltaX;
                    view.y  = deltaY;

                    v.scrollRectToVisible(view);
                }
            }
        };

        sp.getViewport().addMouseListener(mouseAdapter);
        sp.getViewport().addMouseMotionListener(mouseAdapter);
        
        f.add(sp);
        f.setJMenuBar(mb);
        mb.add(in);
        mb.add(out);
        f.setContentPane(sp);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
package sometest;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
import static sometest.SomeTest.in;
import static sometest.SomeTest.out;

public class Visualization extends JPanel implements Scrollable, ActionListener
{
    private double zoomFactor = 1;
    private double prevZoomFactor = 1;
    private boolean zoomer;
    private double xOffset = 0;
    private double yOffset = 0;
    
    public Visualization()
    {
        in.addActionListener(this);
        out.addActionListener(this);
        Font currentFont = getFont();
        Font newFont = currentFont.deriveFont(currentFont.getSize() * 18F);
        setFont(newFont);
        setBackground(Color.WHITE);
    }
    
    @Override
    public void actionPerformed(ActionEvent e) 
    {
        if(e.getSource()==in)
        {
            zoomer = true;
            zoomFactor *= 1.1;
            repaint();
        }
        
        if(e.getSource()==out)
        {
            zoomer = true;
            zoomFactor /= 1.1;
            repaint();
        }
    }
    
    @Override
    protected void paintComponent(Graphics g) 
    {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        
         if(zoomer) 
         {
            AffineTransform at = new AffineTransform();
            
            Dimension center = getPreferredScrollableViewportSize();
            
            Double xRel = center.getWidth()/2;
            Double yRel = center.getHeight()/2;

            double zoomDiv = zoomFactor / prevZoomFactor;

            xOffset = (zoomDiv) * (xOffset)   (1 - zoomDiv) * xRel;
            yOffset = (zoomDiv) * (yOffset)   (1 - zoomDiv) * yRel;

            at.translate(xOffset, yOffset);
            at.scale(zoomFactor, zoomFactor);
            prevZoomFactor = zoomFactor;
            g2d.transform(at);
            zoomer = false;
        }
         
        g2d.drawString("1234567890", 500, 200);
            
        g2d.dispose();
    }
    
    @Override
        public Dimension getPreferredSize() 
        {
            if(zoomer)
            {
                return new Dimension((int)(2000*zoomFactor), (int)(750*zoomFactor));
            }
            else
            {
                 return new Dimension(2000, 750);
            }
        }

        @Override
        public Dimension getPreferredScrollableViewportSize() 
        {
            return new Dimension(1550, 750);
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) 
        {
            return 32;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) 
        {
            return 32;
        }

        @Override
        public boolean getScrollableTracksViewportWidth() 
        {
            return false;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() 
        {
            return false;
        }
}

When I zoom in and scroll, I'm expecting the drawn number to remain the same size. As far as I know, the repaint method is supposed to automatically handle cases such as when scrolling or resizing. Have I misused the repaint method or is my problem something entirely different?

CodePudding user response:

It might be better to use the AffineTransform#concatenate(AffineTransform) method to combine the AffineTransform for zoom and the AffineTransform for translation.

@Override
protected void paintComponent(Graphics g) {
  super.paintComponent(g);
  Graphics2D g2d = (Graphics2D) g.create();
  AffineTransform scrollTransform = g2d.getTransform();
  scrollTransform.concatenate(zoomTransform);
  g2d.setTransform(scrollTransform);

  g2d.drawString("1234567890", 500, 200);

  g2d.dispose();
}

SomeTest2.java

// package sometest;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;

public class SomeTest2 {
  public JComponent makeUI() {
    Visualization v = new Visualization();

    MouseAdapter mouseAdapter = new DragScrollListener();
    v.addMouseListener(mouseAdapter);
    v.addMouseMotionListener(mouseAdapter);

    JButton in = new JButton("Zoom In");
    in.addActionListener(e -> v.setZoomFactor(1.1));

    JButton out = new JButton("Zoom Out");
    out.addActionListener(e -> v.setZoomFactor(1 / 1.1));

    JMenuBar mb = new JMenuBar();
    mb.add(in);
    mb.add(out);
    EventQueue.invokeLater(() -> v.getRootPane().setJMenuBar(mb));
    return new JScrollPane(v);
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new SomeTest2().makeUI());
      f.setSize(640, 480);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class Visualization extends JPanel {
  private final AffineTransform zoomTransform = new AffineTransform();
  private final Rectangle rect = new Rectangle(2000, 750);

  public Visualization() {
    Font currentFont = getFont();
    Font newFont = currentFont.deriveFont(currentFont.getSize() * 18F);
    setFont(newFont);
    setBackground(Color.WHITE);
  }

  public void setZoomFactor(double zoomFactor) {
    zoomTransform.scale(zoomFactor, zoomFactor);
    revalidate();
    repaint();
  }

  @Override
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    AffineTransform scrollTransform = g2d.getTransform();
    scrollTransform.concatenate(zoomTransform);
    g2d.setTransform(scrollTransform);

    g2d.drawString("1234567890", 500, 200);

    g2d.dispose();
  }

  @Override
  public Dimension getPreferredSize() {
    Rectangle r = zoomTransform.createTransformedShape(rect).getBounds();
    return new Dimension(r.width, r.height);
  }
}

class DragScrollListener extends MouseAdapter {
  private final Point origin = new Point();

  @Override
  public void mouseDragged(MouseEvent e) {
    Component c = e.getComponent();
    Container p = SwingUtilities.getUnwrappedParent(c);
    if (p instanceof JViewport) {
      JViewport viewport = (JViewport) p;
      Point cp = SwingUtilities.convertPoint(c, e.getPoint(), viewport);
      Point vp = viewport.getViewPosition();
      vp.translate(origin.x - cp.x, origin.y - cp.y);
      ((JComponent) c).scrollRectToVisible(new Rectangle(vp, viewport.getSize()));
      origin.setLocation(cp);
    }
  }

  @Override
  public void mousePressed(MouseEvent e) {
    Component c = e.getComponent();
    Container p = SwingUtilities.getUnwrappedParent(c);
    if (p instanceof JViewport) {
      JViewport viewport = (JViewport) p;
      Point cp = SwingUtilities.convertPoint(c, e.getPoint(), viewport);
      origin.setLocation(cp);
    }
  }
}
  • Related