【问题标题】:Drawn image inside panel seems to have wrong x,y offset面板内绘制的图像似乎有错误的 x,y 偏移
【发布时间】:2012-10-21 09:50:58
【问题描述】:

所以,我有一个带有菜单、工具栏和面板的 JFrame。我在面板中加载图像,但出于某种奇怪的原因(至少对我而言),它们没有正确显示在面板中。有时它们从工具栏下方开始,有时在上方。此外,图像在底部被切割。该代码是可以编译和测试的完整示例中的一个干净示例。提前致谢。

包含框架的类:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.FileDialog;

    import java.awt.Toolkit;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.util.zip.ZipInputStream;

    import javax.imageio.ImageIO;
    import javax.swing.BorderFactory;
    import javax.swing.JButton;
    import javax.swing.JFileChooser;
    import javax.swing.JFrame;
    import javax.swing.JMenu;
    import javax.swing.JMenuBar;
    import javax.swing.JMenuItem;
    import javax.swing.JPanel;
    import javax.swing.JToolBar;
    import javax.swing.border.BevelBorder;
    import javax.swing.border.Border;
    public class SSCE extends JFrame {
        private JComicPanel panel;
        private JToolBar toolbar;
        private JButton buttonZoom;
        private JButton buttonPrev;
        private JButton buttonNext;
        private JMenuBar menuBar;

        private Dimension dim;
        private BufferedImage img;
        private int currentPage;
        private JFrame parentFrame;




                public SSCE(){

                    super("JComic");
                    BorderLayout layout = new BorderLayout();
                    setLayout(layout);

                    dim = Toolkit.getDefaultToolkit().getScreenSize();

                    setSize((int)(dim.width /2.5),dim.height);

                    this.setPreferredSize(new Dimension((int) (dim.width /2.5),dim.height));

                    createToolbar();
                    createPanel();

                    add(toolbar,BorderLayout.NORTH);    

                    add(panel,BorderLayout.CENTER);


                    createMenu();
                    add(menuBar);
                    setJMenuBar(menuBar);
                    panel.setVisible(true);
                    img = null;
                    pack();
                    parentFrame = this;

                }


                private void createPanel(){
                    Border raisedbevel, loweredbevel;
                    raisedbevel = BorderFactory.createRaisedBevelBorder();
                    loweredbevel = BorderFactory.createLoweredBevelBorder();
                    panel = new JComicPanel(img);
                    panel.setBorder(BorderFactory.createCompoundBorder(raisedbevel,loweredbevel));


                }
                private void createToolbar(){
                    toolbar = new JToolBar();
                    toolbar.setPreferredSize(new Dimension(dim.width,45));  
                    toolbar.setFloatable(false);
                    buttonZoom = new JButton("+");

                    toolbar.add(buttonZoom);
                    buttonPrev = new JButton("<-");
                    toolbar.add(buttonPrev);
                    buttonNext = new JButton("->");
                    toolbar.add(buttonNext);
                    toolbar.setBackground(Color.RED);


                }
                private void createMenu(){
                    JMenu menuFile,menuJComic;
                    JMenuItem fileOpen; 
                    JMenuItem quitJComic,aboutJComic;


                    menuBar = new JMenuBar();
                    menuJComic = new JMenu("JComic");
                    aboutJComic = new JMenuItem("About JComic...");
                    menuJComic.add(aboutJComic);
                    quitJComic = new JMenuItem("Quit");
                    quitJComic.addActionListener(
                            new ActionListener(){
                                public void actionPerformed(ActionEvent e) {
                                    System.exit(0);
                                }
                            }
                            );
                    menuJComic.add(quitJComic);

                    menuBar.add(menuJComic);

                    menuFile = new JMenu("File");

                    fileOpen = new JMenuItem("Open...");
                    fileOpen.addActionListener(
                            new ActionListener(){
                                public void actionPerformed(ActionEvent e) {
                                    try {


                                        img = ImageIO.read(new File("superman.jpg"));
                                        currentPage = 1;

                                        int offset = menuBar.getHeight() + toolbar.getHeight();
                                        panel.setImage(img,parentFrame,offset);


                                    }
                                    catch (IOException e1) {
                                        System.out.println(e1.getMessage());
                                    }

                                }
                            }
                            );

                    menuFile.add(fileOpen);
                    menuBar.add(menuFile);


                }

        /**
         * @param args
         */
        @SuppressWarnings("deprecation")
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            SSCE f = new SSCE();
            f.show();

        }

    }

面板类

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.RenderingHints;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.awt.image.BufferedImage;
    import java.io.File;

    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JPanel;

    public class JComicPanel extends JPanel{
        private BufferedImage img;
        private int offset;
        private final float scaling = 0.5f;
        public JComicPanel(BufferedImage img){
            super();
            this.img = img;
            addComponentListener(new ComponentAdapter() {
                @Override
                public void componentResized(ComponentEvent e)
                {
                    repaint();
                }
            });

        }




         public void setImage(BufferedImage img,JFrame parentFrame,int offset){



             try{

                 int w = img.getWidth();
                 int h = img.getHeight();
                 int newW = (int)(w * scaling);
                 int newH = (int)(h * scaling);
                 BufferedImage dimg =  new BufferedImage(newW, newH, img.getType());  
                 Graphics2D g = dimg.createGraphics();  
                 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);  
                 g.drawImage(img, 0, 0, newW, newH, 0, 0, w, h, null);  
                 this.img = img;
                 System.out.printf("dim %d x %d",newW,newH);


                 this.setSize(new Dimension(newW,newH+offset));

                 parentFrame.pack();
             }
             catch(Exception e){
             }


         }

         public Dimension getPreferredSize(){

             return new Dimension(img.getWidth(),img.getHeight());
         }

        public void paintComponent(Graphics g){

               // Draw our Image object.
               super.paintComponent(g);
               g.drawImage(img,0,0,getSize().width,getSize().height, this); // at location 50,10
               System.out.println("painting 2");
              }

    }

图片 superman.jpg 可以在这里找到http://i50.tinypic.com/2yxnc3n.jpg。如您所见,图像位于工具栏下方,但在我的完整代码中,它也在上方。

【问题讨论】:

  • 嗯,问题是,“为什么图像在工具栏下方或上方移动”?不应该从JPanel的(0,0)开始绘制吗?
  • 不要在任何地方设置大小。而是给您的绘图 JPanel 一个 getPreferredSize() 覆盖,如果它包含一个图像,让它返回该图像的尺寸。然后考虑在更改图像后在顶级窗口上调用pack()。此外,您几乎永远不应该有一个空的 catch 块。另外,不要忘记在完成绘图后处理 BufferedImage 的 Graphics 上下文。
  • 在您回答后,我已经尝试过您所说的内容,但是如果我删除了 this.setSize(newW,newH); 行图像根本没有被绘制出来。
  • @Alf:我认为您需要创建并发布一个sscce(请单击并阅读链接),以便我们运行和测试,这是您获得好答案的最佳机会。这需要使用公开可用的图像,例如可以在网上找到的图像。
  • @AlfioCastorina 您修剪了太多代码,让我们无法猜测出了什么问题。听从 HFOE 的建议并发布SSCCE

标签: java swing jmenubar


【解决方案1】:

让我们从这里开始......

public void setImage(BufferedImage img,JFrame parentFrame,int offset){
    try{
        // You're scaling the incoming image, which means you no longer
        // have a reference to the original image should you want to 
        // change that scale...
        // You're previous code...

        // Don't do this.  The parent container's layout manager will simple
        // override it, so it's useless...
        this.setSize(newW,newH);
        repaint();

        // This is a bad idea (personally)...
        parentFrame.setSize(newW,newH+offset);
    } catch(Exception e){
    }
 }

以这种方式调用setSize 将暂时允许组件采用您设置的大小。如果你在它之后调用invalidate(),组件实际上会被重新调整大小以满足布局管理器的要求。

更大的问题是您正在设置父框架的大小,但您不知道框架的其他组件的布局要求,例如工具栏、菜单栏和框架(例如)

这是不考虑原图比例的画图

g.drawImage(img,0,0,getSize().width,getSize().height, this);

正如建议的那样,在缩放图像后,将图像简单地设置为已使用 BorderLayout 添加到图像窗格中的 JLabel 的图标可能更容易

您假设面板的大小是正确的,但它不会像面板的布局管理器那样接管。

这是多余的,因为 Swing 会在调整组件大小时自动重新绘制组件。

addComponentListener(new ComponentAdapter() {
    @Override
    public void componentResized(ComponentEvent e)
    {
        repaint();
    }
});

最好的办法是将图像窗格拖放到滚动窗格中,或者重新缩放图像以动态适应窗格的大小。

例如...

可缩放页面

public class ComicPage {

    public static void main(String[] args) {
        new ComicPage();
    }

    public ComicPage() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                try {
                    BufferedImage page = ImageIO.read(getClass().getResource("/1346.gif"));
                    ComicPagePane comicPagePane = new ComicPagePane();
                    comicPagePane.setComicPage(page);

                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(comicPagePane);
                    frame.setSize(200, 200);
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

    public class ComicPagePane extends JPanel {

        private BufferedImage comicPage;
        private Image scaledInstance;

        public void setComicPage(BufferedImage page) {
            if (page != comicPage) {
                comicPage = page;
                scaledInstance = null;
                repaint();
            }
        }

        @Override
        public void invalidate() {
            scaledInstance = null;
            super.invalidate(); 
        }

        public BufferedImage getComicPage() {
            return comicPage;
        }

        public double getScaleFactor(int iMasterSize, int iTargetSize) {
            return (double) iTargetSize / (double) iMasterSize;
        }

        public double getScaleFactorToFit(BufferedImage img) {
            double dScale = 1d;

            double dScaleWidth = getScaleFactor(img.getWidth(), getWidth());
            double dScaleHeight = getScaleFactor(img.getHeight(), getHeight());

            dScale = Math.min(dScaleHeight, dScaleWidth);

            return dScale;
        }

        protected Image getScaledInstance(BufferedImage master) {
            if (scaledInstance == null) {
                double scaleFactor = getScaleFactorToFit(master);
                System.out.println("scaleFactor = " + NumberFormat.getNumberInstance().format(scaleFactor));

                // This is not the best scaling alorithm
                scaledInstance = master.getScaledInstance(
                        (int) Math.round(master.getWidth() * scaleFactor),
                        (int) Math.round(master.getHeight() * scaleFactor), Image.SCALE_SMOOTH);
            }
            return scaledInstance;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            BufferedImage comicPage = getComicPage();
            if (comicPage != null) {
                Graphics2D g2d = (Graphics2D) g.create();

                int width = getWidth();
                int height = getHeight();

                // Normally, I would put this into a background worker as this
                // operation can be expensive....
                Image scaledInstance = getScaledInstance(comicPage);

                int x = (width - scaledInstance.getWidth(this)) / 2;
                int y = (height - scaledInstance.getHeight(this)) / 2;
                g2d.drawImage(scaledInstance, x, y, this);
                g2d.dispose();
            }
        }
    }
}

可滚动页面

public class ScrollableComicPage {

    public static void main(String[] args) {
        new ScrollableComicPage();
    }

    public ScrollableComicPage() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                try {
                    BufferedImage page = ImageIO.read(getClass().getResource("/1346.gif"));
                    ComicPagePage comicPagePane = new ComicPagePage();
                    comicPagePane.setComicPage(page);

                    JFrame frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLayout(new BorderLayout());
                    frame.add(new JScrollPane(comicPagePane));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }
        });
    }

    public class ComicPagePage extends JPanel {

        private BufferedImage comicPage;

        @Override
        public Dimension getPreferredSize() {
            return comicPage != null ? new Dimension(comicPage.getWidth(), comicPage.getHeight()) : new Dimension(0, 0);
        }

        public void setComicPage(BufferedImage page) {
            if (page != comicPage) {

                comicPage = page;
                invalidate();
                repaint();
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (comicPage != null) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.drawImage(comicPage, 0, 0, this);
                g2d.dispose();
            }
        }
    }
}

您可以阅读Java: maintaining aspect ratio of JPanel background image,了解有关缩放技术的更多信息。

【讨论】:

  • 感谢代码和输入。现在我会尝试修复我的。仍然无法理解为什么 setSize 会导致图像出现并删除它。为什么重绘没有任何结果?
  • repaint 是对重绘管理器在未来某个时间执行绘制的请求。无法保证油漆何时会出现或是否会出现。在这种情况下,调用invalidaterepaint 可能会解决问题,因为重绘管理器会考虑组件层次结构的状态。 Repaint 非常聪明,并试图减少它所做的绘制次数以提高应用程序的性能,有时它有点聪明。看看Painting in AWT and Swing
  • +1 表示代码,但查看 “让我们从这里开始...” 代码让我觉得这个 GUI 更适合在 @ 中显示图像987654337@.
  • 叹息,对这个很生气....我现在正在尝试 JLabel picLabel = new JLabel(new ImageIcon(this.img));添加(图片标签);但它仍然不起作用。如果我不写 setSize 图像不会出现,如果我这样做标签内的图像不会缩放并且仍然在工具栏上方绘制自身:-(
  • 看起来所有的混乱都来自工具栏,如果我删除它就可以了。
【解决方案2】:

技术原因是您暂时将 menuBar 添加到 contentPane 的中心,即与之前添加的面板相同的逻辑位置:

add(panel, BorderLayout.CENTER);
// following line is wrong - must be removed!!! 
add(menuBar); // no constraint == BorderLayout.CENTER
setJMenuBar(menuBar);

这样做会使面板脱离 LayoutManager 的控制,但不会脱离面板 - 最终效果是它没有约束。

作为一个 menuBar 并不特别(它需要添加到 rootPane 的 layeredPane,而不是 contentPane - 这就是它有自己的专用方法 setJMenuBar 的原因),同样的混乱会发生在任何其他任意组件上中心。

除此之外,我建议您清理代码:

  • 删除全部 setXXSize (some reasons)
  • 决定框架是以其首选(使用pack)还是任意固定大小(使用setSize)出现。在大多数情况下,前者是正确的做法,后者有其用例 - 但两者都做没有意义
  • 组件默认可见

【讨论】:

  • 是的,更改了顺序并为工具栏设置了preferredSize,现在它似乎可以工作了。谢谢。
猜你喜欢
  • 2017-04-01
  • 2012-12-11
  • 1970-01-01
  • 2015-03-03
  • 2015-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多