How to position a JButton around the screen? - java

I have tried looking at many documentations and tutorials but none of them seem to work together. I am just trying to make a simple "main menu" for a "game" my friend and I are attempting to make. I am able to move the buttons around when there is no background image present, and I am only able to get the background image but I can't move the buttons around.
So my question is how can I position JButtons?
Here is my code & a screenshot:
What my frame looks like
package game;
import java.awt.FlowLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MainMenu extends JFrame{
private JButton singPlay = new JButton("Single Player");
private JButton twoPlay = new JButton("Two Player");
public MainMenu()
{
JFrame frame = new JFrame("TestTEST");
JPanel panel = new JPanel();
frame.setSize(400,500);
frame.setLocation(700,300);
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new JLabel(new ImageIcon("C:\\Users\\Austin\\Pictures\\Landscape.jpg")));
frame.setLayout(new FlowLayout());
frame.add(singPlay);
frame.add(twoPlay);
frame.setSize(399,499);
frame.setSize(400, 500);
}
public static void main(String[] args)
{
new MainMenu();
}
}

So my question is how can I position JButtons?
Use an appropriate layout manager.
You set the layout manager to a FlowLayout so the buttons are displayed at the top of the frame.
Maybe you want to center the buttons on the frame. If so then you can use a GridbagLayout.
Also, don't use setSize(). Use the pack() method. Finally the frame should be made visible AFTER you add the components to the frame.
So your code might look something like:
public MainMenu()
{
JPanel panel = new JPanel();
panel.setOpaque(false);
panel.add(singPlay);
panel.add(twoPlay);
JFrame frame = new JFrame("TestTEST");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setContentPane(new JLabel(new ImageIcon("yourfilename.jpg")));
frame.setLayout(new GridBagLayout());
frame.add(panel, new GridBagConstraints());
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
Or maybe you want the buttons centered vertically. Then you can use a vertical BoxLayout.
Read the Swing tutorial on Layout Managers and decide for yourself what is appropriate.

Right now you're using a JLabel as a content pane, which doesn't make a ton of sense.
Instead of putting your background in a JLabel, you should extend JPanel and override paintComponent() to draw the background yourself. Here is a great tutorial on performing custom painting.
Once you have your background JPanel, put your JButtons inside that and use it as your content pane.
After that, if you're looking to manually position your JButtons, then you might be looking for a null layout.

JFrame.setContentFrame() is not how you set a background image. See the selected answer at How to set background image in Java?
Also, you should be doing all these operations in the Swing event thread.
public static void main(String [] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
new MainMenu();
}
});
}

A simple solution is to use GridBagLayout, the following will centre the buttons, on on top of each, vertically and horizontally in the container
public MainMenu() {
JFrame frame = new JFrame("TestTEST");
JPanel panel = new JPanel();
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new JLabel(new ImageIcon("C:\\Users\\Austin\\Pictures\\Landscape.jpg")));
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(4, 4, 4, 4);
gbc.fill = GridBagConstraints.HORIZONTAL; frame.add(singPlay, gbc);
frame.add(twoPlay, gbc);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
As a side note, while JLabel work in this case, you need to be ware that it will calculate it's preferred size from the icon (and text) properties of the label itself, not it's children, JLabel really wasn't designed to hold other components

Related

Layout Problem in Java - set a 20%opaque Panel on Bottom of JFrame

Well, actually I have a Layout problem in java Swing. I simply want to add a JPanel on the bottom of a Frame - a coding snipplet that might be done with every web based language in about 5 Minutes. Not so in Java. I tried to add a jPanel to a jFrame, that Contains a jContentPane, set the size of the jPanel to what I need and to repaint and revalidate the jFrame, as well as setting the LayOutManager to null.
Java shows me in this case a full-width jPanel, that fills my whole jFrame.
Therefore I tried another approach: I divided my jPanel in a fully transparent jPanel on top and a 20%opaque jPanel on the bottom. Still it didn't work out as expected.
Since then I tried to resize the child jPanels of my new Panel and the Panel as well and tried to repaint and revalidate the jFrame. Without any effect.
Despite of my efforts, java still shows me a full sized 20%opaque jPanel on the whole jFrame, that now contains another 20%opaque jPanel on Top.
I know that this whole problem is caused by the LayoutManager, Java useless per Default. However, it is not an option to set the LayoutManager to null or even change the LayoutManager of our jFrame, because that would lead us to refactor the whole functionality of our tiny app we worked on for several weeks.
public void showUndoPanel() {
System .out.println("Show Undo Panel");
JPanel myPanel = new JPanel(null);
JPanel glassPanel = new JPanel();
JPanel ContentPanel = new JPanel();
JLabel myJLabel = new JLabel("Great Job!");
myPanel.setBackground(new Color(255,122,122,100));
glassPanel.setSize(650, 550);
glassPanel.setBackground(new Color(255,122,122,100));
myPanel.add(glassPanel);
ContentPanel.setSize(650, 30);
ContentPanel.setBackground(new Color(255,122,122,20));
ContentPanel.add(myJLabel);
myPanel.revalidate();
myPanel.repaint();
undoPanel = myPanel;
myJFrame.add(undoPanel);
myJFrame.revalidate();
}
What I expected:
What it actually does:
Well, I solved the problem by using a BoxLayoutManager and a RigidArea. In case if anyone else may encounter that problem again in the future, I decided to provide the code for this simple solution.
public void showUndoPanel() {
System .out.println("Show Undo Panel");
JPanel myPanel = new JPanel(null);
JPanel glassPanel = new JPanel();
JPanel ContentPanel = new JPanel();
JLabel myJLabel = new JLabel("Great Job!");
myPanel.setBackground(new Color(255,255,255,0));
myPanel.setLayout(new BoxLayout(myPanel, BoxLayout.PAGE_AXIS));
glassPanel.setSize(650, 650);
glassPanel.setBounds(0,0,650,550);
glassPanel.setBackground(new Color(255,122,122,0));
myPanel.add(glassPanel);
myPanel.add(Box.createRigidArea(new Dimension(0,450)));
ContentPanel.setSize(650, 30);
ContentPanel.setBounds(0,750,650,30);
ContentPanel.setBackground(new Color(255,122,122,20));
ContentPanel.add(myJLabel);
myPanel.add(ContentPanel);
myPanel.revalidate();
myPanel.repaint();
undoPanel = myPanel;
myJFrame.add(undoPanel);
myJFrame.revalidate();
}
Now it behaves as expected:
BorderLyout would make it easier to implement.
Note the comments in the following mre:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Main {
private static JFrame myJFrame;
public static void main(String[] args) {
myJFrame = new JFrame();
myJFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
myJFrame.setLocationRelativeTo(null);
showUndoPanel();
myJFrame.pack();
myJFrame.setVisible(true);
}
public static void showUndoPanel() {
JPanel myPanel = new JPanel();
myPanel.setBackground(new Color(255,255,255,0));
myPanel.setLayout(new BorderLayout());
JPanel glassPanel = new JPanel(); //uses FlowLayout by default
//glassPanel.setSize(650, 650); //use preferred size
glassPanel.setPreferredSize(new Dimension(650, 650));
//glassPanel.setBounds(0,0,650,550); //no need to set bounds. bounds are set by the layout manager
glassPanel.setBackground(new Color(255,122,122,0));
myPanel.add(glassPanel, BorderLayout.CENTER);
JPanel contentPanel = new JPanel(); //uses FlowLayout by default
//contentPanel.setSize(650, 30);//use preferred size
contentPanel.setPreferredSize(new Dimension(650, 30));
//contentPanel.setBounds(0,750,650,30); //no need to set bounds. bounds are set by the layout manager
contentPanel.setBackground(new Color(255,122,122,20));
JLabel myJLabel = new JLabel("Great Job!");
contentPanel.add(myJLabel);
myPanel.add(contentPanel, BorderLayout.SOUTH);
myJFrame.add(myPanel);
}
}

Overlaying a JButton onto a JFrame with a JTabbedPan in its content pane?

The below code creates a frame with a JTabbedPane in the frame's contentPane and a "Help" button added directly to the frame's layered pane - at a higher index than the default, so it should always be painted in front of the tabbed pane. But as you can see if you run it, as soon as you click on one of the tabs (or, on Mac, as soon as you hover over a tab), the "Help" button gets painted over - i.e. disappears. You have to resize the frame to make the "Help" button re-appear.
Is this a Java bug or am I doing something wrong? If the latter, what needs to be done to fix the problem? I've consulted both https://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html and https://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html .
Please pardon the poor positioning of the "Help" button. I was just trying to write a quick test to include here. The idea is the to overlay a "Help" icon button in the unused space of the JTabbedPane.
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class TabTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
JTabbedPane tp = new JTabbedPane();
tp.addTab("hello", new JPanel());
tp.addTab("there", new JPanel());
frame.getContentPane().add(tp, BorderLayout.CENTER);
JLayeredPane layeredPane = frame.getRootPane().getLayeredPane();
JButton helpButton = new JButton("Help");
helpButton.setBounds(800, 5, 50, 20);
layeredPane.add(helpButton, 400);
frame.setSize(new Dimension(900, 800));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
This is a common misunderstanding with JLayeredPane.
If you have a look at the JavaDocs for Container, you will note that there are (at least) two add methods
Container#add(Component, int)
Container#add(Component, Object)
The question you need to answer is, which one are you actually calling?
Both the JavaDocs for JLayeredPane and How to use JLayeredPane demonstrate that you should be calling the second one.
While the first can affect the z-ordering of the components, it's no guarantee that the component positions won't be changed.
Instead of:
layeredPane.add(helpButton, 400);
you should be using:
layeredPane.add(helpButton, new Integer(400));
which will pass the value as a constraint to the container, instead of the desired position within the container hierarchy - yeah, suitable I know
An alternative solution might be to use the glassPane instead, for example...
JFrame frame = new JFrame();
JTabbedPane tp = new JTabbedPane();
tp.addTab("hello", new JPanel());
tp.addTab("there", new JPanel());
frame.getContentPane().add(tp, BorderLayout.CENTER);
// Null layout used here for demonstration purposes only
JPanel glassPane = new JPanel(null);
glassPane.setOpaque(false);
frame.getRootPane().setGlassPane(glassPane);
// This is important, as setGlassPane makes it invisible
glassPane.setVisible(true);
JButton helpButton = new JButton("Help");
helpButton.setBounds(800, 5, 50, 20);
glassPane.add(helpButton);
frame.setSize(new Dimension(900, 800));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);

Two JPanels in one JFrame

I want to use two JPanels in one JFrame, with an invisible horizontal line between them. I played a little bit and got this:
public class Application {
public static void main(String[] args)
{
JFrame jframe = new JFrame();
jframe.setSize(500,700);
jframe.setVisible(true);
jframe.setTitle("Title");
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setResizable(false);
JSplitPane splitPane = new JSplitPane();
JPanel leftPanel = new JPanel();
JPanel rightPanel = new JPanel();
splitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setDividerLocation(250);
splitPane.setLeftComponent(leftPanel);
splitPane.setRightComponent(rightPanel);
jframe.add(splitPane);
}
}
Now, the first problem is how can I turn off the "resizability" of the Line between the panels? And how do I make it "invisible"? Maybe use something else than split pane?
Second of all, how do can I work with only one side of the JPanel?
(I am working on an application that lets you draw a circle on the left hand side).
This seems like an easy question but I am relatively new to Java.
As said before in a comment by #MadProgrammer you can use BorderLayout or GridBagLayout but as you're placing the "split" line right in the middle of both panels you could use GridLayout which will make both panels be of the same size no matter if the window is resized.
I didn't tried with GridBagLayout but I did an example on how you could achieve this pane separation without using a JSplitPane.
With GridLayout all you need to do is add the elements to the left pane (in my example I used a JLabel to differentiate them) while in BorderLayout you need to specify that the panel where you'll be painting the circle to be aligned to the left (WEST constant) as I did.
However if you use BorderLayout approach and add text or elements to the right pane, they will be aligned to the right, you can fix it by "boxing" the elements in another pane with a different Layout.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Application {
private JFrame frame;
private JPanel containerPane;
private JPanel topPane;
private JPanel bottomPane;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Application().createAndShowGui();
}
});
}
public void createAndShowGui() {
frame = new JFrame("Example of 2 panels");
containerPane = new JPanel();
topPane = new JPanel();
bottomPane = new JPanel();
containerPane.setLayout(new GridLayout(2, 1));
topPane.setLayout(new GridLayout(1, 2));
bottomPane.setLayout(new BorderLayout());
topPane.add(new JLabel("Left side"));
topPane.add(new JLabel("Right side"));
bottomPane.add(new JLabel("Left side"), BorderLayout.WEST);
bottomPane.add(new JLabel("Right side"), BorderLayout.EAST);
topPane.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLUE), "Using GridLayout"));
bottomPane.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLUE), "Using BorderLayout"));
containerPane.add(topPane);
containerPane.add(bottomPane);
frame.add(containerPane);
// frame.pack();
frame.setSize(500, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I didn't call pack() in this example because the size of both panels (or JLabels in this case was not tall enough to show the difference:
Using pack():
Calling setSize():
Additional tips
Don't forget to place your program on the Event Dispatch Thread (EDT), I did it by writing these lines on the main method:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Application().createAndShowGui();
}
});
Don't place all your code on the constructor, otherwise it will be hard to maintain
It looks like you can use GridLayout to do this. Here is what i think,
public class Application {
public static void main(String[] args) {
JFrame jframe = new JFrame();
jframe.setTitle("Title");
jframe.setResizable(false);
//This creates one row and two equally divided columns
GridLayout gridLayout = new GridLayout(0, 2);
jframe.setLayout(gridLayout);
gridLayout.layoutContainer(jframe);
JPanel leftPanel = new JPanel();
leftPanel.add(new Label("Left side"));
jframe.add(leftPanel);
JPanel rightPanel = new JPanel();
rightPanel.add(new Label("Right side"));
jframe.add(rightPanel);
jframe.setSize(800, 500);
jframe.setVisible(true);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Here is how it looks:
The panels will not resize as well as there is no line visible that seprates them.

JLabel not showing up on JPanel

For some reason even though I have implemented the validate method in my code, and added my JPanel to my JFrame. I am also trying to get my JLabel to have Comic Sans font and for it to be centered in the screen. Why is my JLabel not showing up on my JPane
package math_program;
import java.awt.Color;
import java.awt.Font;
import java.util.Random;
import javax.swing.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class Canvas
{
Images obj = new Images();
public void paintFrame()
{
//Instantiation of objects
Random gen = new Random();
JFrame frame = new JFrame();
JPanel panel = new JPanel();
//Text
JLabel problem = new JLabel();
frame.setVisible(true);
frame.setAlwaysOnTop(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE); //Make frame actually closeable
frame.setTitle("Math Owl: Alpha V:0.1 (coded by John)");
frame.setSize(800, 500);
frame.setLocationRelativeTo(null);
frame.add(panel); //Add JPanel to JFrame
panel.setVisible(true);
panel.setLayout(null);
panel.setBackground(Color.WHITE); //To see if text is even appearing
//Adding Components
problem.setFont(new Font("Comic Sans MS", Font.PLAIN, 20));
problem.setLocation(400,250);
problem.setText(gen.nextInt(11) + " + " + gen.nextInt(11));
panel.add(problem);
panel.validate();
}
}
Use a proper Layout Manager just as copeg said. Even the default Layout Manager (Flow Layout) will work. Just remove panel.setLayout(null); and your JLabel will show up.
Also when I try your snippet code there also problem on Images obj = new Images(); I assume you already have the Images class defined in your package somehow.
`
//frame.add(panel);
frame.setContentPane(panel);
//panel.setVisible(true); // not necessary code
//panel.setLayout(null); // not necessary code
panel.setBackground(Color.WHITE); //To see if text is even appearing
frame.getContentPane().add(problem);
The problem is that with null layout, you have to set both the location and the size of each component. You set only the location of the label, so you can fix it with problem.setSize(x, y);. With that being said, it's just wrong to take this approach. See what your whole GUI design for this frame looks like and choose an appropriate LayoutManager.
Try this:
public class MyCanvas {
public MyCanvas() {
Random gen = new Random();
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JLabel problem = new JLabel();
panel.setBackground(Color.WHITE);
problem.setFont(new Font("Comic Sans MS", Font.PLAIN, 20));
problem.setText(gen.nextInt(11) + " + " + gen.nextInt(11));
panel.add(problem); // Default FlowLayout
frame.add(panel); // Default BorderLayout at position CENTER
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Math Owl: Alpha V:0.1 (coded by John)");
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
new MyCanvas();
}
}
Notes:
frame.setVisible(true) should be the last call you make.
frame.setAlwaysOnTop(true) and frame.setResizable(false) are things you usually want to avoid from the user's perspective.
frame.setSize(...) should be replace with frame.pack().
frame.setLocationRelativeTo(null) should be called between pack() and setVisible(true).
No need for panel.setVisible(true), it does nothing for you.
No need for panel.validate() (should be revalidate()), it is only needed when changing the layout during runtime.
The class Canvas already exists, use a different name.
There two way
is you are using any layout manager then you can simply show the component by using setSize(new Dimension(width, height)
if you have set the layout manager to null then you must use
setBounds(x,y, width, height) (if you do this obviously no need to set size) to show the component
otherwise just do
setSize(new Dimension(width,height)
then setLocation(x,y)
if you want to dynamically calculate the position then you can set the initial locaion as setLocation(0,0)
afterwards when all the components are showing you can use your algorithm to recalculate x and y position and again setLocation(x,y)
for e.g.
label1=setSize(new Dimension(100,20)
label1.setLocation(0,0)
Label2=setSize(new Dimension(100,20)
Label2.setLocation((int)label1.getSize().getWidth()+10,20) and so on

Best layout for frame

What kind of layout should I use to create a page Like this:
It should be resizable
It has two main panels Right and Left?
Extra space will be given to the 'Main Text' text area, and extra height will be given to the button panel while centering them.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class EndOfLineButtonLayout {
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 BorderLayout());
gui.setBorder(new EmptyBorder(2, 3, 2, 3));
JPanel textPanel = new JPanel(new BorderLayout(5,5));
textPanel.add(new JScrollPane(new JTextArea("Top Text",3,20)),
BorderLayout.PAGE_START);
textPanel.add(new JScrollPane(new JTextArea("Main Text",10,10)));
gui.add(textPanel, BorderLayout.CENTER);
JPanel buttonCenter = new JPanel(new GridBagLayout());
buttonCenter.setBorder(new EmptyBorder(5,5,5,5));
JPanel buttonPanel = new JPanel(new GridLayout(0,1,5,5));
for (int ii=1; ii<6; ii++) {
buttonPanel.add(new JButton("Button " + ii));
}
// a component added to a GBL with no constraint will be centered
buttonCenter.add(buttonPanel);
gui.add(buttonCenter, BorderLayout.LINE_END);
JFrame f = new JFrame("Demo");
f.add(gui);
// 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);
}
}
You can use gridbag layout, try using netbeans, I have tried it and found really usefull.
Once you create it with netbeans you can use the same and build infact any kind of layout.
best of luck with other solutions.
p.s. border layout is perfect for your requirement, but I mentioned this just in case you would like to do lot more .
I would use BorderLayout.
Create Three JPanels and add them to a JFrame as follows:
public class YourClass extends JFrame{
//code here
this.setLayout(new BorderLayout());
this.add(TopPanel, BorderLayout.NORTH);
this.add(RightPanel, BorderLayout.EAST);
this.add(MainPanel, BorderLayout.CENTER);
this.pack();
this.setVisible(true);
The two main panels would be placed inside a main JPanel using a BorderLayout. The left panel would be placed using BorderLayout.CENTER, and the right panel would be placed using BorderLayout.LINE_END.
The left panel would use a BoxLayout, Y axis to separate the two JPanels within the left panel.
The right buttons panel would use a GridBagLayout. This sizes the buttons the same and allows you to use Insets to add some spacing to the buttons.
The buttons would be spaced from the top to the bottom of the right buttons panel. If you want all the buttons towards the top of the right buttons panel, you would put the right buttons panel inside of another JPanel using a FlowLayout.

Categories