BG image with Swing without overriding paintComponent - java

I'm currently designing a menu with several screens with multiple buttons on each screen. To use buttons on top of the background image, which is in a jLabel (by default, I can't put buttons on TOP of the jLabel), I used GridBagLayout with two panels/menu screen, one panel containing the buttons (opaque = false) and one panel with the background image, or jLabel. In order to switch the current panels being displayed, depending on where the user is in the menu, I made each menu screen (aka. every 2 panels) in separate methods, not classes.
Now, I've come to the point where I'm working on parts of the interface that are unnecessarily complicated, and I don't feel GridBag will serve my purposes, so I was wondering if there was a different way to draw my background image, still being able to use my buttons on top of the image.
The most popular way I looked up was overriding the paintComponent method, but I can't do that, since I've made my JPanels in separate methods, not classes. They're all contained in my original JFrame.
Help would be greatly appreciated, thank you!
Just added this code, but my background remains white for some reason? Trying the other suggestion right now, thanks guys!
private void mainPanel() {
icon = new ImageIcon(getClass().getResource("/phantasma/menuv1.png"));
mainContainer1 = new javax.swing.JPanel() {
#Override
protected void paintComponent(Graphics g) {
g.drawImage(icon.getImage(), 0,0, null);
super.paintComponent(g);
}
};

BG image with Swing without overriding paintComponent
I have no idea why all the postings suggest doing custom painting for this. You would only do custom painting if you need to automatically scale the background image.
If you want the image painted at its real size then use a JLabel.
I can't put buttons on TOP of the jLabel),
Sure you can. Just set a LayoutManager for the JLabel and then you can add any component to it the same way you add components to a panel.

In my comment above I state:
You can always create an anonymous inner JPanel-derived class and override the paintComponent method there if need be.
As an example of what I mean, you can override paintComponent in any JPanel that you create whether it's derived from a stand-alone class or created within a method. For e.g.,
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
public class AnonInnerPanel {
private static void createAndShowGui() {
JPanel mainPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
};
JFrame frame = new JFrame("AnonInnerPanel");
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();
}
});
}
}

Related

Why is a Background Image of a JFrame only showing after i resize the window in Swing?

I have noticed some weird behaviour with setting an image as a background for my JFrames. I want to have a background image for both Frames display when they are created.
When i create Window1 inside my main method it only shows the background after i manually resize the window. When i click the button in Window1 and create window2 inside Window1, Window2 displays the background image correctly. When i create Window2 inside my main method the background image also does not show correctly.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TestStart {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Window1();
}
});
}
}
public class Window1 extends JFrame {
Image img = Toolkit.getDefaultToolkit().getImage("C:\\Users\\Tim\\Desktop\\water.jpg");
public Window1() {
super("gridtest");
setSize(600, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
this.setContentPane(new JPanel(){
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(img,0,0,getWidth(),getHeight(),null);
}
});
JButton btn = new JButton("klick");
add(btn, BorderLayout.CENTER);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
new Window2();
}
});
pack();
setVisible(true);
}
}
public class Window2 extends JFrame {
Image img = Toolkit.getDefaultToolkit().getImage("C:\\Users\\Tim\\Desktop\\water.jpg");
public Window2() {
super("gridsecond");
setSize(600, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
this.setContentPane(new JPanel(){
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(img,0,0,getWidth(),getHeight(),null);
}
});
pack();
setVisible(true);
}
}
I tried adding in revalidate()/repaint() calls and calling the paint method at different points in my code, but except for the weird behaviour i described above, i cant get the background image to show without resizing my window.
Disclaimer: I know it's not good practice to create a new JFrame for every window, but i am not able to change the whole project structure, so im stuck with this.
So, a series of issues that might be causing your issue...
First, using Toolkit.getDefaultToolkit().getImage
Image img = Toolkit.getDefaultToolkit().getImage("C:\\Users\\Tim\\Desktop\\water.jpg");
The problem with this is, the image loading is down on a seperate thread. This means that when getImage returns the image may or may not have actually been loaded
Second, when calling drawImage, you pass null as the ImageConsumer, so the component won't be notified of changes to the images "loaded" state and won't be able to schedule updates to the component accordingly...
this.setContentPane(new JPanel(){
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(img,0,0,getWidth(),getHeight(),null);
}
});
Three, this is more of a side effect, but, you've set the contentPane manually using a JPanel, which by default uses a FlowLayout, so using BorderLayout constraints is actually kind of pointless.
setLayout(new BorderLayout());
this.setContentPane(new JPanel(){
//...
JButton btn = new JButton("klick");
add(btn, BorderLayout.CENTER);
Besides, BorderLayout is the default layout manager used by window based components ;)
So, how would you fix this issue? Well, immediately, you could pass this as the ImageConsumer...
g.drawImage(img,0,0,getWidth(),getHeight(),this);
This will allow the component to monitor the loading state of the image and trigger repaints as required.
A longer term solution would be to stop using Toolkit.getDefaultToolkit().getImage (and by extension, ImageIcon), as they can be annoying, and start using ImageIO instead.
Apart from supporting a wider range of image formats, the API won't return till the image is fully loaded or an error occurred (try diagnose loading issues with the other APIs 🙄)
See Reading/Loading images for more details

Repaint never reaches paintComponent();

I'm back with a problem about java-graphics by swing... I want to paint some stuff at a jframe, here is the code:
PaintUtil-class:
public class PaintUtil extends JPanel{
public PaintUtil(){
this.setFocusable(true);
this.requestFocus();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
System.out.println("Repainted");
g.drawstuff...
}
}
Main-class:
public static PaintUtil util = new PaintUtil();
JFrame frame = new JFrame();
frame.setSize(500,600);
frame.setRezisable(false);
frame.add(util);
frame.setDefaultCloseOperation( 3 );
frame.getContentPane().setColor(Color.BLACK);
setup(); //This add some buttons
frame.setVisible(true);
util.repaint(); //not working
util.paintComponent(frame.getGraphics()); //works
Can you guys help me?
There is no error, no message in the console, just nothing
frame.setLayout(null);
Don't use a null layout. Swing was designed to be used with layout managers. Get rid of that statement.
By default the size of your panel is (0, 0) so there is nothing to paint.
You will need to override the getPreferredSize() method of your panel so the layout manager can do its job.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.

Strange repositioning using repaint();

I have a JSlider in a JPanel that return me a value of R-G-B .
I create it, in the Costructor of JPanel. I draw in same Panel (using paintComponent) a little circle, and I change his color using the Slider. I want that the color change in contemporany of slider shift.
So, i use the method repaint.. Next to Panel there is another Panel, with two button.. If I use method repaint in first panel , the buttons of second panel duplicated in the topLeft of First Panel. Why? Thank's you.
First Panel:
public class OptionsPanel extends JPanel {
static JSlider RBG = new JSlider(0,255);
OptionsPanel(){
this.setVisible(false);
this.setSize(350,1000);
this.setLayout(null);
this.setBackground(new Color(200,200,0));
Main.f1.add(this);
RBG.setVisible(true);
RBG.setSize(255,50);
RBG.setLocation(30,240);
this.add(RBG);
LotL lotl = new LotL();
Button save = new Button("Save");
save.setVisible(true);
save.setSize(100,40);
save.setLayout(null);
save.setLocation(60,300);
save.addActionListener(lotl);
save.setBackground(Color.yellow);
save.identificatore=3;
this.add(save);
}
boolean draw=false;
#Override
public void paintComponent(Graphics g){
g.drawOval(50,100,70,70);
g.setColor(new Color(RBG.getValue(),180,200));
g.fillOval(50,100,70,70);
repaint();
}
}
Second Panel:
public class FirstPanel extends JPanel{
FirstPanel(){
this.setVisible(true);
this.setSize(1000,1000);
this.setLayout(null);
this.setBackground(new Color(255,200,180));
Main.f1.add(this);
Button start = new Button("Start Game!");
Button options = new Button("Options");
LotL LotL = new LotL();
start.setVisible(true);
start.setSize(200,80);
start.setLayout(null);
start.setLocation(400,450);
start.addActionListener(LotL);
start.setBackground(Color.green);
start.identificatore=1;
this.add(start);
options.setVisible(true);
options.setSize(200,70);
options.setLayout(null);
options.setLocation(400,550);
options.addActionListener(LotL);
options.setBackground(Color.green);
options.identificatore=2;
this.add(options);
}
}
You've broken the paint chain...
#Override
public void paintComponent(Graphics g){
g.drawOval(50,100,70,70);
g.setColor(new Color(RBG.getValue(),180,200));
g.fillOval(50,100,70,70);
repaint();
}
Graphics is a shared resource, which gets passed to ALL the components that are painted during a given paint cycle.
One of the jobs of paintComponent is to prepare the Graphics context for painting, but filling with the components background color.
You MUST call super.paintComponent before performing any custom painting.
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawOval(50,100,70,70);
g.setColor(new Color(RBG.getValue(),180,200));
g.fillOval(50,100,70,70);
}
Also, there is never any need for paintComponent to be public, no one should ever be calling directly and NEVER modify the state of a component from within any paint method which may trigger a repaint, you will get yourself into a infinite loop which will eventually consume your CPU and make you computer unusable.
Take a look at Painting in AWT and Swing and Performing Custom Painting for more details
You should also avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify

Add GUI with Swing, but unsure of the sequence to take

I'm building a GUI for a data processing algorithm. I can instantiate the window, give it a background, title, etc., but when I try adding panels to it, I run into trouble. What I'm really looking for more than a proofreader is a suggestion for the sequence in which to build, configure, and add objects in Java Swing so that they behave correctly, in a generic sense. So, is this the best way to build a JFrame with a different-colored panel in it?
Declare JFrame
Set JFrame color (background color)
Declare JPanel (box to represent data graphically)
Set JPanel color (box color)
Add JPanel to JFrame
Set JFrame to visible = true
It makes sense intuitively but it doesn't seem to work, no matter what I do. I've found step-by-step instructions elsewhere but they tend to explain what to type more than why you're typing it, so you get a very narrow understanding of what's going on. Thanks for any help!
Below is the full code; I hesitated to post it because I'd begun experimenting with Graphics2D and it isn't well-commented, but if it helps:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class GUI extends JFrame
{
JFrame mainWindow = new JFrame();
JPanel backgroundPanel = new JPanel();
JPanel subPanel = new JPanel();
Color background = new Color(40,40,40);
Color subWindow = new Color(255, 255, 255);
TitledBorder title = BorderFactory.createTitledBorder("title");
Rectangle rect1 = new Rectangle(10, 10, 40, 40);
Graphics2D g;
public static void main (String[] args)
{
new GUI();
}
public GUI()
{
initializeGUI();
}
private void initializeGUI()
{
mainWindow.setSize(1340, 880);
backgroundPanel.setBackground(background);
subPanel.setBackground(subWindow);
subPanel.setBorder(title);
mainWindow.setTitle("Ed");
mainWindow.setLocationRelativeTo(null);
mainWindow.setDefaultCloseOperation(EXIT_ON_CLOSE);
mainWindow.add(backgroundPanel);
backgroundPanel.add(subPanel);
updateGUI();
}
public void updateGUI()
{
mainWindow.setVisible(false);
mainWindow.setVisible(true);
}
public void paintComponent(Graphics g)
{
this.g.setColor(subWindow);
this.g.fill(rect1);
this.g = (Graphics2D) g;
}
}
Let's break this down....
public class GUI extends JFrame {
JFrame mainWindow = new JFrame();
There is no need to extend from JFrame as you are neither using it nor are you adding any value to the class.
This...
public void paintComponent(Graphics g) {
this.g.setColor(subWindow);
this.g.fill(rect1);
this.g = (Graphics2D) g;
}
is doing nothing and will never be called, as nothing you've extended from implements a paintComponent method (that is, JFrame does not have a paintComponent methd) (and you class is not attached to anything displayed on the screen anyway). Also, you should NEVER maintain a reference to ANY Graphics context you did not create yourself.
The reason that subPanel is appearing so "small" is because it has not definable size, aside from the border.
You could rectify this in one of three ways...
You could change the layout manager of backgroundPanel to something like BorderLayout
You could override the getPreferredSize method of the subPanel to return a more suitable size or
You could add other components to it and let the layout manager figure it out...
In any case, you should have a look at Laying Out Components Within a Container.
You should also have a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting is done in Swing

PaintComponent not being called netbeans GUI

I am completely new to netbean's graphics system, and have been struggling with a java textbook. I am trying to produce a simple program to display some things, and have followed the book exactly as it wants me to. I found in my research a bunch of other people with a similar issues. These people tend to be told to use dimensions and preferredSize methods, though neither of these are mentioned in the section of the book I am trying to reproduce in java. The following is my code:
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
JFrame frame = new JFrame(); //create frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //makes x button end program
frame.setSize(300,200); //determine the size of the frame
ImageIcon image = new ImageIcon("panda.jpg");
ColorPanel p = new ColorPanel(Color.pink, image);
Container pane = frame.getContentPane();
pane.add(p);
frame.setVisible(true); //make frame show up
}
}
public class ColorPanel extends JPanel {
ImageIcon image;
public ColorPanel(Color c, ImageIcon i){
setBackground(c);
image = i;
}
#Override
public void paintComponents(Graphics g){
super.paintComponents(g);
setPreferredSize(new Dimension(100,100));
System.out.println("Blah!");
g.setColor(Color.blue);
g.drawRect(10,25,40,30);
}
}
I suppose there is a small typo in your code. You definitely mean to override paintComponent() and not paintComponents(). The first is called to paint the component, the second one to paint all components contained in your panel. Since there is none it will not be called.
These people tend to be told to use dimensions and preferredSize methods
You should not really use setPreferredSize(). Instead, you should override the getPreferredSize() method to return a proper value.
Also, you should never invoke setPreferredSize() in the paintComponent() method or change any property of the class in the paintComponent() method.

Categories

Resources