Paint two overlaid JLabels - java

I want to paint a JLabel over another.
I override paint() so that a second JLabel is painted after calling super.paint().
However this second JLabel is not painted at all (because it is not showing, I think).
How can I achieve that effect?
public class OverlaidJLabel extends JLabel {
private String upperText;
public OverlaidJLabel(){
}
public void setUpperText(String upperText){
this.upperText = upperText;
}
#Override
public void paint(Graphics g){
super.paint(g);
JLabel upperLabel = new JLabel(upperText);
upperLabel.paint(g);
}
}

Some big problems here:
You don't want to override paint in this situation but rather paintComponent
You never want to create components within a painting method. These methods should be for painting and painting only as you'll be creating components many times (since painting methods can be called many times -- and completely out of your control) when they only should be created once, and you don't want to ever slow painting down since this hampers the perceived responsiveness of your GUI.
It makes no sense to create a component that is never added to the GUI, such as your upperLabel. Calling its paint method will achieve absolutely nothing (as you're finding out).
In general, you will want to favor composition over inheritance.
Much better:
If you want to have text on top of text, you could create two JLabels (separately), and place them on top of each other using an appropriate layout manager or using a JLayeredPane.
Or you can do your painting and your painting overlay in a single component, likely the paintComponent override of a JPanel.
For example, one possible overly can use a JLayeredPane that holds two JLabels. A simple example below uses JLabels that hold only text, although you could also implement with ImageIcons, and allow changing each JLabel's font, foreground color, etc...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class MyLabel extends JLayeredPane {
// labels to hold texts. label1 is the lower label
private JLabel label1 = new JLabel();
private JLabel label2 = new JLabel();
public MyLabel(String text1, String text2) {
// just so I can see the text separately:
label2.setForeground(Color.RED);
// set text
label1.setText(text1);
label2.setText(text2);
// JPanels to hold the labels. GridBagLayout will center the labels
JPanel baseComponent1 = new JPanel(new GridBagLayout());
JPanel baseComponent2 = new JPanel(new GridBagLayout());
// add labels to the JPanels
baseComponent1.add(label1);
baseComponent2.add(label2);
// have to be able to see through the JPanels
baseComponent1.setOpaque(false);
baseComponent2.setOpaque(false);
// add to the JLayeredPane label2 above label1
add(baseComponent1, JLayeredPane.DEFAULT_LAYER);
add(baseComponent2, JLayeredPane.PALETTE_LAYER);
// If the overall component resizes, resize the
// container base JPanels
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
baseComponent1.setSize(MyLabel.this.getSize());
baseComponent2.setSize(MyLabel.this.getSize());
repaint();
}
});
}
// preferred size depends on the largest dimensions
// of the two JLabels
#Override
public Dimension getPreferredSize() {
Dimension size1 = label1.getPreferredSize();
Dimension size2 = label2.getPreferredSize();
int w = Math.max(size1.width, size2.width);
int h = Math.max(size1.height, size2.height);
return new Dimension(w, h);
}
}
and it can be tested like so:
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class MyLabelTest extends JPanel {
public MyLabelTest() {
add(new MyLabel("Test .............................. String 1", "Number 2"));
}
private static void createAndShowGui() {
MyLabelTest mainPanel = new MyLabelTest();
JFrame frame = new JFrame("MyLabelTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
My next iteration would be to add mutator methods to MyLabel to allow changing font, text, and foreground:
public void setFont1(Font font) {
label1.setFont(font);
}
public void setFont2(Font font) {
label2.setFont(font);
}
public void setText1(String text) {
label1.setText(text);
}
public void setText2(String text) {
label2.setText(text);
}
public void setForeground1(Color fg) {
label1.setForeground(fg);
}
public void setForeground2(Color fg) {
label2.setForeground(fg);
}

Related

JScrollPane not showing content until mouse scroll or frame resizing

I have a JScrollPane with a JPanel in it that works perfectly, except for one flaw. When I run the program, the JScrollPane is added and shows up on its parent panel just fine, however the content inside it does not show up.
If I resize the window or if I scroll on the JPanel the content shows up immediately. I have also checked and the dimensions are correct for both the JPanel and the JScrollPane. Repaint is also called so I don't think I'm missing anything there.
I did also look at this question but it didn't help:
JScrollPane doesn't show scroll bars when JPanel is added
Null layouts being used are intentionally, I'm doing my own formatting instead to account for multiple size screens. Thank you ahead of time!
class FilesPanel extends JPanel
{
public JScrollPane scroller;
private FileHolderPanel holder;
public FilesPanel()
{
//setLayout(null);
setBackground(extraLight);
holder = new FileHolderPanel();
System.out.println(holder.getWidth() + " " + holder.getHeight() + " " + holder.getX() + " " + holder.getY() + " " + holder.getBounds());
scroller = new JScrollPane();
JViewport viewport = new JViewport();
viewport.add(holder);
scroller.setViewport(viewport);
scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroller.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scroller.setBorder(BorderFactory.createEmptyBorder());
scroller.setBackground(blue);
add(scroller);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
scroller.setLocation(0, 0);
scroller.setSize(filesWidth, frame.getHeight() - 40);
}
class FileHolderPanel extends JPanel
{
public FileHolderPanel()
{
setBackground(extraLight);
setLayout(null);
setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
}
}
}
The "main" issue is the fact that FileHolderPanel has not concept of the size it might like to be, so the JScrollPane has no really of how large it needs to be or when it should display its scroll bars. This is a bit of guess work, as you've not provided a fully runnable example.
Something like the below example works fine...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new ContentPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ContentPane extends JPanel {
public ContentPane() {
setLayout(new BorderLayout());
JScrollPane scrollPane = new JScrollPane(new FileHolderPane());
scrollPane.setBorder(BorderFactory.createEmptyBorder());
scrollPane.setBackground(Color.BLUE);
add(scrollPane);
}
}
public class FileHolderPane extends JPanel {
public FileHolderPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
add(new JLabel("All your content is belong to us"));
}
#Override
public Dimension getPreferredSize() {
// This will make the panel a fixed size, so beware of that
return new Dimension(400, 400);
}
}
}
If you need more control, then you'll probably need to look at the Scrollable interface, but that's another level of complexity

When i interchange the commented lines , i receive different output.

I am learning java abstract window toolkit and i am stuck in this code.When i interchange the commented line, the output changes.Any Explanation for both the cases will be appreciated.
import java.awt.*;
public class guibutton
{
public guibutton()
{
Frame f = new Frame("Panel Example");
Panel panel = new Panel();
panel.setBounds(40,80,200,200);
panel.setBackground(Color.gray);
f.add(panel);
f.setVisible(true); ////////////////this line
f.setLayout(null); /////////////////this line
f.setResizable(true);
f.setSize(400,400);
}
public static void main(String args[])
{
new guibutton();
}
}
This line:
f.setVisible(true);
renders your GUI in its current state, one where the JFrame's default BorderLayout is in force. Note that BorderLayout ignores the setBounds(...) method.
This line:
f.setLayout(null);
removes the JFrame contentPane's BorderLayout, and so your GUI is rendered without the layout, changing the positioning of the added JPanel -- the setBounds(...) method call here is respected.
If you call this after the GUI has been rendered, it won't have an effect, unless you do something that triggers the layout managers to re-layout the components, such as re-size the GUI.
Myself, I wouldn't use AWT but would use Swing, I'd draw the rectangle within the paintComponent method of a JPanel, using a Rectangle object. This way, I could monitor the mouse in relation to the rectangle, and change its state. For instance, try out this program, and see what happens to the rectangle when the mouse hovers over it:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class GuiButton2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int RECT_X = 40;
private static final int RECT_Y = 80;
private static final int RECT_W = 200;
private static final Color DEFAULT_RECT_COLOR = Color.GRAY;
private static final Color HOVER_RECT_COLOR = Color.PINK;
private Rectangle rectangle = new Rectangle(RECT_X, RECT_Y, RECT_W, RECT_W);
private boolean hover = false;
public GuiButton2() {
setPreferredSize(new Dimension(PREF_W, PREF_H));
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
// hover true if mouse is hovering over the rectangle
hover = rectangle.contains(e.getPoint());
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// if hover true -- use hover color, otherwise use default color
Color c = hover ? HOVER_RECT_COLOR : DEFAULT_RECT_COLOR;
g2.setColor(c);
g2.fill(rectangle); // draw rectangle
}
private static void createAndShowGui() {
GuiButton2 mainPanel = new GuiButton2();
JFrame frame = new JFrame("GUI Button");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Side note 1:
While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Side note 2:
You will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.

Java JComponent paint --Almost working

I almost have the repaint() Jcomponent working. I had it working and then tried to make an absolute positioning and now it doesn't work.
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
public class DDHGenericFrame {
private static final long serialVersionUID = 1L;
DDHGenericPanel d = new DDHGenericPanel();
//DDHCircleOne o = new DDHCircleOne();
public DDHGenericFrame() {
initUI();
}
public final void initUI() {
JFrame frame = new JFrame("AbsoluteLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Draw Circle");
frame.setBackground(Color.green);
frame.setLayout(null);
frame.setSize(300,425);
frame.add(d);
// frame.add(o);//didn't work neither
frame.setVisible(true);
}
public static void main(String[] args) {
DDHGenericFrame ex = new DDHGenericFrame();
}
}
Class 2: (This is the JComponent that I am trying to set)
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
public class DDHTestDraw extends JComponent {
public DDHTestDraw () {
settPreferredSize();
}
public void settPreferredSize() {
Dimension d = new Dimension(25,25);
setPreferredSize(d);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Text",20,20);
}
}
I added my component to the Container and then added the container to the JPanel and then the JPanel is added to the JFrame. I would think that this should work. I have a set preferred size. I had it working once and now it doesn't work.
I want to be able to make a component that is a circle. I want that circle to be able to be drawn any where on a Jframe, and then I want that circle to be able to move based on a a certain length of time. I am going to make a game that has circles dropping from the top and then falling to the bottom. When I had it working I did have the circle being written to the JPanel which is a complex piece of code. But I have had to go back to the old simple method of writing a single graphical word.
When you use null layout, you are completely responsible for making sure that components added have proper location and size (not preferredSize) set.
You should almost never use null layout.
Wouldn't this sort of thing work better by creating a logical class to represent the Circle, not a component? Then your drawing JPanel could hold a collection of logical circles, and the drawing JPanel could be responsible for drawing each Circle in its paintComponent method.
Edit
Your comments/my replies:
when you say never use an absolute layout, the company that I worked for always used a absolute layout only.
There are times when it is useful, but not for creating a typical component-filled GUI. Otherwise the GUI becomes very hard to modify and maintain.
When you mean a logical class you mean a class that just creates one circle.
Yes, and that holds all the necessary properties of that circle such as its Color, location, movement, etc..
Then the Jpanel would draw each circle.
Yes. I would imagine the drawing JPanel having an ArrayList<MyCircle> of them, and the paintComponent method iterating througgh this List.
when you say Size this is a property in JComponent.
I think that it is a property of Component, JComponent's parent. If you use null layout, then all components must have their size and location specified. Otherwise the component defaults to a location of [0, 0] and a size of [0, 0].
Edit 2
public Dimension Size(int a, int b) {
Dimension d = new Dimension();
d.width = a;
d.height = b;
return d;
}
This is the code that I used for the preferred size. I am at a lost why this doesn't work.
This code has no effect on either the size or the preferredSize properties of Component/JComponent. It doesn't surprise me that it will not help you. You would either have to override getSize() or getPreferredSize() or explicitly call setSize(...) or getPreferredSize(...) to change the state of the properties.
I am going to try it with a different layout manager and see but I would see the difference between one layout manager or another.
I'm not sure how to interpret this.
Edit 3
You state:
I worked at one company and we used absulute layouts all of the time. How would an absolute layout not work as good as, say BorderLayout(). To me the BorderLayout() are harder to implement. Or is it that you use a Jframe() with a BorderLayout, and then insert a Jpanel into an already existing position that is already also a BorderLayout(). I always have trouble getting my buttions and positions correct in a layout that is something different than a BorderLayout(). Can you post an example that would be easier to use than
I'm guessing you want an example of where use of layout managers is easier than using absolute positioning.
Let's take the example of a very simple calculator, one with buttons for numeric input and simple operations. Again this example is very basic, and is non-functional, but serves to illustrate the use of layouts. I could easily place my buttons in a GridLayout-using JPanel, and then place that button JPanel into a BorderLayout-using JPanel at the BorderLayout.CENTER position with the JTextField, display, placed in the same BorderLayout-using JPanel at the BorderLayout.PAGE_START position:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.*;
public class CalcEg {
private static final float BTN_FONT_SIZE = 20f;
private static final String[][] BTN_LABELS = {
{"7", "8", "9", "-"},
{"4", "5", "6", "+"},
{"1", "2", "3", "/"},
{"0", ".", " ", "="}
};
private static final int GAP = 4;
private JPanel mainPanel = new JPanel(new BorderLayout(GAP, GAP));
private JPanel buttonPanel = new JPanel();
private JTextField display = new JTextField();
public CalcEg() {
int rows = BTN_LABELS.length;
int cols = BTN_LABELS[0].length;
buttonPanel.setLayout(new GridLayout(rows, cols, GAP, GAP));
for (String[] btnLabelRow : BTN_LABELS) {
for (String btnLabel : btnLabelRow) {
if (btnLabel.trim().isEmpty()) {
buttonPanel.add(new JLabel());
} else {
JButton btn = createButton(btnLabel);
buttonPanel.add(btn);
}
}
}
display.setFont(display.getFont().deriveFont(BTN_FONT_SIZE));
display.setEditable(false);
display.setFocusable(false);
display.setBackground(Color.white);
mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.add(buttonPanel, BorderLayout.CENTER);
mainPanel.add(display, BorderLayout.PAGE_START);
}
private JButton createButton(String btnLabel) {
JButton button = new JButton(btnLabel);
button.setFont(button.getFont().deriveFont(BTN_FONT_SIZE));
return button;
}
public JComponent getMainComponent() {
return mainPanel;
}
private static void createAndShowGui() {
CalcEg mainPanel = new CalcEg();
JFrame frame = new JFrame("CalcEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
This would result in a calculator that looks like so:
Now, sure you could say that you could produce this with null layout and setbounds(...), and that's all well and good, but now say that you're not satisfied with this calculator, and now desire that it has some scientific calculation functionality. Say you now want to add buttons for square, square root, exponential, and logarithm, but not only that, say you wish to add the buttons below the display and above the numeric and basic operations buttons. If you were to do this with null layout, you would have to reposition all the components below and to the right of any new components added, and you'd have to expand the size of the JTextField, all calculations that are tedious and prone to error.
If you used layout managers, you would instead only need to add one line of code, actually an additional row to an array:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.*;
public class CalcEg {
private static final float BTN_FONT_SIZE = 20f;
private static final String[][] BTN_LABELS = {
{"sqr", "sqrt", "exp", "log"}, // ******* Line Added Here *********
{"7", "8", "9", "-"},
{"4", "5", "6", "+"},
{"1", "2", "3", "/"},
{"0", ".", " ", "="}
};
private static final int GAP = 4;
private JPanel mainPanel = new JPanel(new BorderLayout(GAP, GAP));
private JPanel buttonPanel = new JPanel();
private JTextField display = new JTextField();
public CalcEg() {
int rows = BTN_LABELS.length;
int cols = BTN_LABELS[0].length;
buttonPanel.setLayout(new GridLayout(rows, cols, GAP, GAP));
for (String[] btnLabelRow : BTN_LABELS) {
for (String btnLabel : btnLabelRow) {
if (btnLabel.trim().isEmpty()) {
buttonPanel.add(new JLabel());
} else {
JButton btn = createButton(btnLabel);
buttonPanel.add(btn);
}
}
}
display.setFont(display.getFont().deriveFont(BTN_FONT_SIZE));
display.setEditable(false);
display.setFocusable(false);
display.setBackground(Color.white);
mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.add(buttonPanel, BorderLayout.CENTER);
mainPanel.add(display, BorderLayout.PAGE_START);
}
private JButton createButton(String btnLabel) {
JButton button = new JButton(btnLabel);
button.setFont(button.getFont().deriveFont(BTN_FONT_SIZE));
return button;
}
public JComponent getMainComponent() {
return mainPanel;
}
private static void createAndShowGui() {
CalcEg mainPanel = new CalcEg();
JFrame frame = new JFrame("CalcEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
which would result in this GUI:
Again this is a very simplistic example, but the general principles apply to any GUI that holds components such as JButtons, JTextComponents, etc.
With Swing animation, the Timer class is an exceptional option.
//this class is a JPanel that implements ActionListener`
Timer t = new Timer(1000,this);//the first arg is how many times it repeats in milliseconds
//Then in the constructor...
t.start();
//the ActionPerformed function normally increments a variable then calls the repaint method.
rectangle_x++;
repaint(); //calls paintComponent
Another good idea is to cast g to a Graphics2D object- it's a lot safer and more capable.
Another way to use the Timer class:
Timer t = new Timer(510, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
rectangle_x++;
repaint();
}
})
...
t.start();
You still need to override actionPerformed() in your main class though.

JButton and JLabel dissappears when adding custom background

JButton and JLabel disappears when adding custom background. I don't see any problems in my program, but maybe you guys find an solution! I think it's only a little thing I forgot, but I can't figure it out.
Here's the code:
GameWindow.java:
setContentPane(new StartImagePanel(RollrackLogo));
out.println("adding JLWelcome");
JLWelcome.setText("Welcome to Rollrack, " + namewindow.name);
add(JLWelcome);
JLWelcome.setVisible(true);
out.println("JLWelcome added");
out.println("adding JBRandom");
JBRandom.setText("Random");
add(JBRandom);
JBRandom.setVisible(true);
out.println("added JBRandom");
The background appears perfect, but not the JButton and JLabel!
Code to the StartImagePanel.java:
public class StartImagePanel extends JComponent{
private Image image;
public StartImagePanel(Image image) {
this.image = image;
}
#Override
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
}
}
Your button and label are added to your GameWindow frame while they should be added to its contentPane, setContentPane(new StartImagePanel(RollrackLogo)); instead. That's why they are not showing, they are added to the frame.
Make a variable of the StartImagePanel and add the button and label to it and they should show up.
StartImagePanel contentPanel = new StartImagePanel(RollrackLogo);
setContentPane(contentPanel);
...
out.println("adding JLWelcome");
JLWelcome.setText("Welcome to Rollrack, " + namewindow.name);
contentPanel.add(JLWelcome);
JLWelcome.setVisible(true);
out.println("JLWelcome added");
out.println("adding JBRandom");
JBRandom.setText("Random");
contentPanel.add(JBRandom);
JBRandom.setVisible(true);
out.println("added JBRandom");
Answer dispute
The claims in the first paragraph are plain wrong. Here is source that proves it.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class AddToCustomContentPane {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
// the GUI as seen by the user (without frame)
JPanel gui = new JPanel(new FlowLayout());
gui.setBorder(new EmptyBorder(2, 3, 2, 3));
gui.setBackground(Color.RED);
JFrame f = new JFrame("Demo");
f.setContentPane(gui);
// Acid test. Can we add buttons direct to the frame?
f.add(new JButton("Button 1"));
f.add(new JButton("Button 2"));
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See http://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
Edit after the custom panel code was given
Here's a snippet that works to show both button and label on a black image background, I removed that was not needed (listeners).
public static void main(String[] v) {
class StartImagePanel extends JPanel {
private Image image;
public StartImagePanel(Image image) {
this.image = image;
}
#Override
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
}
}
class GameWindow extends JFrame{
public GameWindow() {
BufferedImage RollrackLogo;
RollrackLogo = new BufferedImage(400,200,BufferedImage.TYPE_INT_RGB);
final JButton JBRandom = new JButton();
final JLabel JLWelcome = new JLabel();
setDefaultCloseOperation(EXIT_ON_CLOSE);
StartImagePanel panel = new StartImagePanel(RollrackLogo);
setContentPane(panel);
setExtendedState(MAXIMIZED_BOTH);
setVisible(true);
JLWelcome.setText("Welcome to Rollrack");
panel.add(JLWelcome);
JLWelcome.setVisible(true);
JBRandom.setText("Random");
panel.add(JBRandom);
JBRandom.setVisible(true);
}
}
GameWindow window = new GameWindow();
window.pack();
window.setVisible(true);
}
I rather use an instance of a JFrame, instead of extending it, as #Andrew Thompson suggested in another question.
However, if you're extending it, it might be a good practice to call super() in the constructor.
Additionally, we may need to know what is going on in your StartImagePanel.
It seems, to me, to be the problem.
Ensure both your GameWindow and StartImagePanel extend properly their superclasses (call super();).
Ensure your StartImagePanel has a proper Layout.
Add your components before you set your frame visible. This also means you won't need JLWelcome.setVisible(true);.
Ensure that your code is executed in the EDT (Event-Dispatch Thread).
Example:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class GameWindow extends JFrame{
BufferedImage rollrackLogo;
JButton jbRandom;
JLabel jlWelcome;
public GameWindow() {
super();
jbRandom = new JButton("Random");
jlWelcome = new JLabel("Welcome to Rollrack, " +
namewindow.name);
rollrackLogo = new BufferedImage(400, 200,
BufferedImage.TYPE_INT_RGB);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setContentPane(new StartImagePanel(rollrackLogo));
// Add your components.
add(jlWelcome);
add(jbRandom);
addKeyListener(new KeyListener() {
#SuppressWarnings("static-access")
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == e.VK_ESCAPE){
System.exit(7);
}
}
#Override
public void keyReleased(KeyEvent arg0) {}
#Override
public void keyTyped(KeyEvent arg0) {}
});
// Pack, or otherwise set fullscreen.
pack();
// Now, set frame visible.
setVisible(true);
}
}
Edit: Now that you've posted the code for your StartImagePanel, I see that you're extending JComponent. Follow my previous advice, (call super), set a Layout, and extend JPanel instead.

Why does my JFrame stay empty, if I subclass JPanel and JFrame?

I'm trying to write custom JFrame and JPanel for my Java application. Currently, I just want to have a JPanel with a start button in the very middle of the screen. So, here's the code I have:
package gui;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class SubitizingFrame extends JFrame implements KeyListener {
public SubitizingFrame() {
super("Subitizing");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addKeyListener(this);
add(new LaunchPanel());
pack();
setVisible(true);
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_F5)
System.out.println("F5 pressed");
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
and here is my panel:
package gui;
import instructions.Settings;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class LaunchPanel extends JPanel implements ActionListener {
private JButton startButton;
public LaunchPanel() {
int width = Settings.getScreenSizeX(), height = Settings.getScreenSizeY();
setPreferredSize(new Dimension(width, height));
setLayout(null);
startButton = new JButton("Start");
startButton.setLocation((width/2) - (startButton.getWidth()/2), (height/2) - (startButton.getHeight()/2));
add(startButton);
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
But when the application launches, I don't see anything. Just a big gray screen.
Do not use a null layout. If you simply use the default layout manager of JPanel (i.e. FlowLayout), the JButton with "automagically" be placed in the center. Also, in order to place the JFrame in the middle of the screen, invoke setLocationRelativeTo(null).
Since it's hard to tell what you mean by "screen", this example shows how you center a JButton in a JPanel in a JFrame, that is then centered on the monitor.
public final class CenterComponentsDemo {
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame("Center Components Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ButtonPane());
frame.setSize(new Dimension(300, 100)); // Done for demo
//frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static class ButtonPane extends JPanel{
public ButtonPane(){
super();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBackground(Color.PINK);
final JButton button = new JButton("Start");
button.setAlignmentX(Component.CENTER_ALIGNMENT);
add(Box.createVerticalGlue());
add(button);
add(Box.createVerticalGlue());
}
}
}
Recommendations:
Avoid using null layout as this makes your app difficult to upgrade and maintain and makes it potentially very ugly or even non-usable on boxes with different OS's or screen resolutions.
If you have your JPanel use a GridBagLayout and add a single component to it without using GridBagConstraints, it will be placed in the center of the JPanel.
You almost never have to or should extend JFrame and only infrequently need to extend JPanel. Usually it's better to enhance your GUI classes through composition rather than inheritance.
Avoid having your "view" or gui classes implement your listener interfaces. This is OK for "toy" programs, but as soon as your application gains any appreciable size or complexity, this gets hard to maintain.
If you don't use any LayoutManager (which btw you probably should), then you'll need to set the size of the panel as well (along with its position).
Although we strongly recommend that you use layout managers, you can perform layout without them. By setting a container's layout property to null, you make the container use no layout manager. With this strategy, called absolute positioning, you must specify the size and position of every component within that container. One drawback of absolute positioning is that it does not adjust well when the top-level container is resized. It also does not adjust well to differences between users and systems, such as different font sizes and locales.
From: http://download.oracle.com/javase/tutorial/uiswing/layout/using.html
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class LaunchPanel extends JPanel {
private JButton startButton;
public LaunchPanel() {
int width = 200, height = 100;
setPreferredSize(new Dimension(width, height));
setLayout(new GridBagLayout());
startButton = new JButton("Start");
add(startButton);
setBorder( new LineBorder(Color.RED, 2));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(null, new LaunchPanel());
}
});
}
}
addKeyListener(this);
Don't use KeyListeners. Swing was designed to be used with Key Bindings. Read the section from the Swing tutorial on How to Use Key Bindings for more information.
The tutorial also has a section on Using Layout Manager which you should read. You should not create GUI's with a null layout.

Categories

Resources