简体   繁体   English

放大和缩小 JPanel 后的绘画问题

[英]Issues with paint after zooming in and zooming out of JPanel

I have a scrollable JPanel, with some number drawn through the "drawString" method.我有一个可滚动的 JPanel,其中有一些数字是通过“drawString”方法绘制的。 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?我误用了重绘方法还是我的问题完全不同?

It might be better to use the AffineTransform#concatenate(AffineTransform) method to combine the AffineTransform for zoom and the AffineTransform for translation.最好使用AffineTransform#concatenate(AffineTransform)方法组合用于缩放的AffineTransform和用于平移的AffineTransform

@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 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);
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM