【问题标题】:how to put my background image at the bottom如何将我的背景图片放在底部
【发布时间】:2016-04-05 15:03:49
【问题描述】:

我想把我的背景图片放在这个框架的最底部,把按钮放在上面。但是我在下面编写的代码不起作用。谁能看出问题出在哪里?

另一件事是,即使我为按钮设置了位置,它仍然显示在框架的顶部中心。

请忽略注释行。 (我只是在猜测,并希望它们会起作用,但显然它们不会。)

public class Menu extends JFrame{
private JLayeredPane pane;
private JLayeredPane pane2;

public Menu(){
    final JFrame f = new JFrame("Chinese Chess");


    JButton play = new JButton("Play vs. AI");

    f.setLayout(new FlowLayout());
    f.setLocationRelativeTo(null);
    f.setSize(800, 800);
    f.setVisible(true);
    f.setResizable(false);
    //f.pack(); 

    pane = new JLayeredPane();
    pane2 = new JLayeredPane();
    f.add(pane);
    f.add(pane2);


    //background image
    JLabel background = new JLabel(new ImageIcon("res/img/background.png"));
    background.setLocation(0, 0);
    background.setSize(800, 800);
    pane.add(background, JLayeredPane.FRAME_CONTENT_LAYER);
    pane2.add(play, JLayeredPane.DEFAULT_LAYER);
    //pane.moveToBack();

    //button PlayAI
    play.setLocation(500,500);
    play.setPreferredSize(new Dimension(100,50));
    //f.setLayout(new FlowLayout());

    //frame menu
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //f.getContentPane().add(play);


    play.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
            new PlayAI();
        }
    });
}

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

【问题讨论】:

    标签: java swing frame layer


    【解决方案1】:

    问题/解决方案:

    • 大多数布局管理器都会忽略setLocation(...)setBounds(...) 类型的调用。使用它们的唯一方法是通过 .setLayout(null); 将容器的布局设置为 null
    • 但是话虽如此,虽然空布局和setBounds() 对于 Swing 新手来说似乎是创建复杂 GUI 的最简单和最好的方法,但创建的 Swing GUI 越多,使用它们时遇到的困难就越严重。当 GUI 调整大小时,它们不会调整您的组件大小,它们是增强或维护的皇家女巫,放置在滚动窗格中时它们完全失败,在与原始不同的所有平台或屏幕分辨率上查看时它们看起来很糟糕.
    • 总而言之,不要这样做,不要使用 null 布局或 setBounds,而是嵌套 JPanel,每个 JPanel 都使用自己的布局管理器,从而创建易于维护和体面的 GUI。
    • 如果您希望图像位于背景中,则通过在 JPanel 的 paintComponent(Graphics g) 方法中将其绘制在用作 GUI 组件容器的 JPanel 中,正如在许多类似问题中所展示的那样这个网站 -- 我马上就会给你找一些我的。
    • 如果您在此图像绘制 JPanel 上添加任何 JPanel,请确保您可以通过在这些重叠的 JPanel 上调用 setOpaque(false) 来查看它们。否则你会掩盖图像。
    • 当只需要一个时,您的代码有两个 JFrame。扔掉你不使用的那个。
    • 在将组件添加到 GUI 之前,在 JFrame 上调用 setVisible(true) 太早了——不要。仅在将所有内容添加到 GUI 后调用它,以便所有内容都显示正常。
    • 您正在创建两个 JLayedPanes,并通过将它们添加到 JFrame 来完全覆盖另一个,而无需了解 JFrame 的 BorderLayout 如何处理添加的组件。
    • 我建议您甚至不要使用一个 JLayeredPane,而是如上所述在 JPanel 中绘制,并将其用作您的容器。
    • 当按下播放按钮时,您的代码看起来像是打开了一个全新的 GUI 窗口,如果是这样,这很快就会让用户感到厌烦。考虑使用CardLayout 交换视图。

    例如:

    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.GridBagLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    
    import javax.imageio.ImageIO;
    import javax.swing.*;
    
    // extend JPanel so you can draw to its background
    @SuppressWarnings("serial")
    public class Menu2 extends JPanel {
        private BufferedImage bgImage = null; // our background image
        private JButton playButton = new JButton(new PlayVsAiAction("Play Vs. AI", KeyEvent.VK_P));
    
        public Menu2(BufferedImage bgImage) {
            this.bgImage = bgImage;
    
            setLayout(new GridBagLayout());  // center our button
            add(playButton);
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (bgImage != null) {
                g.drawImage(bgImage, 0, 0, this);
            }
        }
    
        // to size our GUI to match a constant or the image.
        @Override
        public Dimension getPreferredSize() {
            if (isPreferredSizeSet()) {
                return super.getPreferredSize();
            }
    
            // if you want to size it based on the image
            if (bgImage != null) {
                int width = bgImage.getWidth();
                int height = bgImage.getHeight();
                return new Dimension(width, height);            
            } else {
                return super.getPreferredSize();
            }
    
            // if you want to size the GUI with constants:
            // return new Dimension(PREF_W, PREF_H);
        }
    
        private class PlayVsAiAction extends AbstractAction {
            public PlayVsAiAction(String name, int mnemonic) {
                super(name); // have our button display this name
                putValue(MNEMONIC_KEY, mnemonic); // alt-key to press button
            }
    
            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO code to start program
    
            }
        }
    
        private static void createAndShowGui() {
            BufferedImage img = null;
            String imagePath = "res/img/background.png";
            try {
                // TODO: fix this -- use class resources to get image, not File 
                img = ImageIO.read(new File(imagePath));
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(-1);
            }
            Menu2 mainPanel = new Menu2(img);
    
            JFrame frame = new JFrame("Chinese Chess");
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                createAndShowGui();
            });
        }
    }
    

    【讨论】:

    • 非常感谢!它确实有助于解释。请给我一些时间来修改我的代码,稍后我会给你一些反馈。 @HovercraftFullOfEels
    • 为什么设置bgImage = null? @HovercraftFullOfEels
    • @AnthonyZhang:因为我没有将 BufferedImage 分配给我声明变量的 bgImage ——我不能,因为这是不可能的。相反,我在构造函数中分配它(你可以清楚地看到)。
    【解决方案2】:

    除了上面的解决方案...您应该以这种方式创建和启动您的 Swing 应用程序:

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
    
    private static void createAndShowGUI() {
        // Instantiate your JFrame and show it 
    }
    

    【讨论】:

    • 谢谢 ;) @HovercraftFullOfEels
    • 谢谢!我实际上是java的新手。我在你的两个代码中都不理解的一行是“SwingUtilities.invokeLater(new Runnable())”。你能告诉我它是做什么的吗? @豪尔赫
    • @AnthonyZhang 考虑到这是一份简短的简历!!!我希望它有所帮助。使用 swing,对用户界面的任何更新都必须发生在事件调度线程(AWT 事件调度线程)上。因此,任何对任何意味着更新我们的 GUI 的方法的调用都应该从这个线程中调用。使用 invokeLater 可以保证您的代码将在 EDT 中执行。还有其他选项(例如invokeAndWait),但在特定情况下,这将尽快在EDT中执行您的代码(在处理完所有其他先前事件之后)
    猜你喜欢
    • 2014-01-01
    • 1970-01-01
    • 2013-11-26
    • 1970-01-01
    • 2018-08-10
    • 2011-08-28
    • 1970-01-01
    • 2017-08-01
    • 1970-01-01
    相关资源
    最近更新 更多