Does anyone know whether its possible to change the colour of the empty space provided by the BorderFactory.createEmptyBorder() method. By default it always sets it to white - i want to set it to whatever colour the background of my JFrame is (i.e. grey).
You want an empty border that has a color? The point of an empty border is that it takes up no space. Something with no space can't have a color. Did you want a line border instead?
see BorderFactory#createLineBorder(Color color)
Apply the empty border & color (or 'not' for plain) to a panel added to the content pane.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class PlainColoredEmptyBorder {
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(20, 30, 20, 30));
JTree tree = new JTree();
tree.setVisibleRowCount(4);
gui.add(new JScrollPane(tree));
gui.setBackground(Color.BLUE);
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);
}
}
Why does the cursor show up when i have an empty border but not when i use any of the other border types?
Because an empty border is the only Border that does not paint. Any other type of Border will paint over the text pane cursor.
Putting your text pane inside of a JPanel with a background color doesn't work either because your text pane will be painted after the parent JPanel.
Setting the background colour of the text field and setting it to opaque fixes this
Related
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.
I'm trying to make overlapping panels, but widgets below the 'topmost' panel somehow react to mouse events and repaint events and render above the top layer. For example, here I have bottom layer with lots some buttons and labels, and a top layer with up/down buttons, which is fully opaque for this test (gray background):
This uses the OverlayLayout manager to place panels on different Z levels. The buttons pop into view when hovering over them, and the labels on the right, which auto-update, also pop into view.
SSCCE:
public class Temp extends JPanel {
public static void main(String... args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setContentPane(new Temp());
f.setSize(500, 250);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
public Temp() {
super(null);
setLayout(new OverlayLayout(this));
JButton bottom = new JButton("Bottom");
JButton top = new JButton("Top");
bottom.setBounds(100, 50, 200, 100);
top.setBounds(200, 100, 200, 100);
// OverlayLayout adds components from top to bottom.
add(new JPanel(null) {{
setBackground(new Color(175, 150, 125));
add(top);
}});
add(new JPanel(null) {{
add(bottom);
}});
}
}
This is basically how I do it now. It looks like this after a bit of mouse hovering, but with hovering I can bring any of the two buttons above the other:
My question is, how can I fix this in the easiest way, or what is the standard way to handle this? Perhaps a Non-modal frame-less JDialog? One argument I have against the dialog is I rather not that the user uses keyboard shortcuts to move it around the screen.
and a top layer with up/down buttons, which is fully opaque for this test (gray background):
It doesn't make sense to have an "opaque" top panel. You will not see the buttons on the bottom panel. The top panel should be transparent and the bottom panel should be your background color.
My question is, how can I fix this
If you are referring to the fact that the buttons ZOrder keeps changing as you hover over each button then you need to override the following method on your Temp panel (ie. the panel using the OverlayLayout):
#Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
This basically says that the Temp panel may contain overlapping components so all panels need to be repainted in the proper ZOrder each time.
Below is an example of adding two panels to a frame. Only one panel (the 2nd, red panel) appears.
Why does the first panel disappear?
import java.awt.Color;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class DisappearingPanelInFrame {
DisappearingPanelInFrame() {
JFrame f = new JFrame(this.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(getColoredPanel(Color.GREEN));
f.add(getColoredPanel(Color.RED));
f.pack();
f.setVisible(true);
}
private JPanel getColoredPanel(Color color) {
JPanel p = new JPanel();
p.setBackground(color);
p.setBorder(new EmptyBorder(20, 150, 20, 150));
return p;
}
public static void main(String[] args) {
Runnable r = DisappearingPanelInFrame::new;
SwingUtilities.invokeLater(r);
}
}
The default layout of a JFrame (or more specifically in this case, the content pane of the frame) is a BorderLayout.
When adding a component to a BordeLayout with no constraint, the Swing API will put the component in the CENTER.
A BorderLayout can contain exactly one component in each of the 5 layout constraints.
When a second component is added to the same (in this case CENTER) constraint of a BorderLayout, this implementation of Java will display the last component added.
As to what would be a better approach depends on the specific needs of the user interface.
When a second component is added to the same (in this case CENTER) constraint of a BorderLayout, this implementation of Java will display the last component added.
Not strictly true.
The BorderLayout will only reset the bounds (ie size and location) of the last component added to a specific constraint location. This is different from other layout managers in that they will reset the bounds of all components in the container.
In the example code the red panel was the "active" panel at the time the frame was validated by using the pack() method and therefore only its bound were set and therefore only it was painted.
For a demonstration of this process run the example below using the following steps:
Click on the "Add Panel in Center" button, nothing appears to happen even though the blue panel was added to the center.
Move the mouse over the red panel and the buttons will appear because the mouse rollover logic will cause the buttons to be repainted.
Now increase the frame width and the blue panel will appear under the red panel.
The code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class DisappearingPanelInFrame {
DisappearingPanelInFrame()
{
JButton button = new JButton ("Add Panel In Center");
button.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
JPanel blue = new JPanel();
blue.setBackground( Color.BLUE );
blue.add( new JButton("Button 1") );
blue.add( new JButton("Button 2") );
Component c = (Component)e.getSource();
Window window = SwingUtilities.windowForComponent(c);
window.add(blue );
window.revalidate();
window.repaint();
}
});
JFrame f = new JFrame(this.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(new ColoredPanel(Color.GREEN));
//f.pack();
f.add(new ColoredPanel(Color.RED));
f.add(button, BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new DisappearingPanelInFrame();
}
};
SwingUtilities.invokeLater(r);
}
}
class ColoredPanel extends JPanel {
ColoredPanel(Color color) {
setBackground(color);
setBorder(new EmptyBorder(20, 150, 20, 150));
}
}
When the blue panel is added to the BorderLayout and when the revalidate() is invoked the bounds of the blue panel are set.
However, because of the way Swing does ZOrder painting the blue panel is painted first and then the red panel is painted on top of the blue panel. The green panel still has a size of (0, 0) since it was never the "active" panel in the BorderLayout.CENTER when the frame was initially validated with the pack() method.
When the frame is resized, the blue panel being the "active" panel in the BorderLayout.CENTER, has its bounds adjusted, so it will now fill the extra space in the frame.
Now for another test:
pack() the frame after adding the green panel to the frame.
run the code and increase the width of the frame and the red and green frame will appear
then click the button and increase the width and now all 3 panels will appear
Bottom line is still the same:
Don't try to add multiple panels to the same constraint of a BorderLayout. If you do, then make sure you remove the previous panel or you have the potential for unexpected results.
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.
I would like to position a JComboBox in the way illustrated in the picture below. I was wondering if there is easy code to achieve this?
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class CenteredCombos {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
String[] values = {"Dog","Cat"};
// put the controls in a single column, with a little space.
JPanel controlPanel = new JPanel(new GridLayout(0,1,5,5));
for (int ii=0; ii<4; ii++) {
controlPanel.add(new JComboBox(values));
}
// the GUI as seen by the user (without frame)
// The GBL is used to center the controlPanel
JPanel gui = new JPanel(new GridBagLayout());
gui.setBorder(new EmptyBorder(2, 3, 2, 3));
gui.add(controlPanel);
// Make the BG panel more clear..
gui.setBackground(Color.WHITE);
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);
}
}
use an appropriate layout manager, e.g. BoxLayout. for more information, please see How to Use BoxLayout.
you could use MigLayout with row and column constraints like [max][min][max] - would divide your root panel into a 3x3 grid with "greedy" borders, and in the middle area (the one thats min/min) drop a JPanel with a vertical flow layout, into which you could add your components