I've seen a number of questions that ask how to rotate a JLabel or image at an arbitrary angle. All I need to do is rotate my text field 90 degrees, but I haven't found an easier way specifically for that angle. I thought I copied the answers correctly, but my text field is not rotating.
Here's an SSCCE of what I'm doing:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class VerticalRotationSSCCE {
private static class VerticalTextField extends JTextField {
private static final long serialVersionUID = 1L;
public VerticalTextField(String text) {
super(text);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
int cx = getWidth() / 2;
int cy = getHeight() / 2;
g2.rotate(1/2 * Math.PI, cx, cy);
super.paintComponent(g2);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.getContentPane().add(new VerticalTextField("Foo"));
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
What am I missing from the answers on how to rotate components?
Actually, yes, it can be done, but will require some additional libraries and access to some source code which has vanished off the net.
Using JXLayer, it is possible to transform live components at runtime...
public class JLayerTransform {
public static void main(String[] args) {
new JLayerTransform();
}
public JLayerTransform() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new ExamplePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ExamplePane extends JPanel {
private JSlider slider;
private FieldPane fieldPane;
private DefaultTransformModel transformModel;
public ExamplePane() {
setLayout(new BorderLayout());
slider = new JSlider(0, 360);
slider.setValue(0);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
transformModel.setRotation(Math.toRadians(slider.getValue()));
}
});
fieldPane = new FieldPane();
transformModel = new DefaultTransformModel();
transformModel.setRotation(Math.toRadians(0));
transformModel.setScaleToPreferredSize(true);
JXLayer<JComponent> rotatePane = TransformUtils.createTransformJXLayer(fieldPane, transformModel);
add(slider, BorderLayout.NORTH);
add(rotatePane);
}
}
public class FieldPane extends JPanel {
public FieldPane() {
setLayout(new GridBagLayout());
JTextField field = new JTextField(10);
field.setText("Hello world");
add(field);
}
}
}
Caveats
This requires JXLayer (I was using version 3), SwingX (I was using version 1.6.4) and Piet Blok's excellent examples, which no longer seem to be available on the net...
I've put all the source code of JXLayer (version 3) and Piet's examples into a single zip and I would suggest, if you are interested, you grab a copy and store it some where safe.
You can't usefully rotate interactive components without also transforming the mouse coordinates, but you can rotate the graphics context to render non-interactive components like JLabel, as shown here.
In your example, 1/2 * Math.PI != Math.PI / 2.
Related
I am trying to make a JFrame with a background that changes slowly over time.
here's my code:
public class RainbowWindow extends Applet implements ActionListener {
private static final long serialVersionUID = 1L;
public void paint(Graphics g) {
Timer timer = new Timer(1000, this);
timer.start();
}
#Override
public void actionPerformed(ActionEvent e) {
Color color = new Color(((int) Math.random()), ((int) Math.random()), ((int) Math.random()), 255);
setBackground(color);
}
}
but all i get is just a black screen
Java Plugin support deprecated and Moving to a Plugin-Free Web
DON'T create a Timer in paint, paint is called EVERY TIME the component needs to be painted and often in quick succession. See Painting in AWT and Swing nd Performing Custom Painting for more details about how painting works in Swing
You sould avoid overriding paint of top level containers like JFrame and Applet, they aren't double buffered and it tends to cause no end of issues, better to start with some kind of component and add it to whatever container you want
JFrame != Applet
Math.random returns a value between 0 and 1, Color expects a values between 0-255
For example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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() {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int red = (int)(Math.random() * 255);
int green = (int)(Math.random() * 255);
int blue = (int)(Math.random() * 255);
Color color = new Color(red, green, blue, 255);
setBackground(color);
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
You should be extending JApplet (not Applet), and adding a component to set the color on, and you shouldn't be starting a Timer in your paint method, and you casting a floating point value to an int isn't going to give you any values in your random color generation. Something like,
public class RainbowWindow extends JApplet implements ActionListener {
private static final long serialVersionUID = 1L;
private JPanel panel = new JPanel();
#Override
public void actionPerformed(ActionEvent e) {
Color color = new Color(((int) (Math.random() * 255)),
((int) (Math.random() * 255)),
((int) (Math.random() * 255)), 255);
panel.setBackground(color);
}
public RainbowWindow() {
Timer timer = new Timer(1000, this);
timer.start();
add(panel);
setVisible(true);
}
}
Which I tested, and it changes the background color to a new random color once a second.
Working on mixer app with multiple sliders. I want to create multiple instances of the same slider side by side for as many channels as I determine (1-16). This will very so I want to just create new side by side instances of VolumeControl from left to right. For now I just they can all work from the same inputs, listeners, etc. I'm just working on getting them to display. Here's what I have in order to create a single instance of JSlider (mostly taken from a demo). NOTE: I'm coming from Android development so I could be going the complete wrong, complicated way!
public class VolumeControl extends JPanel implements ChangeListener {
...
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(VolumeControl::createAndShowGUI);
}
public VolumeControl () {
super(new BorderLayout());
...
JSlider volumeControl = new JSlider(JSlider.VERTICAL,
VOLUME_MIN, VOLUME_MAX, currentVolume);
volumeControl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
add(volumeControl, BorderLayout.LINE_START);
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Volume Control");
VolumeControl animator = new VolumeControl();
frame.add(animator, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
Simply create one or more factory methods which you can use to create and configure the sliders the way you want...
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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 VolmeControlPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class VolmeControlPane extends JPanel {
protected static final int VOLUME_MIN = 0;
protected static final int VOLUME_MAX = 100;
protected static final String SLIDER_CONTROL_KEY = "Slider.controlKey";
public VolmeControlPane() {
setLayout(new GridBagLayout());
JSlider[] sliders = makeSliders(16);
for (JSlider slider : sliders) {
add(slider);
}
}
public JSlider[] makeSliders(int count) {
JSlider[] sliders = new JSlider[count];
for (int index = 0; index < count; index++) {
sliders[index] = makeSlider(VOLUME_MIN, VOLUME_MAX, VOLUME_MIN + (VOLUME_MAX / 2));
sliders[index].putClientProperty(SLIDER_CONTROL_KEY, index);
}
return sliders;
}
public JSlider makeSlider(int min, int max, int value) {
JSlider volumeControl = new JSlider(JSlider.VERTICAL,
min, max, value);
volumeControl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
return volumeControl;
}
}
}
One little trick I added was using putClientProperty to provide a key to identify the slider, you could also use setName. The idea here is when you attach a listener, you can interegate the slider for the key and identify which channel it represents, as a an idea
I Want to capture Image from screen and draw it in JPanel , it works but it is displayed one more time like entering in a loop , I am confused by this , How can I fix it ,Please?
Varibles Iwidth,Ihieght ares above initiaized, but I take the block of code that cause the problem
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Image img;
ImageIcon i = null;
Rectangle screenRect=new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
try {
BufferedImage capture=new Robot().createScreenCapture(screenRect);
capture.getHeight();
capture.getWidth();
i=new ImageIcon(capture);
} catch (AWTException ex) {
Logger.getLogger(TestDrawing.class.getName()).log(Level.SEVERE, null, ex);
}
img = i.getImage();
g.drawImage(img,Iwidth,Ihieght,null);
super.repaint();
}
Never have code like that inside of paintComponent. That method should be reserved for painting and painting only, and is a method that you don't really have full control over since it is called by the JVM in response to both your request, and to requests from the OS, and even if you request a repaint, there's no guarantee that it will be complied with, especially if the requests are stacking up. Also, your GUI's perceived responsiveness will often depend on how quick painting is done, which is while file reading and image capturing should never be done inside of paintComponent.
Instead, you should read the image in as a reaction to some event, perhaps a Timer, then after the image is read, call repaint() and in paintComponent draw the obtained image.
Never call repaint() inside of paintComponent either.
Something like in pseudo code
// inside of the ActionListener of a Swing Timer (if you want to do this repeatedly)
get screen image from robot and feed it into the capture BufferedImage field
consider doing this in a SwingWorker
call repaint() when SwingWorker is done (via a PropertyChangeListener)
Inside of paintComponent:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (capture != null) {
g.drawImage(capture, capture.getWidth(), capture.getHeight());
}
}
Edit
For example:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
import javax.swing.SwingWorker.StateValue;
public class ScreenCaptureTest extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private JButton btn = new JButton(new ScreenCaptureAction(this,
"Capture Screen", KeyEvent.VK_C));
private ImagePanel imagePanel = new ImagePanel();
public ScreenCaptureTest() {
JPanel buttonPanel = new JPanel();
buttonPanel.add(btn);
setLayout(new BorderLayout());
add(new JScrollPane(imagePanel), BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
}
public void setImagePanelImage(BufferedImage img) {
imagePanel.setImage(img);
revalidate();
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
ScreenCaptureTest mainPanel = new ScreenCaptureTest();
JFrame frame = new JFrame("ScreenCaptureTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ImagePanel extends JPanel {
private BufferedImage image;
public void setImage(BufferedImage image) {
this.image = image;
revalidate();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, null);
}
}
#Override
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(), image.getHeight());
}
return super.getPreferredSize();
}
}
class ScreenCaptureAction extends AbstractAction {
private ScreenCaptureTest screenCaptureTest;
public ScreenCaptureAction(ScreenCaptureTest screenCaptureTest, String name,
int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
this.screenCaptureTest = screenCaptureTest;
}
#Override
public void actionPerformed(ActionEvent e) {
setEnabled(false);
final SwingWorker<BufferedImage, Void> mySwingWorker = new SwingWorker<BufferedImage, Void>() {
#Override
protected BufferedImage doInBackground() throws Exception {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
Robot robot = new Robot();
BufferedImage capture = robot.createScreenCapture(new Rectangle(
screenSize));
return capture;
}
};
mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if ("state".equals(pcEvt.getPropertyName())
&& pcEvt.getNewValue() == StateValue.DONE) {
setEnabled(true);
try {
screenCaptureTest.setImagePanelImage(mySwingWorker.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
});
mySwingWorker.execute();
}
}
Edit
Note, that if this were my program, I'd display the image as an ImageIcon in a JLabel as it is much simpler to code. Then you could do away with the ImagePanel class and its paintComponent method, and simply code the main as:
public class ScreenCaptureTest extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private JButton btn = new JButton(new ScreenCaptureAction(this,
"Capture Screen", KeyEvent.VK_C));
//!! private ImagePanel imagePanel = new ImagePanel();
private JLabel screenLabel = new JLabel(); //!!
public ScreenCaptureTest() {
JPanel buttonPanel = new JPanel();
buttonPanel.add(btn);
setLayout(new BorderLayout());
//!! add(new JScrollPane(imagePanel), BorderLayout.CENTER);
add(new JScrollPane(screenLabel), BorderLayout.CENTER); //!!
add(buttonPanel, BorderLayout.SOUTH);
}
public void setImagePanelImage(BufferedImage img) {
//!! imagePanel.setImage(img);
Icon icon = new ImageIcon(img);
screenLabel.setIcon(icon);
//!! revalidate();
//!! repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
ScreenCaptureTest mainPanel = new ScreenCaptureTest();
JFrame frame = new JFrame("ScreenCaptureTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
It's been suggested on this Stack Overflow question that the best way to implement zooming in Swing applications is via the JLayer decorators provided with Java 7.
I've been following the Oracle tutorial and think the best way to do this is by creating my own ZoomUI that extends the LayerUI. My thoughts so far are that this class will have a zoom member variable that is applied before painting the actual component.
Then later on I can then use this same class to catch mouse events and dispatch them to their un-zoomed coordinates.
I'm having a little trouble with the first step and cannot understand why the g2.scale(zoom, zoom) call is having no effect in my SSCCE below.
import javax.swing.*;
import javax.swing.plaf.LayerUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
public class Demo {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
final JLayeredPane panel = new JLayeredPane();
final ZoomUI layerUI = new ZoomUI();
final JLayer<JComponent> jLayer = new JLayer<JComponent>(panel, layerUI);
Button zoomIn = new Button("Zoom In");
zoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.zoom += 0.1;
jLayer.repaint();
}
});
zoomIn.setBounds(0,0,100,50);
panel.add(zoomIn);
Button zoomOut = new Button("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.zoom -= 0.1;
jLayer.repaint();
}
});
zoomOut.setBounds(100,0,100,50);
panel.add(zoomOut);
frame.setPreferredSize(new Dimension(400,200));
frame.add(jLayer);
frame.pack();
frame.setVisible(true);
}
});
}
private static class ZoomUI extends LayerUI<JComponent> {
public int zoom = 1.5; // Changing this value seems to have no effect
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
g2.scale(zoom, zoom);
super.paint(g2, c);
g2.dispose();
}
#Override
public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK | AWTEvent.ACTION_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK
);
}
#Override
public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
}
}
In this example I'm expecting the zoom in/out buttons to grow in size when clicked and when zoomed to respond to mouse events correctly. That is not having to click where they used to reside in order to trigger an event.
Sadly I can't even get them to zoom before clicked by changing the commented line so I'd really appreciate some help!
You immediate problem is you are mixing heavy weight/AWT components with light weight/Swing components...
Button is a native component, meaning that it's painting is out side the control of Swing and therefore is not affected by it.
Instead, use JButton instead.
public class TestJLayerZoom {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
final ZoomUI layerUI = new ZoomUI();
final JLayer<JComponent> jLayer = new JLayer<JComponent>(panel, layerUI);
JButton zoomIn = new JButton("Zoom In");
zoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.zoom += 0.1;
jLayer.repaint();
}
});
panel.add(zoomIn);
JButton zoomOut = new JButton("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
layerUI.zoom -= 0.1;
jLayer.repaint();
}
});
panel.add(zoomOut);
frame.setPreferredSize(new Dimension(400, 200));
frame.add(jLayer);
frame.pack();
frame.setVisible(true);
}
});
}
private static class ZoomUI extends LayerUI<JComponent> {
public double zoom = 2; // Changing this value seems to have no effect
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2 = (Graphics2D) g.create();
g2.scale(zoom, zoom);
super.paint(g2, c);
g2.dispose();
}
#Override
public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer) c;
jlayer.setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK | AWTEvent.ACTION_EVENT_MASK
| AWTEvent.MOUSE_MOTION_EVENT_MASK
);
}
#Override
public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer) c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
}
}
You're next problem will be working out how to translate the mouse events ;)
I am a total Beginner and while I had most of the Application right away. I can t make a background picture for my swing gui.
I ve read that you should do it with the overriding the paint methode, which I did. But somewhere I am making a mistake. Since nothing chances, except that the Button is invisible until I go over it with the Mouse. I tried several things, maybe one of you can see the Problem and help me out? Thanks a lot :)
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
/**
*
* #author Shore
*/
public class GUI extends JFrame implements ActionListener {
Container c;
JButton überprüfungsButton = new JButton();
JTextField eingabeTextField = new JTextField();
JTextArea ausgabeTextFeld = new JTextArea();
Image hintergrundBild;
public GUI(){
this.hintergrundBild = Toolkit.getDefaultToolkit().getImage( "Bild2.jpg" );
c = getContentPane();
c.setLayout(new BorderLayout());
c.setBackground(Color.LIGHT_GRAY);
überprüfungsButton = new JButton("Test");
überprüfungsButton.addActionListener(this);
c.add(überprüfungsButton, BorderLayout.CENTER);
eingabeTextField = new JTextField(40);
c.add(eingabeTextField, BorderLayout.PAGE_START);
eingabeTextField.setOpaque(false);
ausgabeTextFeld = new JTextArea(30,30);
c.add(ausgabeTextFeld, BorderLayout.PAGE_END);
ausgabeTextFeld.setOpaque(false);
}
public static void main(String[] args) {
GUI fenster = new GUI();
fenster.setTitle("Für");
fenster.setSize(800, 800);
fenster.setVisible(true);
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
protected void paintComponent(Graphics g) {
if (hintergrundBild != null) {
g.drawImage(hintergrundBild, 0, 0, getWidth(), getHeight(), null);
}
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == überprüfungsButton){
Thanks whoever took the Time to take a look at it.
Update: I actually can resolve the Problem with Netbeans and the swing-GUI Creator.
However, I am still very curious!
If you could still help me, I would appreciate it.
JFrame does not override paintComponent so Swing will not invoke it.
Adding the #Override annotation above the method will show that the method is not contained in the super class.
The simplest way to implement a background image is to create a subclass of JPanel and override paintComponent there.
Update:
There are many examples on the web where the class JFrame is extended. From a design point is view this is not necessary as you typically do not any new functionality to the frame. For this reason just creating a direct instance & using is better (shown):
public class BackGroundProblem {
public static void main(String[] args) throws IOException {
final Image image = ImageIO.read(new URL("http://news.beloblog.com/ProJo_Blogs/architecturehereandthere/hallstattsumm.jpg"));
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
JPanel backgroundPanel = new ImagePanel(image);
backgroundPanel.add(new JButton("Sample Button 1"));
backgroundPanel.add(new JButton("Sample Button 2"));
frame.add(backgroundPanel);
frame.setLocationByPlatform(true);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class ImagePanel extends JPanel {
private static final long serialVersionUID = 1L;
private Image image;
ImagePanel(Image image) {
this.image = image;
};
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
}