How to set a background picture in JPanel

前端 未结 3 906
猫巷女王i
猫巷女王i 2020-11-22 14:40

hello i am using JPanel as my container of my frame then i really want to used a background picture in my Panel i really need help this is my code so far . this is the updat

3条回答
  •  囚心锁ツ
    2020-11-22 14:57

    There are any number of ways this might be achieved.

    You Could...

    Disclaimer

    Cavet, using a JLabel for this purpose may result in the contents over spilling the continer, see below for more details

    Create a JLabel, apply the image to it's icon property and set this as the frames content pane. You would then need to set the layout manager appropriately, as JLabel doesn't have a default layout manager

    JFrame frame = ...;
    JLabel background = new JLabel(new ImageIcon(ImageIO.read(...)));
    frame.setContentPane(background);
    frame.setLayout(...);
    frame.add(...);
    

    Update with full example

    label based example

    import java.awt.EventQueue;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class LabelBackground {
    
        public static void main(String[] args) {
            new LabelBackground();
        }
    
        public LabelBackground() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    try {
                        // Load the background image
                        BufferedImage img = ImageIO.read(new File("/path/to/your/image/on/disk"));
    
                        // Create the frame...
                        JFrame frame = new JFrame("Testing");
                        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
                        // Set the frames content pane to use a JLabel
                        // whose icon property has been set to use the image
                        // we just loaded                        
                        frame.setContentPane(new JLabel(new ImageIcon(img)));
    
                        // Supply a layout manager for the body of the content
                        frame.setLayout(new GridBagLayout());
                        GridBagConstraints gbc = new GridBagConstraints();
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        // Add stuff...
                        frame.add(new JLabel("Hello world"), gbc);
                        frame.add(new JLabel("I'm on top"), gbc);
                        frame.add(new JButton("Clickity-clackity"), gbc);
    
                        frame.pack();
                        frame.setLocationRelativeTo(null);
                        frame.setVisible(true);
                    } catch (IOException exp) {
                        exp.printStackTrace();
                    }
                }
            });
        }
    }
    

    The problem with this is the JLabel won't resize the image when the frame is resized

    WARNING - Using a JLabel could cause issues if the required space of the child components exceeds the size of the background image, as JLabel does not calculate it's preferred size based on it's contents, but based on its icon and text properties

    You Could...

    Create a custom component, extending from something like JPanel and override it's paintComponent method, painting the background as you see fit.

    Take a look at Performing Custom Painting for more details.

    This provides you with the ability to decide how best the image should be scaled when it's available space changes. While there are a number of ways this might be achived, you should read through The Perils of Image.getScaledInstance() to understand the pros and cons of them.

    This raises a bunch of new questions, to you want to scale them and preserve the aspect ratio? If so, do you want to fit the image to available area or fill it (so it will always cover the available space)?

    Take a look at Java: maintaining aspect ratio of JPanel background image for more details.

    Other considerations

    Images are generally best loaded through the ImageIO API, as it's capable of loading a wide range of images, but will also throw an IOException when something goes wrong.

    See Reading/Loading an Image for more details.

    The location of the image is also important. If the image is external to the application (somewhere on the file system), you can use ImageIO.read(new File("/path/to/image")). However, if the the image is embedded within your application (stored within the Jar for example), you will need to use something more like ImageIO.read(getClass().getResource("/path/to/image")) instead...

    For example...

    • Trouble Figuring Out How To Set Background Image
    • Add an Background image to a Panel
    • Java: JPanel background not scaling

    Example

    This example demonstrates the use of a custom component which acts as the background component. When the components size exceeds the size of the background image, the image is scaled up to fill the available content area.

    enter image description here

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.HeadlessException;
    import java.awt.RenderingHints;
    import java.awt.Transparency;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class SimpleBackground {
    
        public static void main(String[] args) {
            new SimpleBackground();
        }
    
        public SimpleBackground() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
    
                    try {
                        BackgroundPane background = new BackgroundPane();
                        background.setBackground(ImageIO.read(new File("/path/to/your/image/on/your/disk")));
    
                        JFrame frame = new JFrame("Testing");
                        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                        frame.setContentPane(background);
                        frame.setLayout(new GridBagLayout());
                        GridBagConstraints gbc = new GridBagConstraints();
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        frame.add(new JLabel("Hello world"), gbc);
                        frame.add(new JLabel("I'm on top"), gbc);
                        frame.add(new JButton("Clickity-clackity"), gbc);
                        frame.pack();
                        frame.setLocationRelativeTo(null);
                        frame.setVisible(true);
                    } catch (IOException exp) {
                        exp.printStackTrace();
                    }
                }
            });
        }
    
        public class BackgroundPane extends JPanel {
    
            private BufferedImage img;
            private BufferedImage scaled;
    
            public BackgroundPane() {
            }
    
            @Override
            public Dimension getPreferredSize() {
                return img == null ? super.getPreferredSize() : new Dimension(img.getWidth(), img.getHeight());
            }
    
            public void setBackground(BufferedImage value) {
                if (value != img) {
                    this.img = value;
                    repaint();
                }
            }
    
            @Override
            public void invalidate() {
                super.invalidate();
                if (getWidth() > img.getWidth() || getHeight() > img.getHeight()) {
                    scaled = getScaledInstanceToFill(img, getSize());
                } else {
                    scaled = img;
                }
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (scaled != null) {
                    int x = (getWidth() - scaled.getWidth()) / 2;
                    int y = (getHeight() - scaled.getHeight()) / 2;
                    g.drawImage(scaled, x, y, this);
                }
            }
    
        }
    
        public static BufferedImage getScaledInstanceToFill(BufferedImage img, Dimension size) {
    
            double scaleFactor = getScaleFactorToFill(img, size);
    
            return getScaledInstance(img, scaleFactor);
    
        }
    
        public static double getScaleFactorToFill(BufferedImage img, Dimension size) {
    
            double dScale = 1;
    
            if (img != null) {
    
                int imageWidth = img.getWidth();
                int imageHeight = img.getHeight();
    
                double dScaleWidth = getScaleFactor(imageWidth, size.width);
                double dScaleHeight = getScaleFactor(imageHeight, size.height);
    
                dScale = Math.max(dScaleHeight, dScaleWidth);
    
            }
    
            return dScale;
    
        }
    
        public static double getScaleFactor(int iMasterSize, int iTargetSize) {
    
            double dScale = (double) iTargetSize / (double) iMasterSize;
    
            return dScale;
    
        }
    
        public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
    
            return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
    
        }
    
        protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean bHighQuality) {
    
            BufferedImage imgScale = img;
    
            int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
            int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);
    
    //        System.out.println("Scale Size = " + iImageWidth + "x" + iImageHeight);
            if (dScaleFactor <= 1.0d) {
    
                imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
    
            } else {
    
                imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
    
            }
    
            return imgScale;
    
        }
    
        protected static BufferedImage getScaledDownInstance(BufferedImage img,
                int targetWidth,
                int targetHeight,
                Object hint,
                boolean higherQuality) {
    
            int type = (img.getTransparency() == Transparency.OPAQUE)
                    ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
    
            BufferedImage ret = (BufferedImage) img;
            if (targetHeight > 0 || targetWidth > 0) {
                int w, h;
                if (higherQuality) {
                    // Use multi-step technique: start with original size, then
                    // scale down in multiple passes with drawImage()
                    // until the target size is reached
                    w = img.getWidth();
                    h = img.getHeight();
                } else {
                    // Use one-step technique: scale directly from original
                    // size to target size with a single drawImage() call
                    w = targetWidth;
                    h = targetHeight;
                }
    
                do {
                    if (higherQuality && w > targetWidth) {
                        w /= 2;
                        if (w < targetWidth) {
                            w = targetWidth;
                        }
                    }
    
                    if (higherQuality && h > targetHeight) {
                        h /= 2;
                        if (h < targetHeight) {
                            h = targetHeight;
                        }
                    }
    
                    BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
                    Graphics2D g2 = tmp.createGraphics();
                    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
                    g2.drawImage(ret, 0, 0, w, h, null);
                    g2.dispose();
    
                    ret = tmp;
                } while (w != targetWidth || h != targetHeight);
            } else {
                ret = new BufferedImage(1, 1, type);
            }
            return ret;
        }
    
        protected static BufferedImage getScaledUpInstance(BufferedImage img,
                int targetWidth,
                int targetHeight,
                Object hint,
                boolean higherQuality) {
    
            int type = BufferedImage.TYPE_INT_ARGB;
    
            BufferedImage ret = (BufferedImage) img;
            int w, h;
            if (higherQuality) {
                // Use multi-step technique: start with original size, then
                // scale down in multiple passes with drawImage()
                // until the target size is reached
                w = img.getWidth();
                h = img.getHeight();
            } else {
                // Use one-step technique: scale directly from original
                // size to target size with a single drawImage() call
                w = targetWidth;
                h = targetHeight;
            }
    
            do {
                if (higherQuality && w < targetWidth) {
                    w *= 2;
                    if (w > targetWidth) {
                        w = targetWidth;
                    }
                }
    
                if (higherQuality && h < targetHeight) {
                    h *= 2;
                    if (h > targetHeight) {
                        h = targetHeight;
                    }
                }
    
                BufferedImage tmp = new BufferedImage(w, h, type);
                Graphics2D g2 = tmp.createGraphics();
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
                g2.drawImage(ret, 0, 0, w, h, null);
                g2.dispose();
    
                ret = tmp;
                tmp = null;
    
            } while (w != targetWidth || h != targetHeight);
            return ret;
        }
    
    }
    

    It would be a simple matter to also have the image scaled down when the space decreases, but I deliberately decided to maintain the image at it's smallest size.

    The example also makes use of a custom divide and conquer scaling algrotithm in order to generate a high quality scaled result.

提交回复
热议问题