Align JLabel using SwingConstants - java

Why is my JLabel text not aligned to the left or center? Is the FlowLayout the issue?
Both of the texts are just appearing at the top, next to each other and I can't fix this.
Main Class
public static void main(String[] args) {
simpleGui SG = new simpleGui();
SG.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SG.setSize(1000,1000);
SG.setVisible(true);
}
Constructor class
public class simpleGui extends JFrame{
private JLabel label1;
private JLabel label2;
simpleGui(){
//title bar
super("Simple GUI");
setLayout(new FlowLayout());
label1 = new JLabel("I'm a label in the window");
label1.setVerticalTextPosition(SwingConstants.BOTTOM);
add(label1);
label2 = new JLabel("Label2");
label2.setHorizontalTextPosition(SwingConstants.LEFT);
add(label2);
}
}

Refer to Laying Out Components Within a Container which is a lesson in the Creating a GUI With Swing trail of Oracle's Java tutorials.
The javadoc for method setVerticalTextPosition, of class JLabel states the following:
Sets the vertical position of the label's text, relative to its image.
A JLabel can contain both text and an image. The image is also referred to as an icon. Since the JLabel in your code contains only text, calling this method does nothing. The same applies for method setHorizontalTextPosition.
A layout manager is responsible for placing a JComponent within its parent container. FlowLayout, by default, places the components in the top, center part of the container (which is JPanel in your code) and gives them their preferred size. (Refer to method getPreferredSize in class javax.swing.JComponent)
I suggest that you put a border around each component so that you can see how much space each one actually takes up. In the below screen capture, I added a red border to the [content pane of the] JFrame, a cyan border around label1 and a blue border around label2.
I assume that you want label1 to appear at the bottom of the content pane and label2 to appear at the left side. Therefore I think that the default layout manager is appropriate. The default layout manager is BorderLayout.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
public class SimpleGui extends JFrame {
private JLabel label1;
private JLabel label2;
public SimpleGui() {
super("Simple GUI");
((JPanel) getContentPane()).setBorder(BorderFactory.createLineBorder(Color.red));
label1 = new JLabel("I'm a label in the window", SwingConstants.CENTER);
label1.setBorder(BorderFactory.createLineBorder(Color.cyan));
add(label1, BorderLayout.PAGE_END); // changed
label2 = new JLabel("Label2");
label2.setBorder(BorderFactory.createLineBorder(Color.blue));
add(label2, BorderLayout.LINE_START); // changed
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
SimpleGui sg = new SimpleGui();
sg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
sg.setSize(1000,1000);
sg.setVisible(true);
});
}
}
This is how it looks when I run the above code.
Note that BorderLayout gives the bottom component the same width as the content pane as well as its preferred height while the left component is given its preferred width but its height matches the height of the content pane. Refer to the tutorial I linked to at the start of this answer.
Since the width of label1 is greater than the width of the text it contains, I explicitly set the horizontal alignment of label1 to CENTER. By default it is LEFT. The alignment only affects the display when the width of the JLabel is larger than the width of its text.
I recommend that you go through the entire Creating a GUI With Swing trail in Oracle's Java tutorials.

I've updated your code a bit, I changed the classname to include java naming conventions and I've stuck with the default BorderLayout. I think this is what you're after judging by the names of your constants.
import javax.swing.*;
import java.awt.*;
public class SimpleGui extends JFrame{
private JLabel label1;
private JLabel label2;
SimpleGui(){
//title bar
super("Simple GUI");
//setLayout(new FlowLayout());
label1 = new JLabel("I'm a label in the window");
label1.setVerticalTextPosition(SwingConstants.BOTTOM);
add(label1, BorderLayout.SOUTH);
label2 = new JLabel("Label2");
label2.setHorizontalTextPosition(SwingConstants.LEFT);
add(label2, BorderLayout.WEST);
}
public static void main(String[] args) {
SimpleGui SG = new SimpleGui();
SG.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SG.setSize(1000,1000);
SG.setVisible(true);
}
}
The FlowLayout is going to position things differently and you cannot just say, "you're at the top" and "you're at the left".

What you perceive: Regardless of the calls setVerticalTextPosition() or setHorizontalTextPosition() the text is always at the same location. But your calls actually had some effect that you just did not see.
The reason is that there are two positioning algorithms at the same time:
Positioning the label within the frame
You asked FlowLayout to take care. FlowLayout will simply ask the components about their preferred size, then place one besides the other just as you would expect characters or words to show up besides each other in an editor. Not that at this time each label was sized to it's preferred size.
Positioning the text within the label
When the label is asked about it's preferred size, it will give the minimum size necessary to render the configured text with the configured font. This way the information can be rendered comfortably without clipping off any information.
Later, when the label is asked to render the text, it will try follow your settings setVerticalTextPosition() or setHorizontalTextPosition(). But since there is no excess space within the label, doing it left-aligned or right-aligned or top-aligned or bottom aligned all ends up at the same coordinate.
Getting out of this
Either accept that the labels use their minimum size and you take care that the labels themselves are positioned correctly (GridBagLayout is very powerful in this respect), or resize the labels by setting a minimum and/or preferred size on them, then watch how your text positioning works.
The text positioning can be seen very nicely if you use BorderLayout and add the label in the center position. But this typically will work for one component only.

Related

Java cannot display the JPanel, but can display JLabel?

The question is that I cannot add JPanel and JLabel in the Frame at the same time.
When i using following code, only MyPanel will be visible. myFrame.add(myLabel);myFrame.add(myPanel);myFrame.setVisible(true);
when I execute: myFrame.add(myLabel);myFrame.setVisible(true);myFrame.add(myPanel);
only myLabel will be visible.
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String[] args) {
MyFrame myFrame = new MyFrame();
MyLabel myLabel = new MyLabel();
MyPanel myPanel = new MyPanel();
myFrame.add(myLabel);
myFrame.add(myPanel);
myFrame.setVisible(true);
}
}
public class MyFrame extends JFrame {
MyFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //exit out of application.
//https://docs.oracle.com/javase/7/docs/api/javax/swing/JFrame.html#setDefaultCloseOperation%28int%29
this.setSize(750, 750); //set the size.
this.setResizable(true);//resize the frame.
this.setTitle("Welocme to new world."); //set the Title.
ImageIcon imageIcon = new ImageIcon("logo.png");
this.setIconImage(imageIcon.getImage());
this.getContentPane().setBackground(new Color(217, 217, 217));
this.setBackground(Color.YELLOW);//this.setVisible(true);//Make the frame visible.
}}
import javax.swing.*;
import java.awt.*;
public class MyPanel extends JPanel {
MyPanel() {
this.setBackground(Color.white);
this.setBounds(2,2,25,25);
}}
import javax.swing.*;
import java.awt.*;
public class MyLabel extends JLabel {
MyLabel(){
this.setText("<html>Heaven <br/>Heaven's body\"<br/> Whirl around me <br/>Make me wonder</html>");
//,SwingConstants.CENTER);
//https://stackoverflow.com/questions/1090098/newline-in-jlabel
//How to print multi line in java
ImageIcon image = new ImageIcon("Cosmogony_Björk_Cover.jpg");
this.setIcon(image);
//jLabel.setForeground(new Color(217,217,217));
this.setForeground(Color.BLACK);
this.setFont(new Font("helvetica",Font.PLAIN,18));
this.setBackground(Color.gray);
this.setOpaque(true);
//jLabel.setVerticalTextPosition(JLabel.TOP); Set the relative text position of the label.
//jLabel.setBorder();
this.setVerticalAlignment(JLabel.CENTER);
this.setHorizontalAlignment(JLabel.CENTER);
}}
The default Layout Manager of JFrame is the BorderLayout.
Since you did not change the layout manager of your JFrame this is also the current layout manager used in your snippet.
Usually, when using the BorderLayout, you specify which area of the BorderLayout should be populated when adding a component. This is usually done via
frame.add(component, BorderLayout.CENTER);
Notice the area specification in the add() method, which tells the BorderLayout where to place the component.
Here is the issue however. If you use the add() method in combination with the BorderLayout without specifying the placement of the component, it will always place the component in BorderLayout.CENTER. (Causing the component which is currently there to be replaced)
To work around this, do one of the following things:
Specify the placement explicitly, so both components will show up:
frame.add(component1, BorderLayout.PAGE_START);
frame.add(component2, BorderLayout.CENTER);
Or use a different Layout Manager, which will take care of the placement for you. E.g. FlowLayout
JPanel contentPanel = new JPanel(); // JPanel uses flowlayout by default!
contentPanel.add(component1);
contentPanel.add(component2);
myFrame.setContentPane(contentPanel);
You could also explicitly set the layout:
Container contentPane = myFrame.getContentPane();
// creates new FlowLayout and sets on content pane
contentPane.setLayout(new FlowLayout());
contentPane.add(component1);
contentPane.add(component2);
Sidenotes:
Look through the Laying out components within a container Oracle tutorial, which will give you more information on which layout managers there are and how to work with them.
When building your GUI, setVisible() on the JFrame should be the last thing you are doing after adding all components. Because if you add components after setting the frame visible, the changes will not immediately take effect without you telling swing that something changed.
When correctly working with the Swing Layout Managers, there should be no need to use things like setBounds(...) or setSize(). After adding all components, calling pack() on the JFrame is the preferred way to go. This will size the JFrame according to the preferred size of the components inside.

ImageIcon extends beyond window, resizing window expands all of components instead of showing more

private void setupGUI(){
// Setup Frame
f = new JFrame("Shape Image Generator");
f.setBounds(500, 150, 450, 350);
f.setLayout(new GridLayout(8,1));
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEvent){
System.exit(0);
}
});
}
I create the frame above, then 8 panels. I create various components and add them to the panels and everything works fine. Until I created an ImageIcon and added it to a label and added that label to the 8th panel. The image used is 140x129 pixels. The problem is, only the top.... maybe 1/4 of the image is showing. If I change the frames dimensions in the code, more empty space is created between each panel, but only a slight bit more of the image is shown, so the image is still off of the screen. I'd say the window is easily adding 10 pixels of spacing for every 1 more pixel of the image it shows. If I drag the corners of the window to expand it, the same thing happens. If the window is maximized I still can only see a little over half of my now very stretched image.
Things I tried:
None of my components have preferred dimensions set, but I tried setting a preferred dimension for the label then panel that contains the ImageIcon and it only added the difference between the image and preferred size in gray space above the image, pushing it further offscreen. So, I undid that.
Adding the label containing the ImageIcon to a different panel which was not the 8th and last panel, in this case, the image is still cut off, but at the point that it gets cut off, the components on the panel underneath it appear (over top of the background coloring which cuts off the image).
Exhaustively Googling this situation with about 30 different ways of phrasing it and not finding a solution.
(row1 - row8 are JPanels, I didn't include the coding for them)
ImageIcon iconStart = createImageIcon("/images/ShapeClipart.png", "Shapes");
JLabel imgLabel = new JLabel();
row8.add(imgLabel);
// Add image to image label
imgLabel.setIcon(iconStart);
// Add panels to frame
f.add(row1);
f.add(row2);
f.add(row3);
f.add(row4);
f.add(row5);
f.add(row6);
f.add(row7);
f.add(row8);
f.setVisible(true);
Window at execution
Window when stretched
edit:
adding f.pack() makes a very tall skinny window (the windows height taller than my screen) but it still looks like when I manually expand the window (empty space between panels, image partially offscreen), even if I take out f.setBounds and only use f.setLocation.
You are using a GridLayout. This gives all of the enclosed panels the same amount of space. In this case it is a vertical grid.
You should probably use something a bit different. I might try a BorderLayout in the JFrame and put the a panel containing the top seven panels (in a GridLayout) into the CENTER, and then put the JLabel into the SOUTH portion of the JFrame.
There are other ways to lay it out, but this is the first I could think of.
GridLayout makes each cell in the grid the same size and the size of each cell is determined by the largest Component contained in the grid.
In your code, the icon is the largest component and you also have only one column in your grid so every row has the same height as your icon.
Since you also limit the size of your JFrame by calling method setBounds(), the Swing infrastructure cuts off the icon so that all the components fit into the bounds you specified.
One alternative, but not the only one, is to use BoxLayout since it uses the preferred size of each of its contained components.
Here is a sample GUI that matches the screen capture that you posted and uses BoxLayout.
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.net.URL;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
public class Shapes23 implements Runnable {
private JFrame frame;
#Override // java.lang.Runnable
public void run() {
showGui();
}
private JPanel createEighthRow() {
JPanel eighthRow = new JPanel();
URL url = getClass().getResource("paint-bursht.jpg");
Icon ico = new ImageIcon(url);
JLabel label = new JLabel(ico);
eighthRow.add(label);
return eighthRow;
}
private JPanel createFifthRow() {
JPanel fifthRow = new JPanel();
JTextField textField = new JTextField(20);
fifthRow.add(textField);
return fifthRow;
}
private JPanel createFirstRow() {
JPanel firstRow = new JPanel();
JLabel label = new JLabel("2D Shapes");
firstRow.add(label);
return firstRow;
}
private JPanel createFourthRow() {
JPanel fourthRow = new JPanel();
fourthRow.add(createRadioButton("Sphere"));
fourthRow.add(createRadioButton("Cube"));
fourthRow.add(createRadioButton("Cone"));
fourthRow.add(createRadioButton("Cylinder"));
fourthRow.add(createRadioButton("Torus"));
return fourthRow;
}
private JPanel createMainPanel() {
JPanel mainPanel = new JPanel();
BoxLayout layout = new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS);
mainPanel.setLayout(layout);
mainPanel.add(createFirstRow());
mainPanel.add(createSecondRow());
mainPanel.add(createThirdRow());
mainPanel.add(createFourthRow());
mainPanel.add(createFifthRow());
mainPanel.add(createSixthRow());
mainPanel.add(createSeventhRow());
mainPanel.add(createEighthRow());
return mainPanel;
}
private JRadioButton createRadioButton(String text) {
JRadioButton radioButton = new JRadioButton(text);
return radioButton;
}
private JPanel createSecondRow() {
JPanel secondRow = new JPanel();
secondRow.add(createRadioButton("Circle"));
secondRow.add(createRadioButton("Rectangle"));
secondRow.add(createRadioButton("Square"));
secondRow.add(createRadioButton("Triangle"));
return secondRow;
}
private JPanel createSeventhRow() {
JPanel seventhRow = new JPanel();
JButton button = new JButton("Enter");
seventhRow.add(button);
return seventhRow;
}
private JPanel createSixthRow() {
JPanel sixthRow = new JPanel();
JTextField textField = new JTextField(20);
sixthRow.add(textField);
return sixthRow;
}
private JPanel createThirdRow() {
JPanel thirdRow = new JPanel();
JLabel label = new JLabel("3D Shapes");
thirdRow.add(label);
return thirdRow;
}
private void showGui() {
frame = new JFrame("Shape Image Generator");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Shapes23());
}
}
Here is a screen capture of how it looks. Note that I couldn't find the same icon as in your screen capture so I just used a different one.

How to make a Jframe containing a Jpanel scrollable?

So I have this JFrame that contains a JPanel and in there I add JLabels with information I want but since I'll be adding labels all the time at some point the text is too long to appear so I want to add a scrollbar. Basically I want to make my JFrame with a JPanel in it scrollable. I have this code but my problem is that even though the scrollbar appears but it doesnt move and doesn't really work when the text is a lot, meaning the text still gets cut out and the scrollbar is there not moving. Does anyone know how to fix this?
import javax.swing.*;
import java.awt.*;
import java.util.*;
public class Bar {
JFrame info = new JFrame("Information");
JLabel ballinf = new JLabel();
JPanel contentPane = new JPanel();
JScrollPane scrolling = new JScrollPane();
public Bar(){
contentPane.setOpaque(true);
contentPane.setBackground(Color.WHITE);
contentPane.setLayout(null);
scrolling = new JScrollPane(contentPane,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
info.add(scrolling);
info.setSize(750, 600);
info.setLocationByPlatform(true);
info.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
info.setVisible(true);
}
public void adding(int pos){
ballinf = new JLabel("Something ",JLabel.CENTER);//assume the text will be bigger here and have more info
ballinf.setSize(700, 30);
ballinf.setForeground(Color.green);
ballinf.setLocation(5, 5+pos);
contentPane.add(ballinf);
info.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
info.setVisible(true);
}
public static void main(String[] args){
Bar stats = new Bar();
stats.adding(0);
stats.adding(20);//this will be done in a for loop for more than 2 times so the text ends up to be a lot
}
}
contentPane.setLayout(null);
Don't use a null layout!!!
You need to use an appropriate layout manager. Read the section from the Swing tutorial on Layout Managers for more information and working examples. The layout manager will then determine the preferred size of the panel as you add components to the panel.
The scrollpane will then display the scrollbars when necessary.
If you dynamically add components to the panel (after the GUI is visible) then the code should be something like:
panel.add(...);
panel.revalidate();
panel.repaint();

JLabel too wide

I wrote the following code which is a simple window with a JLabel header at the top. The code is as follows:
import java.awt.Color;
import java.awt.Font;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
init();
}
public Main() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int WIDTH = 500;
int HEIGHT = 500;
setBackground(Color.LIGHT_GRAY);
setSize(WIDTH, HEIGHT);
setTitle("New Window");
setLocationRelativeTo(null);
JPanel pane = new JPanel();
pane.setOpaque(false);
setContentPane(pane);
pane.setLayout(null);
JLabel title = new JLabel("New Window", SwingConstants.CENTER);
title.setFont(new Font("Neuropol", Font.PLAIN, 22));
title.setSize(WIDTH, 20);
title.setBorder(BorderFactory.createLineBorder(Color.black));
pane.add(title);
}
static void init() {
Main frame = new Main();
frame.setVisible(true);
}
}
I'm having a weird issue with it though. As you can see I set the width of the JLabel to the same width as the JFrame (and therefore the same width as the JPanel), but for some reason, the label goes past the right edge of the Frame. The width of the frame is 500, but I have to set the JLabel to a width of 483 in order for the border to be within the JFrame. I've been over and over this, but can't see anything wrong. Does anybody see my mistake here?
I set the width of the JLabel to the same width as the JFrame
But the JFrame width includes the "decorations" of the frame, like the Border.
I have to set the JLabel to a width of 483 in order for the border to be within the JFrame.
No, 483 will make the label too big for the frame. The Borders of the frame are not 17 pixels. I think the Borders are 4 or 5 pixels each depending on the LAF.
So this is another reason why you should NOT be using a null layout as hardcoding a value for one LAF may not work on another LAF.
Also, what happens if the user resizes the frame wider? Now the label will not go to the end. Use a Layout Manager do design dynamic GUI's that adapt to changes as the user resizes the frame.
The easiest solution is to just use the default BorderLayout of content pane of the frame. Then you add your label to the PAGE_START.
add(title, BorderLayout.PAGE_START);
There is also no reason to create a content pane. The frame already has a JPanel which uses a BorderLayout.
Read the Swing tutorial on Layout Managers for more information and working examples. Download the demos code and use them as a starting point for your code. The demo code will show you how to better structure your code to follow Swing conventions.

Strange setBackground() error - Java Swing

import java.awt.*;
import javax.swing.*;
public class JFrameGUI extends JFrame
{
JLabel item1;
public JFrameGUI(int l, int b , String Title)
{
setTitle(Title);
setLayout(new FlowLayout());
setSize(l, b);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
item1 = new JLabel("This is a Sentence.");
item1.setToolTipText("This is gonna show up on hover.");
add(item1);
}
public static void main(String[] args)
{
JFrameGUI g = new JFrameGUI(1280,720,"The End Of The Line");
JPanel p = new JPanel();
p.setBackground(Color.BLUE);
g.add(p);
}
}
When I execute this , all i get is a tiny Blue square nest to the "This is a sentence" string . I've tried everything !
You need to set the layout of the frame to a layout that doesn't respect the preferred sizes of its children. FlowLayout does, and your JPanel has no preferred size without any components added to it, or specifying a preferred size.
A simple fix, set the layout of the frame to BorderLayout, or not set a layout at all, since JFrame already has a default BorderLayout. Note though that you probably want to add the JLabel to the JPanel and not the JFrame. Unless you do want to add it the JFrame and not the background JPanel, you need to specify a BorderLayout position for the one you don't want in the center.
You can see this answer to see which layout managers respect preferred sizes and which don't
See more at Layout out Components Withing a Container
Also, setVisible(true) shoul be the last thing you do after adding all components.

Categories

Resources