The problem is when I run the code below, I see a white splash on my screen for about maybe 100ms which is extremely annoying:
import java.awt.*;
import javax.swing.*;
public class MyPanel extends JPanel {
public MyPanel() {
this.setBackground(Color.black);
JTextArea jtext = new JTextArea(10, 20);
jtext.setBackground(Color.black);
jtext.setForeground(Color.white);
jtext.setText("some stuff here");
add(jtext);
add(new JButton("some button"));
// TODO: gui elements
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("MyPanel");
frame.getContentPane().add(new MyPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setVisible(true);
}
});
}
}
My entire work setup is in dark theme. Equivilant code written in C++ with Qt does not show any white splashs on the same system. I'm using Ubuntu 12.04 and Intel HD Graphics 3000, java version "1.7.0_45".
Please let me know how this can be fixed.
I googled this problem but all I found was: "how to create splash screen in java?" which what I'm trying to avoid.
update: solution: I'm not sure why this works:
I extended MyPanel to JFrame and put most of the functions there. then in the run() invoked it and set visible to true.
here is the code:
import java.awt.*;
import javax.swing.*;
public class MyPanel extends JFrame {
public MyPanel() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBackground(Color.black);
JTextArea jtext = new JTextArea(10, 20);
jtext.setBackground(Color.black);
jtext.setForeground(Color.white);
jtext.setText("some stuff here");
getContentPane().add(jtext);
// TODO: gui elements
setExtendedState(JFrame.MAXIMIZED_BOTH);
setSize(350, 250);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MyPanel mypanel = new MyPanel();
mypanel.setVisible(true);
}
});
}
}
I did not see the 'white flash' here, but try this altered code that calls pack() on the frame.
import java.awt.*;
import javax.swing.*;
public class MyPanel extends JPanel {
public MyPanel() {
this.setBackground(Color.black);
JTextArea jtext = new JTextArea(10, 20);
jtext.setBackground(Color.black);
jtext.setForeground(Color.white);
jtext.setText("some stuff here");
add(jtext);
add(new JButton("some button"));
// TODO: gui elements
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("MyPanel");
frame.getContentPane().add(new MyPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.pack();
frame.setVisible(true);
}
});
}
}
Although it looks normal to me on Ubuntu 12.04 / Intel Iris Pro 5200 / Java 6, I can see the effect by running in a slowed emulator. Setting the frame's background to black before setVisible() seems to help.
frame.setBackground(Color.black);
Related
I am currently working on a little game but I just encountered a problem:
I have three classes, the first one is the JFrame:
public class Game
{
public static void main(String[] args)
{
new Game().gui();
}
public void gui()
{
DrawPanel panel = new DrawPanel();
JFrame frame = new JFrame("Test");
//frame.add(panel);
frame.add(new MainMenu());
frame.setSize(800, 700);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
}
Now I have two other classes, one is the mainMenu, currently consisting of just one JButton.
I decided to make the menu its own class because later, I want to call the menu by pressing escape, but the problem is that (for testing reasons) I want to draw an rectangle when "start" is pressed. I tried different approaches but nothing happens.
public class MainMenu extends JPanel implements ActionListener
{
GamePanel panel = new GamePanel();
public MainMenu()
{
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton b1 = new JButton("Start");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipadx = 200;
b1.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
add(b1, c);
}
}
public class DrawPanel extends JPanel
{
public void paint(Graphics g)
{
g.drawRect (10, 10, 200, 200);
}
}
There are several errors I found in your code:
You're not adding your DrawPanel to anything because this line
frame.add(panel);
is commented, so, that leads us to the next problem:
You're overriding paint() method instead of paintComponent() and also don't forget to call
super.paintComponent();
at the very beginning of the method. Without it, you're preventing your code to keep painting the rest of the components. See Custom painting in Swing
That still doesn't makes anything appear because you haven't declared a location for your DrawPanel and thus it's taking JFrame's default Layout Manager which is BorderLayout and it's default location when not specified is CENTER, and as you're adding new MainMenu() to it on the same position it replaces the DrawPanel panel, since only one component can exist on the same position.
Instead try and place the DrawPanel to the CENTER and the MainMenu to the SOUTH. It now should look like this:
Don't forget to place your program on the Event Dispatch Thread (EDT) by writing your main method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
You're implementing ActionListener on MainMenu but not implementing it's methods, remove it or implement the actionPerformed method and move the code inside the b1 action listener to it. However I highly recommend you to take at Should your class implement ActionListener or use an object of an anonymous ActionListener class too
You're playing with MainMenu's JPanel visibility, instead you should try using a CardLayout or consider using JDialogs.
For example I would make a JDialog and place the JButton there, so it will open the JFrame with the Rectangle drawn in it.
Now, the code that made the above output and follows all recommendations (but #6) is:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Game {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Game().gui();
}
});
}
public void gui() {
DrawPanel panel = new DrawPanel();
JFrame frame = new JFrame("Test");
frame.add(panel, BorderLayout.CENTER);
frame.add(new MainMenu(), BorderLayout.SOUTH);
frame.setSize(400, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
}
class MainMenu extends JPanel {
// GamePanel panel = new GamePanel();
public MainMenu() {
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton b1 = new JButton("Start");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipadx = 200;
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
add(b1, c);
}
}
class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(10, 10, 200, 200);
}
}
As suggested in the comments by #MadProgrammer, you can also override the getPreferredSize() method of your DrawPanel and then call frame.pack():
Your DrawPanel class should now look like this:
class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(10, 10, 200, 200);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
For example instead of the default operating system container around the application you could have something custom like this swing project below.
You can use a transparent image on a undecorated frame with a transparent background:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TransparentImageFrame
{
private static void createAndShowUI()
{
JLabel label = new JLabel( new ImageIcon("...") );
label.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e)
{
if (e.getClickCount() == 2)
{
System.exit(0);
}
}
});
JFrame frame = new JFrame("Image Frame");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add( label );
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
The mouse will only respond to non-opaque pixels in whatever image you use.
The host OS owns the frame decorations, but you can Create Translucent and Shaped Windows and use Frame#setUndecorated(), as shown here.
I have got a frame in which sometimes a dialog is opened. I would like this dialog to be attached to the existing frame, so for example when I drag that frame the opened dialog follows it. I have heard that this might be possible to achieve using GlassPane but I need some hints. Right now, when I open a new dialog and set its location relative to frame it looks like this:
I would like the "testDialog" to appear next to the frame attached to its upper-right corner.
When I drag the "test" frame, the "testDialog" follows it.
Here is a working example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Example {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
showGUI();
}
});
}
public static void showGUI() {
final JFrame frame=new JFrame("test");
JButton open=new JButton("Open new dialog");
open.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("test");
JDialog dialog=new JDialog((Frame)null,"testdialog");
dialog.setPreferredSize(new Dimension(200,300));
dialog.getContentPane().add(new JLabel("testlabel"));
dialog.pack();
dialog.setLocationRelativeTo(frame);
dialog.setVisible(true);
}
});
frame.setLayout(new FlowLayout());
frame.getContentPane().add(open);
frame.getContentPane().add(new JLabel("test"));
frame.setLocationRelativeTo(null);
frame.setPreferredSize(new Dimension(400, 200));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
I was able to hide the the maximize icon by setting setResizable(false), how can achieve the same for the minimize icon?
Here is the way, how to remove min/max buttons from JFrame
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class JFrameTest extends JDialog {
public JFrameTest(JFrame frame, String str) {
super(frame, str);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
}
public static void main(String[] args) {
JFrameTest frame = new JFrameTest(new JFrame(), "Title");
JPanel panel = new JPanel();
panel.setSize(200, 200);
JLabel lbl = new JLabel("JFrame without max/min");
panel.add(lbl);
frame.add(panel);
frame.setSize(400, 400);
frame.setVisible(true);
}
}
But it's really nasty to users and could be walk arounded by key shortcuts etc.
Check the example Specifying Window Decorations
Can someone explain why the following doesn't work as I expect?
Pressing the button 'should' result in the display only containing the (empty) JScrollPane, ie the input field and button should disappear. However they stay until the component is resized...
public static void main(String[] args)
{
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
Container cp = frame.getContentPane();
cp.setLayout(new FlowLayout());
cp.add(new JScrollPane(panel));
Component textField = new JTextField("i am input");
JButton button = new JButton(new AbstractAction("i am pressy")
{
#Override
public void actionPerformed(ActionEvent e)
{
// this is already on the EDT
panel.removeAll();
panel.revalidate();
}
});
panel.setLayout(new FlowLayout());
panel.add(textField);
panel.add(button);
frame.pack();
frame.setVisible(true);
}
Thanks for your help. p.
When updating a visible GUI the code should be:
panel.revalidate();
panel.repaint(); // sometimes needed, this appears to be one of them
The revalidate() method marks components as needing to be laid out, but until something triggers repaint() you won't see any change. Resizing the parent window is one such trigger; switching applications is another. In this previous version, note how setSize() on the panel obviates the need for repaint(). Similarly, this example changes the layout in resetGame().
The article Painting in AWT and Swing goes into more detail.
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
/** #see https://stackoverflow.com/questions/5812002 */
public class RevalidateTest {
private static JPanel panel = new JPanel(); // default FlowLayout
private static JTextField text = new JTextField("Text field");
private static JButton clear = new JButton(new AbstractAction("Clear") {
#Override
public void actionPerformed(ActionEvent e) {
panel.removeAll();
panel.add(reset);
panel.revalidate();
panel.repaint();
}
});
private static JButton reset = new JButton(new AbstractAction("Reset") {
#Override
public void actionPerformed(ActionEvent e) {
panel.removeAll();
panel.add(text);
panel.add(clear);
panel.revalidate();
panel.repaint();
}
});
static void createAndShowGUI() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
panel.add(text);
panel.add(clear);
frame.add(panel); // default BorderLayout center
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
You can execute panel.repaint() as specified in the comment by #Jeremy, however, the UI will still change when you resize the window. The reason being that the removal of the elements from the JPanel will cause the panel to resize. A repaint operation will not cause the panel to resize until the JFrame rechecks its layout (as happens on a window resize).
To make sure that the layout is correctly layed out on a change, you can call frame.validate(). This operation will cause the JFrame to revalidate itself and all child components, which is the same operation that is taking place during a window resize event. To execute this method in your code you would need to change JFrame frame to final, i.e.,
final JFrame frame = new JFrame("test");