问题
I've been struggling to do something I guess pretty simple :
I want to create a form (JTextField) with a background image. In order for the form not to cover the background Image, I'm using a JLayeredPane. I've been trying different stuff, nothing seems to work : for some reason, I'm either displaying only the background, or only the JTextField, but never both. My goal would be to have a background image that never changes, and just use my buttons / textfields on top of it.
package gestion;
import java.awt.*;
import javax.swing.*;
@SuppressWarnings("serial")
public class Main extends JFrame{
JLayeredPane layeredPane;
JPanel board;
JPanel background;
public Main(){
super("Test");
background = new JPanel();
layeredPane = new JLayeredPane();
board = new JPanel();
// Creating frame with LayeredPane
Dimension boardSize = new Dimension(1280, 1024);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setSize(boardSize.width, boardSize.height);
this.setVisible(true);
this.setLocationRelativeTo(null);
layeredPane.setPreferredSize( boardSize );
this.add(layeredPane);
// Add a background to the Layered Pane
JLabel picLabel = new JLabel(new ImageIcon("background.jpg"));
background.add(picLabel);
background.setPreferredSize(boardSize);
background.setBounds(0,0,boardSize.width, boardSize.height);
layeredPane.add(background, JLayeredPane.DEFAULT_LAYER);
// Add a JTextField
final JTextField jtf = new JTextField("Default Value");
Font police = new Font("Arial", Font.BOLD, 14);
jtf.setFont(police);
jtf.setPreferredSize(new Dimension(600, 800));
background.setBounds(0,0,boardSize.width, boardSize.height);
jtf.setForeground(Color.BLUE);
board.add(jtf);
layeredPane.add(board, JLayeredPane.PALETTE_LAYER);
}
public static void main(String[] args)
{
new Main();
}
}
Only the image seems to appear, and for some reason (My best bet is dark magic) the JTextField is not there. Any ideas or help would be greatly appreciated ! Thank you !
回答1:
Any time you rely on anything that uses a null
layout (like JLayeredPane
), you will run into issues.
You should use something like...
board.setBounds(new Rectangle(new Point(0, 0), board.getPreferredSize()));
layeredPane.add(board, JLayeredPane.PALETTE_LAYER);
to set size and location of the board
.
There are also a rafter of other issues with null
layouts which just make them a complete pain in the code and end up wasting more time than they save...
Frankly, a simpler and more usable solution would be to create a custom component which could paint the background image for you, this way, you can use what ever layout manager(s) you need and not suffer from these issues. Before anyone jumps all over me, use a you "can" use a layout manager with JLayeredPane
, but this then introduces more issues with having to get components to overlap so the background layer can act as the background...just more of a mess
Also, before anyone jumps all over me, you could use a JLabel
as the background component, setting a layout manager on it and adding your components to it, but the JLabel
doesn't calculate it's required size based on the child components it contains, but instead, uses the icon
and text
properties. This might not be an issue if your background image is sufficiently big enough, but always seems like a weak point waiting to break.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
BackgroundPane bgPane = new BackgroundPane();
bgPane.setLayout(new GridBagLayout());
add(bgPane);
try {
BufferedImage bg = ImageIO.read(new File("C:\\Users\\shane\\Dropbox\\MegaTokyo\\thumnails\\megatokyo_omnibus_1_3_cover_by_fredrin-d4oupef.jpg"));
bgPane.setBackgroundImage(bg);
} catch (IOException ex) {
ex.printStackTrace();
}
JLabel show = new JLabel("Bananas are yellow");
show.setOpaque(true);
show.setForeground(Color.RED);
show.setBackground(Color.YELLOW);
show.setBorder(new EmptyBorder(20, 20, 20, 20));
bgPane.add(show);
}
}
public class BackgroundPane extends JPanel {
private BufferedImage img;
@Override
public Dimension getPreferredSize() {
BufferedImage img = getBackgroundImage();
Dimension size = super.getPreferredSize();
if (img != null) {
size.width = Math.max(size.width, img.getWidth());
size.height = Math.max(size.height, img.getHeight());
}
return size;
}
public BufferedImage getBackgroundImage() {
return img;
}
public void setBackgroundImage(BufferedImage value) {
if (img != value) {
BufferedImage old = img;
img = value;
firePropertyChange("background", old, img);
revalidate();
repaint();
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage bg = getBackgroundImage();
if (bg != null) {
int x = (getWidth() - bg.getWidth()) / 2;
int y = (getHeight() - bg.getHeight()) / 2;
g.drawImage(bg, x, y, this);
}
}
}
}
This implementation is missing things like auto scaling or repeating, but you get the idea
来源:https://stackoverflow.com/questions/28889667/create-a-form-with-a-background-image-jlayeredpane