简体   繁体   English

如何使用 Java Swing 库实现缩放面板

[英]How can I implement a Zoom Panel with Java Swing libraries

I've implemented a JFrame with Java Swing libraries to visualize a map.我已经使用 Java Swing 库实现了一个 JFrame 来可视化地图。 I'd like to realize a zoom function within this frame.我想在这个框架内实现缩放功能。 This is the code of my JFrame:这是我的 JFrame 的代码:

package components;

import java.awt.BorderLayout;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JScrollPane;




public class MyFrame extends JFrame {

private static final long serialVersionUID = 1L;
private Container contentPane;
public static final int startWidth = 1280;
public static final int startHeight = 800;
public static MyPanel EarthPanel;

public MyFrame(){
    initComponents();
}


public void initComponents(){
    contentPane = getContentPane();
    this.setTitle("TLE Graphic Propagator");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setSize(startWidth, startHeight);
    this.setLocation(0,0);
    this.setLayout(new BorderLayout());
    EarthPanel = new MyPanel(startWidth,startHeight);
    JScrollPane scroll = new JScrollPane(EarthPanel,     JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    MySlider slider = new MySlider();
    contentPane.add(scroll, BorderLayout.CENTER);
    contentPane.add(slider, BorderLayout.SOUTH);
}
}

As you can see above, the frame contains a JPanel in a JScrollPane and a JSlider that I use for zoom function.正如您在上面看到的,框架包含一个 JScrollPane 中的 JPanel 和一个用于缩放功能的 JSlider。 It follows the code of the panel..它遵循面板的代码..

package components;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class MyPanel extends JPanel{

private static final long serialVersionUID = 1L;
private BufferedImage img;
private int width, height;
private String path = "images/earth1280x800.jpg";
private static final int UPDATE_RATE = 10;  //#volte al secondo


public MyPanel(int larghezzaFrame, int altezzaFrame){   

    width = larghezzaFrame;
    height = altezzaFrame;

    this.setPreferredSize(new Dimension(width, height));

    img = loadImage();
}


public BufferedImage loadImage() {
    BufferedImage bimg = null;
    BufferedImage ret = null;
    try {
        bimg = ImageIO.read(new File(path));
    } catch (Exception e) {
        e.printStackTrace();
    }
    ret = new BufferedImage(bimg.getWidth(), bimg.getHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = ret.createGraphics();
    g.drawImage(bimg, 0, 0, null);
    g.dispose();

    return ret;
}

@Override
protected void paintComponent(Graphics g) {
    setOpaque(false);

    Graphics2D g2d = (Graphics2D)g;
    g2d.scale(MySlider.SCALE, MySlider.SCALE);
    g2d.drawImage(img, 0, 0, null); 
    super.paintComponent(g2d);
}

}

..and the slider's one: ..和滑块之一:

package components;

import java.awt.Dimension;

import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class MySlider extends JSlider implements ChangeListener{

private static final long serialVersionUID = 1L;
public static double SCALE = 1;

public MySlider(){
    super(JSlider.HORIZONTAL, 100, 400, 100);
    setMajorTickSpacing(50);  
    setMinorTickSpacing(10);  
    setPaintTicks(true);  
    setPaintLabels(true);  
    addChangeListener(this);   
}

@Override
public void stateChanged(ChangeEvent arg0) {
    int value = ((JSlider) arg0.getSource()).getValue();  
    SCALE = value/100.0; 
    MyFrame.EarthPanel.setPreferredSize(new Dimension((int)(MyFrame.startWidth * MySlider.SCALE),(int)(MyFrame.startHeight * MySlider.SCALE)));
    MyFrame.EarthPanel.repaint();
}

}

To run the code, you just need to insert the following Main function:要运行代码,您只需要插入以下 Main 函数:

package main;

import components.MyFrame;

public class MainPersonalFrame {
public static void main(String[] args){
    MyFrame frame = new MyFrame();
    frame.setVisible(true);
}
}

Note: It will be necessary to insert a package "images" in the same path of the source code with an image called "earth1280x800.jpg" into that.注意:需要在源代码的同一路径中插入一个包“images”,其中包含一个名为“earth1280x800.jpg”的图像。

The problem that usually comes out is that when I use the zoom function, scrolling right/left and up/down, sometimes the image is not contained by the frame anymore.通常出现的问题是,当我使用缩放功能,向右/向左和向上/向下滚动时,有时图像不再包含在框架中。 I'd like to get an automatic refresh of the scrollbars and that the image doesn't come out from the borders.我想自动刷新滚动条,并且图像不会从边框中出来。 How should I do it?我该怎么做?

Thanks to everyone.谢谢大家。

I'm not 100% sure what the problem is, but I suspect that an issue is that you're not changing the preferred size of your image JPanel when the image changes size.我不是 100% 确定问题是什么,但我怀疑一个问题是当图像更改大小时,您没有更改图像 JPanel 的首选大小。 If so, then a solution would be to override getPreferredSize() in the image displaying JPanel and return a dimension that matches that of the zoomed image.如果是这样,那么解决方案是覆盖显示 JPanel 的图像中的getPreferredSize()并返回与缩放图像的尺寸匹配的尺寸。


Edit, no, you sort of do this, but you're not revalidating nor repainting the JScrollPane's viewport, and this is what you must do.编辑,不,你有点这样做,但你没有重新验证或重新绘制 JScrollPane 的视口,这是你必须做的。

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class MainPersonalFrame {
   public static void main(String[] args) {
      MyFrame frame = new MyFrame();
      frame.setVisible(true);
   }
}

class MyFrame extends JFrame {

   private static final long serialVersionUID = 1L;
   private Container contentPane;
   public static final int startWidth = 1280;
   public static final int startHeight = 800;

   // !! This should be private and not static
   private MyPanel earthPanel;

   // !! this should be a field
   private JScrollPane scroll;

   public MyFrame() {
      initComponents();
   }

   public void initComponents() {
      contentPane = getContentPane();
      this.setTitle("TLE Graphic Propagator");
      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      // !! avoid setting sizes if possible
      this.setSize(startWidth, startHeight);
      this.setLocation(0, 0);
      this.setLayout(new BorderLayout());
      earthPanel = new MyPanel(startWidth, startHeight);

      // !! again, scroll is now a field
      scroll = new JScrollPane(earthPanel,
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
      MySlider slider = new MySlider(this);
      contentPane.add(scroll, BorderLayout.CENTER);
      contentPane.add(slider, BorderLayout.SOUTH);
   }

   // !! to avoid having classes directly manipulate other class's fields
   public void setEarthPanelSize(Dimension size) {
      earthPanel.setPreferredSize(size);
   }

   // !! allow other classes the ability to revalidate/repaint viewport
   public void revalidateViewport() {
      scroll.getViewport().revalidate();
      scroll.getViewport().repaint();
   }
}

class MyPanel extends JPanel {

   private static final long serialVersionUID = 1L;
   private BufferedImage img;
   private int width, height;

   // !! changes so that I can run your program
   // !! with an internet image
   // !! private String path = "images/earth1280x800.jpg";
   private String urlPath = "http://image.desk7.net/"
         + "Space%20Wallpapers/1422_1280x800.jpg";

   // !! private static final int UPDATE_RATE = 10; // !! never used

   public MyPanel(int larghezzaFrame, int altezzaFrame) {
      setOpaque(false); // !! this should be here
      width = larghezzaFrame;
      height = altezzaFrame;

      this.setPreferredSize(new Dimension(width, height));

      img = loadImage();
   }

   public BufferedImage loadImage() {
      BufferedImage bimg = null;
      BufferedImage ret = null;
      try {
         URL imgUrl = new URL(urlPath);
         // !! bimg = ImageIO.read(new File(path)); // !!
         bimg = ImageIO.read(imgUrl); // !! use image available to all
         // !! } catch (Exception e) {
         // e.printStackTrace();
      } catch (IOException e) {
         // !! use more specific exception
         e.printStackTrace();
      }
      ret = new BufferedImage(bimg.getWidth(), bimg.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D g = ret.createGraphics();
      g.drawImage(bimg, 0, 0, null);
      g.dispose();

      return ret;
   }

   @Override
   protected void paintComponent(Graphics g) {
      // !! this shouldn't be in paintComponent:
      // !! setOpaque(false); 

      Graphics2D g2d = (Graphics2D) g;
      g2d.scale(MySlider.SCALE, MySlider.SCALE);
      g2d.drawImage(img, 0, 0, null);
      super.paintComponent(g2d);
   }

}

class MySlider extends JSlider implements ChangeListener {

   private static final long serialVersionUID = 1L;
   public static double SCALE = 1;
   private MyFrame myFrame;

   public MySlider(MyFrame myFrame) {
      super(JSlider.HORIZONTAL, 100, 400, 100);
      this.myFrame = myFrame;
      setMajorTickSpacing(50);
      setMinorTickSpacing(10);
      setPaintTicks(true);
      setPaintLabels(true);
      addChangeListener(this);
   }

   @Override
   public void stateChanged(ChangeEvent arg0) {
      int value = ((JSlider) arg0.getSource()).getValue();
      SCALE = value / 100.0;
      int w = (int) (MyFrame.startWidth * MySlider.SCALE);
      int h = (int) (MyFrame.startHeight * MySlider.SCALE);
      Dimension size = new Dimension(w, h);
      myFrame.setEarthPanelSize(size); // !!
      myFrame.revalidateViewport(); // !!
      // !! MyFrame.earthPanel.repaint(); // No, don't do this
   }
}

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

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