I have a JFrame containing various components and I would like to add a translucent grey overlay over the top while the application is initializing various things. Ideally it would prevent interaction with the underlying components and would be able to display some "Loading..." text or a spinning wheel or something similar.
Is there a simple way to do this using Java and Swing?
Take a look at JRootPane and JLayeredPane http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html#layeredpane
What you're asking about specifically sounds like a Glass Pane.
http://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html#glasspane
The Glass Pane prevents interaction with underlying components and can be used to display something on top of your JFrame.
As #David said, you can use the glass pane for displaying some loading text or image above the rest of the application.
As for the grey overlay: why don't you use the built in ability to disable components as long as your application is loading? Disabled components will get grayed out automatically and cannot be interacted with by the user.
Something like this:
public class LoadingFrame extends JFrame{
JButton button;
public LoadingFrame() {
button = new JButton("ENTER");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Application entered");
}
});
setLayout(new BorderLayout());
add(button, BorderLayout.CENTER);
}
public void startLoading(){
final Component glassPane = getGlassPane();
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
final JLabel label = new JLabel();
panel.add(label, BorderLayout.SOUTH);
setGlassPane(panel);
panel.setVisible(true);
panel.setOpaque(false);
button.setEnabled(false);
Thread thread = new Thread(){
#Override
public void run() {
for (int i = 5; i > 0; i--) {
label.setText("Loading ... " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// loading finished
setGlassPane(glassPane);
button.setEnabled(true);
}
};
thread.start();
}
public static void main(String[] args) {
LoadingFrame frame = new LoadingFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.startLoading();
frame.setVisible(true);
}
}
Related
I would like to make a JPanel pop up over an other JPanel in java. However I can not find anything useful on the internet. I tried playing around with absolute positioning but things like buttons from the bottom layer are showing through the top layer.
I uploaded a really ugly drawing of what I want to do.
Is there an easy way of doing this?
Edit:
I tried to make what "Ulkra" suggested. Here is the code and a screenshot:
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(new Dimension(400, 400));
JPanel panel = new JPanel();
JPanel foregroundPanel = new JPanel();
foregroundPanel.setVisible(false);
foregroundPanel.setBackground(Color.BLUE);
foregroundPanel.setLayout(new BoxLayout(foregroundPanel, BoxLayout.Y_AXIS));
JPanel backgroungPanel = new JPanel();
backgroungPanel.setBackground(Color.RED);
backgroungPanel.setLayout(new BoxLayout(backgroungPanel, BoxLayout.Y_AXIS));
for (int i = 0; i < 10; i++) {
backgroungPanel.add(new JButton("BackgroundBtn " + i));
}
foregroundPanel.add(new JButton("ForegroundBtn 1"));
JButton makeVisibleBtn = new JButton("+");
makeVisibleBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
foregroundPanel.setVisible(true);
}
});
backgroungPanel.add(makeVisibleBtn);
JButton makeInvisibleBtn = new JButton("-");
makeInvisibleBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
foregroundPanel.setVisible(false);
}
});
foregroundPanel.add(makeInvisibleBtn);
panel.add(backgroungPanel);
panel.add(foregroundPanel);
frame.add(panel);
}
}
For this, you have to use a Panel (if you are using NetBeans, you can find it in Swing Containers) and in the constuctor of the Frame, you have to SET IT AS UNVISIBLE, so you need the buttom to see it: (In this example, i call my Panel as "jPanel1" and i put a buttom in it)
public Main() {
initComponents();
jPanel1.setVisible(false);//<-- This!
this.setLocationRelativeTo(null);
}
Then, you create the buttom to "activate it" and but this code in:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
jPanel1.setVisible(true);
}
(And of course, if you wanna set it as unvisible you just have to do the same as in the constructor).
I test it and it works:(Imgur is not working so excuse i have to use postimg.org)
https://postimg.org/gallery/1z6fpajva/
Now you could put a border so you can differentiate your new Panel from your other Frame:
https://postimg.org/image/mdpf7hvxx/
I'm changing my JFrame's content pane and simply want to focus a JTextField in the new panel. So I'm doing this:
JPanel pNew = new JPanel();
frame.setContentPane(pNew);
frame.revalidate();
frame.repaint();
public JPanel() {
...
tf.requestFocusInWindow();
}
When I use setVisible(false) and setVisible(true) instead of revalidating and repainting my frame, I get my wished effect, but that's not the way I want to do it.
What else happens in setVisible() but revalidating and repainting?
A CardLayout is typically used to swap panels.
However, even the default implementation of CardLayout does not set focus on the panel when it is swapped. However you can check out Card Layout Focus which will allow you to request focus on the panel when it is switched.
The requestFocusInWindow() method only works on a component that is displayed in a visible frame. So you can't invoke the method in the constructor of the class.
You could use the RequestFocsListener found in Dialog Focus. It will wait until the panel is added to a visible GUI before generating the event.
I got it to work simply by putting the requestFocusInWindow() call in the button's action listener. As camickr mentioned the call needs to be made after the constructor. Here's an example program showing how I got it to work. Hope it helps!
public class PanelRevalidate {
public JFrame frame;
public MyPanel panel1, panel2;
public PanelRevalidate() {
frame = new JFrame();
panel1 = new MyPanel(1);
panel2 = new MyPanel(2);
frame.setContentPane(panel1);
panel1.getSwap().addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
frame.setContentPane(panel2);
frame.revalidate();
panel2.getTextField().requestFocusInWindow();
}
});
panel2.getSwap().addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
frame.setContentPane(panel1);
frame.revalidate();
panel1.getTextField().requestFocusInWindow();
}
});
frame.setVisible(true);
frame.setResizable(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new PanelRevalidate();
}
});
}
}
And the MyPanel class:
public class MyPanel extends JPanel {
public JTextField tf;
public JButton swap;
public JLabel panel_label;
public MyPanel(int n) {
tf = new JTextField(25);
swap = new JButton("Swap");
panel_label = new JLabel("panel " + n);
add(tf);
add(swap);
add(panel_label);
}
public JButton getSwap() {
return swap;
}
public JTextField getTextField() {
return tf;
}
}
I'm fairly new to Java Swing and I'm running into a few problems.
As a side question, when making a fairly large Java Swing Application, what is the best way to split up code?
In my case I want to have an application that has a layout just as Microsoft Word where there is a JToolBar filled with buttons and a main JPanel where changes are made based on the buttons pressed in the Tool Bar.
So as shown in the code below, I have a JFrame and I call the MainPanel class in order to create a panel and add a ToolBar with a button. When the button is pressed it adds a button to the panel. The problem comes when you click the button nothing shows up until you resize the window(in my case I simply manually drag the screen to make it larger).
public class Main {
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("MathMaker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create the menu bar. Make it have a green background.
//MainToolBar mainTB = new MainToolBar();
MainPanel mainPanel = new MainPanel();
frame.getContentPane().add(mainPanel.getGUI(), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
public class MainPanel implements ActionListener{
JPanel mPanel;
JToolBar mToolBar;
JButton addQuestion;
public MainPanel() {
mPanel = new JPanel(new BorderLayout());
mToolBar = new JToolBar();
addQuestion = new JButton("test");
addQuestion.addActionListener(this);
mPanel.setLayout(new BorderLayout());
mPanel.setBackground(new Color(248, 213, 131));
mPanel.setPreferredSize(new Dimension(200, 180));
mToolBar.add(addQuestion);
mPanel.add(mToolBar, BorderLayout.PAGE_START);
}
public JComponent getGUI()
{
return mPanel;
}
#Override
public void actionPerformed(ActionEvent e) {
JButton temp = new JButton("temp");
mPanel.add(temp);
}
}
You should revalidate your panel
#Override
public void actionPerformed(ActionEvent e) {
JButton temp = new JButton("temp");
mPanel.add(temp);
mPanel.revalidate();
mPanel.repaint();
}
I believe you need to call revalidate() and repaint() to see the changes, here is a similar question here
The problem here is the panel is not repainted automatically.. When you resize the panel Java repaints the panel on the screen. Try repainting the panel everytime any button to modify the panel is clicked..
Just call the validate() and repaint() method with the panel
How to change Look and Feel for the ToolBar, the top menu (where the buttons to close, minimize, maximize)
Is it possible to like something change? (Add, delete button, assign a background)
What is import is required to create it?
you can set the background image of a JButton you could have a look at this: Swing Tutorial: JButton which shows the use of the new JButton(String text,ImageIcon imgIco) to create a JButton with an ImageIcon and String.
To set the colour of the background and text you could use setBackground(Color c) and setForeground(Color c)
or
Alternatively just customize Look and Feel color scheme by setting an appropriate supported Look and Feel and then change the color scheme/size etc of its components thier are hundreds of things you can change for every component see this for them all.
To customize the Exit, Minimize and Maximize Toolbar buttons this can also be using the Look and Feel ( Custom design for Close/Minimize buttons on JFrame ):
import java.awt.BorderLayout;
import javax.swing.*;
public class FrameCloseButtonsByLookAndFeel {
FrameCloseButtonsByLookAndFeel() {
String[] names = {
UIManager.getSystemLookAndFeelClassName(),
UIManager.getCrossPlatformLookAndFeelClassName()
};
for (String name : names) {
try {
UIManager.setLookAndFeel(name);
} catch (Exception e) {
e.printStackTrace();
}
// very important to get the window decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame f = new JFrame(UIManager.getLookAndFeel().getName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel gui = new JPanel(new BorderLayout());
f.setContentPane(gui);
JTree tree = new JTree();
tree.setVisibleRowCount(4);
gui.add(tree, BorderLayout.LINE_START);
gui.add(new JScrollPane(new JTextArea(3,15)));
JToolBar toolbar = new JToolBar();
gui.add(toolbar, BorderLayout.PAGE_START);
for (int ii=1; ii<5; ii++) {
toolbar.add(new JButton("Button " + ii));
if (ii%2==0) {
toolbar.addSeparator();
}
}
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new FrameCloseButtonsByLookAndFeel();
}
});
}
}
Well the easiest way to change frame titlebar look is to set LookAndFeel before you create your frame.
Probably this is what you are looking for - http://www.jtattoo.net/ScreenShots.html
I create a Popup using the PopupFactory.getPopup method. According to the documentation, I am required to call the hide() method on the popup when it is no longer needed.
In my application, the popup is the child of a JLabel which may be removed from the current frame in a number of different situations. (Either the JLabel itself or one of its parent containers is removed.) Rather that calling hide() in every single place (and making the Popup object available in all these places) I would prefer to be able to detect the removal of the JLabel or one of its parent containers.
How can I detect the removal? I naively assumed that the removal of a component meant the removal/hiding of its children, but as the code below shows, the popup survives the removal of the JLabel.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Xyzzy extends JFrame {
static Xyzzy frame;
static JPanel panel;
static JLabel text1;
static JLabel text2;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame = new Xyzzy();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.PAGE_AXIS));
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS));
frame.add(panel);
text1 = new JLabel("text1");
text2 = new JLabel("text2");
panel.add(text1);
frame.add(new JButton(new AbstractAction("Add popup") {
public void actionPerformed(ActionEvent e) {
PopupFactory factory = PopupFactory.getSharedInstance();
Popup popup = factory.getPopup(text1, new JLabel("POPUP"),frame.getX()+300,frame.getY()+300);
popup.show();
}
}));
frame.add(new JButton(new AbstractAction("New label") {
public void actionPerformed(ActionEvent e) {
panel.remove(text1);
panel.add(text2);
panel.revalidate();
}
}));
frame.setSize(600, 600);
frame.setVisible(true);
}
});
}
}
This code creates a JFrame displaying the text "text1" and two buttons. If you press the button labeled "Add popup", a Popup with the text "POPUP" appears in the window. This Popup is a child of text1. Press the "New label" button and "text1" is removed from the display, but the Popup survives.
I need to be able to detect when text1 or the containing panel is removed so that I can hide the popup. I want to avoid adding code where the actual remove() method is called.
You can use HierarchyListener:
public void actionPerformed(ActionEvent e) {
PopupFactory factory = PopupFactory.getSharedInstance();
final Popup popup = factory.getPopup(text1, new JLabel("POPUP"),frame.getX()+300,frame.getY()+300);
text1.addHierarchyListener(new HierarchyListener() {
public void hierarchyChanged(HierarchyEvent e) {
if (e.getID() == HierarchyEvent.HIERARCHY_CHANGED
&& (e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
popup.hide();
}
}
});
popup.show();
}