【问题标题】:Java: maintaining aspect ratio of JPanel background imageJava:保持JPanel背景图像的纵横比
【发布时间】:2012-08-11 04:26:14
【问题描述】:

我有一个JPanel,带有一个绘制的背景图像和一个布局管理器,其中包含其他较小的图像,所有这些都在一个JFrame 中。背景图像非常大,无论是在大显示器还是小显示器上,我都希望能够保持其纵横比。

最终,我希望能够将我的LayoutManager 及其单元格中的较小图像“粘合”到背景图片上。

我四处寻找资源,似乎很多示例都使用BufferedImage,但我没有;这会造成问题吗?我将在下面发布我的代码以绘制图像,如果我缺少任何信息,请告诉我。

public class MonitorPanel extends JPanel {
    Image img;
    public MonitorPanel() throws MalformedURLException {
        //add components

        try {
            img = ImageIO.read(new File("src/customer_vlans.jpg"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
protected void paintComponent(Graphics g)
{
    //paint background image
    super.paintComponent(g);
    //g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
    g.drawImage(img, 0, 0, this);

}

}

编辑:我应该提到我知道纵横比公式: 原始高度 / 原始宽度 x 新宽度 = 新高度 但是,我不知道如何正确使用它来发挥我的优势。

【问题讨论】:

    标签: java swing jpanel background-image aspect-ratio


    【解决方案1】:

    嗯,最快最简单的解决方案是使用Image.getScaledInstance

    g.drawImage(img.getScaledInstance(newWidth, -1, Image. SCALE_SMOOTH), x, y, this);
    

    如果您想知道负数,java 文档会说:

    如果宽度或高度为负数,则值为 替换为保持原始图像的纵横比 方面。如果宽度和高度都是负数,那么原来的 使用图像尺寸。

    更新

    顺便说一句(我的 Google 正在发挥作用)。

    getScaledInstance 不是最快或最高质量的方法,但它是最简单的。

    阅读The Perils of Image.getScaledInstance 了解更多想法

    更新

    缩放图像以适应某个区域比简单地缩放纵横比稍微复杂一些。您必须选择是否希望图像“适合”该区域(可能在其周围留下空白区域)或“填充”该区域(使其最小尺寸适合该区域的最大尺寸)。

    适合和填充

    基本上,我使用比例因子

    这将返回特定尺寸的缩放因子。我使用它来根据我需要的算法来决定我想使用哪个因素

    public static double getScaleFactor(int iMasterSize, int iTargetSize) {
    
        double dScale = 1;
        if (iMasterSize > iTargetSize) {
    
            dScale = (double) iTargetSize / (double) iMasterSize;
    
        } else {
    
            dScale = (double) iTargetSize / (double) iMasterSize;
    
        }
    
        return dScale;
    
    }
    

    这两种方法都用到了。他们只需要两个Dimensions。原件和目标。

    public static double getScaleFactorToFit(Dimension original, Dimension toFit) {
    
        double dScale = 1d;
    
        if (original != null && toFit != null) {
    
            double dScaleWidth = getScaleFactor(original.width, toFit.width);
            double dScaleHeight = getScaleFactor(original.height, toFit.height);
    
            dScale = Math.min(dScaleHeight, dScaleWidth);
    
        }
    
        return dScale;
    
    }
    
    public static double getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {
    
        double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
        double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
    
        double dScale = Math.max(dScaleHeight, dScaleWidth);
    
        return dScale;
    
    }
    

    将图像传入(直接或通过支持方法)相对简单。例如,您可以在 paint 方法中调用它

    double factor getScaledFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize());
    
    int scaledWidth = image.getWidth() * scale;
    int scaledHeight *= image.getWidth() * scale;
    

    这将自动为您处理纵横比;)

    更新扩展示例

    public double getScaleFactor(int iMasterSize, int iTargetSize) {
    
        double dScale = 1;
        if (iMasterSize > iTargetSize) {
    
            dScale = (double) iTargetSize / (double) iMasterSize;
    
        } else {
    
            dScale = (double) iTargetSize / (double) iMasterSize;
    
        }
    
        return dScale;
    
    }
    
    public double getScaleFactorToFit(Dimension original, Dimension toFit) {
    
        double dScale = 1d;
    
        if (original != null && toFit != null) {
    
            double dScaleWidth = getScaleFactor(original.width, toFit.width);
            double dScaleHeight = getScaleFactor(original.height, toFit.height);
    
            dScale = Math.min(dScaleHeight, dScaleWidth);
    
        }
    
        return dScale;
    
    }
    
    @Override
    protected void paintComponent(Graphics g) {
    
        super.paintComponent(g);
    
        double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
    
        int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
        int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
    
        Image scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
    
        int width = getWidth() - 1;
        int height = getHeight() - 1;
    
        int x = (width - scaled.getWidth(this)) / 2;
        int y = (height - scaled.getHeight(this)) / 2;
    
        g.drawImage(scaled, x, y, this);
    
    }
    

    【讨论】:

    • +1 不错。我的答案只是扩展了你的答案并使用getScaledInstance 将图像缩放到JPanel 的大小
    • 感谢您的帖子。我究竟如何获得newWidth?我会使用 getWidth() 对不起,如果这是一个微不足道的问题,我对 GUI Java 很陌生
    • @Pat newWidth 是缩放后的大小,所以,是的,我想你可以使用 getWidth
    • 太棒了!我使用了您的两个答案来获得所需的效果。我希望我能检查您的两个答案,非常感谢您的时间和知识。
    • 因此,如果我使用您最近更新的方法并选择“适合”,图像是否会在图像变小时调整纵横比,而一旦窗口扩大到超过原始图像大小,图像会停止调整大小吗?我有点意识到这就是我真正想要的效果哈哈
    【解决方案2】:

    我想出了这个解决方案:

    public class ImageLabel extends JPanel {
    
        private Image image = null;
    
        public void setImage(Image img) {
            image = img;
            repaint();
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
    
            if (image != null) {
    
                int imgWidth, imgHeight;
                double contRatio = (double) getWidth() / (double) getHeight();
                double imgRatio =  (double) image.getWidth(this) / (double) image.getHeight(this);
    
                //width limited
                if(contRatio < imgRatio){
                    imgWidth = getWidth();
                    imgHeight = (int) (getWidth() / imgRatio);
    
                //height limited
                }else{
                    imgWidth = (int) (getHeight() * imgRatio);
                    imgHeight = getHeight();
                }
    
                //to center
                int x = (int) (((double) getWidth() / 2) - ((double) imgWidth / 2));
                int y = (int) (((double) getHeight()/ 2) - ((double) imgHeight / 2));
    
                g.drawImage(image, x, y, imgWidth, imgHeight, this);
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      对于任何有兴趣通过 MadProgrammer 修改 PaintComponent 方法的人,如下所示可以更快地更新显示

              @Override
              protected void paintComponent(Graphics g) {
                  Graphics2D g2d = (Graphics2D) g;
                  super.paintComponent(g);
      
                  double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
      
                  int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
                  int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
      
                  //Image scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
      
                  int width = getWidth() - 1;
                  int height = getHeight() - 1;
      
                  int x = (width - scaleWidth) / 2;
                  int y = (height - scaleHeight) / 2;
      
                  g2d.drawImage(image, x, y, scaleWidth, scaleHeight, this);
      
              }
      

      【讨论】:

      • 这要快得多。 getScaledInstance 很慢。
      【解决方案4】:

      试试这样的:

      import java.awt.*;
      import javax.swing.ImageIcon;
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      
      public class SG2B2 {
      
          JFrame frame;
      
          public static void main(String[] args) {
              SG2B2 gui = new SG2B2();
              gui.createUI();
          }
      
          public void createUI() {
              frame = new JFrame();
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              MyDrawPanel drawPanel = new MyDrawPanel();
              frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
              frame.setSize(300, 400);
              frame.setVisible(true);
          }
      
          class MyDrawPanel extends JPanel {
      
              Image image;
              private final String pic = "Logo.jpg";
      
              public MyDrawPanel() {
                  image = new ImageIcon(pic).getImage();
                  image = scaleImage(image);
              }
      
              @Override
              public void paintComponent(Graphics g) {
                  Graphics2D g2 = (Graphics2D) g;
                  g2.drawImage(image, 0, 0, this);
              }
      
              private Image scaleImage(Image rawImage) {
                  Image scaledImage = null;
                  System.out.println("Scaling");
                  try {
                      int rawImageWidth = rawImage.getWidth(this);
                      int rawImageHeight = rawImage.getHeight(this);
                      int paneWidth = (int) getWidth();
                      int paneHeight = (int) getHeight();
                      System.out.println("Image W = " + rawImageWidth
                              + ", H = " + rawImageHeight
                              + "; Pane W = " + paneWidth
                              + ", H = " + paneHeight);
                      // preserve the original ratio  
                      float widthRatio = (float) rawImageWidth / (float) paneWidth;
                      float heightRatio = (float) rawImageHeight / (float) paneHeight;
                      int widthFactor = -1;
                      int heightFactor = -1;
                      if ((widthRatio > heightRatio) && (widthRatio > 1.0)) {
                          widthFactor = paneWidth;
                      } else if ((heightRatio > widthRatio) && (heightRatio > 1.0)) {
                          heightFactor = paneHeight;
                      }
                      System.out.println("widthRatio = "
                              + String.format("%.3f", widthRatio)
                              + ", heightRatio = "
                              + String.format("%.3f", heightRatio));
                      System.out.println("widthFactor = " + widthFactor
                              + ", heightFactor = " + heightFactor);
                      if ((widthFactor < 0) && (heightFactor < 0)) {
                          scaledImage = rawImage;
                      } else {
                          scaledImage = rawImage.getScaledInstance(widthFactor, heightFactor,
                                  Image.SCALE_SMOOTH);
                          // load the new image, 'getScaledInstance' loads asynchronously  
                          MediaTracker tracker = new MediaTracker(this);
                          tracker.addImage(scaledImage, 0);
                          tracker.waitForID(0);
                      }
                  } catch (InterruptedException ie) {
                      System.err.println("load interrupt: " + ie.getMessage());
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
                  return (scaledImage);
              }
          }
      }
      

      最终将使用getScaledInstance(int width, int height, ImageObserver io) 将图像缩放到JPanel 的大小

      【讨论】:

      • 如果我要使用MediaTracker,我可能会将其卸载到第二个Thread(显然这只是一个示例;))。另一个要记住的有趣事实是每个 Java Component 都是它自己的 ImageObserver,因此您可以在没有 MediaTracker 的情况下逃脱,因为 Component 已经在这样做(在某种程度上)。除非你有一个巨大的图像,你知道加载需要时间,否则我可能会在单独的 Thread 中做同样的事情;)恕我直言
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多