JPanel repaint doesn't work - java

I have a simple task.
There is a frame. There are two panel in that frame. In second panel there is a button. When user click that button first panel must change its content.
Here is a code:
package test;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
class MyJPanel1 extends JPanel {
MyJPanel1() {
this.add(new JButton("MyJPanel1"));
}
}
class MyJPanel2 extends JPanel {
MyJPanel2() {
this.add(new JButton("MyJPanel2"));
}
}
class MyFrame extends JFrame {
JPanel topPanel = null;
MyFrame() {
super("Test");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new GridLayout(0, 1, 20, 20));
topPanel = new MyJPanel1();
this.add(topPanel);
JPanel bottomPanel = new JPanel();
this.add(bottomPanel);
JButton button = new JButton("switch");
button.addMouseListener(new MouseClickListener());
bottomPanel.add(button);
this.pack();
this.setVisible(true);
}
class MouseClickListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
topPanel = new MyJPanel2();
System.out.println("switch");
topPanel.invalidate();
topPanel.validate();
topPanel.repaint();
MyFrame.this.invalidate();
MyFrame.this.validate();
MyFrame.this.repaint();
}
}
}
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MyFrame();
}
});
}
}
But that don't work. After I click on button I see text in console, but first panel remain the same. I read that I must use invalidate() validate() and repaint() methods and I did, but it isn't help.
Any help would be appreciated.

If you want to "switch" panels then you should be using a CardLayout. The CardLayout allows 2 (or more) components to share the same space in a container but only one is ever visible at a time.
Read the section from the Swing tutorial on How to Use CardLayout for more information and working examples.

In your mouseClicked() method you create a new topPanel, but you don't do anything with it. Perhaps you meant to remove the original topPanel from myFrame, create a new topPanel, and then add the new toipPanel to myFrame.
Note that this may not be the best strategy (creating a new topPanel).

Related

JDesktopPane not displaying any component

I'm experimenting with desktop panes so I can use them in my work projects. The problem here is that I want to use an JInternalFrame within a JDesktopPane, in a normal JPanel it shows normally but cannot move it, using the desktop pane doesn't display any component added.
Here is the code of the last try, is simple just for learning how it works:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Internal_FrameShowtst extends JFrame{
Internal_FrameShowtst(){
BorderLayout bl = new BorderLayout();
JDesktopPane p = new JDesktopPane();
JPanel p1 = new JPanel();
JButton b = new JButton("click");
JInternalFrame in = new JInternalFrame("Test");
Internal_Frametst ift = new Internal_Frametst();
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//p1.add(new JLabel("hola"));
//in.add(p1);
//in.setVisible(true);
ift.setVisible(true);
}
});
p1.add(b);
bl.addLayoutComponent(p,BorderLayout.CENTER);
//p.add(in);
p.add(ift);
p.repaint();
setLayout(bl);
add(p);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Internal_FrameShowtst().setVisible(true);
}
});
}
}
custom internal frame class:
import javax.swing.*;
public class Internal_Frametst extends JInternalFrame {
Internal_Frametst(){
JPanel p = new JPanel();
JLabel label = new JLabel("Halo");
setIconifiable(true);
//setIcon(true);
setClosable(true);
p.add(label);
p.setSize(300,300);
add(p);
//setVisible(true);
}
}
I've read and tried the following:
Components inside JDesktopPane not showing
JDesktopPane not displaying components when adding JInternalFrame
I've tried adding the components directly, adding a JPanel, adding the internal frame, trying without it, creating the internal frame in the main class, creating my own internal frame in its own class, using layout managers with both panels (normal and desktop), all with the same result.
Your code creates several components that are never added to the visible UI at all. In the version you have posted, the internal frame is invisible and the button to make it visible is not part of the frame. But there are also problems with the initial sizes of the components.
I strongly recommend to keep the creation of a component, the setting of its initial properties, and the code to add it to a parent component close together in your source code.
Further, consider the points discussed in Prefer composition over inheritance? Your subclasses are entirely unnecessary.
Here is a revised version of your code that will open the internal frame when the button is clicked:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.*;
public class UiExample {
public static void main(String[] args) {
EventQueue.invokeLater(UiExample::initializeUI);
}
static void initializeUI() {
JPanel p1 = new JPanel();
JButton b = new JButton("Show Internal Frame");
p1.add(b);
JInternalFrame ift = initializeInternalFrame();
b.addActionListener(e -> {
ift.pack();
ift.setVisible(true);
});
JDesktopPane p = new JDesktopPane();
p.add(ift);
JFrame mainFrame = new JFrame();
mainFrame.setSize(300, 200);
mainFrame.getContentPane().add(p, BorderLayout.CENTER);
mainFrame.getContentPane().add(p1, BorderLayout.PAGE_START);
mainFrame.setVisible(true);
}
static JInternalFrame initializeInternalFrame() {
JInternalFrame iFrame = new JInternalFrame();
iFrame.setIconifiable(true);
// setIcon(true);
iFrame.setClosable(true);
iFrame.setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE);
JPanel p = new JPanel();
p.add(new JLabel("Hello"));
iFrame.add(p);
return iFrame;
}
}
Note that setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE) is necessary for being able to show the frame again via setVisible(true) after the internal frame has been closed.

Adding JPanel to another JPanel in a different class

I'm trying to add a JPanel to another JPanel from another class. The program does not longer throw an error and all methods have been run, but the new panel just has a black screen. A basic version of the program looks as follows:
package ninjadragon;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class NinjaDragon extends JFrame implements ActionListener{
public JPanel panelMain;
public JPanel panelTurnBase;
public static void main(String[] args) {
NinjaDragon();
}
public static void NinjaDragon() {
NinjaDragon frame;
frame = new NinjaDragon();
frame.CreateMenuScreen();
JFrame.setDefaultLookAndFeelDecorated(true);
frame.setSize(750, 750);
frame.show();
frame.setResizable(false);
frame.pack();
}
private void CreateMenuScreen() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container window = getContentPane();
panelMain =new JPanel();
panelMain.setPreferredSize(new Dimension(750,750));
panelMain.setBackground(Color.BLACK);
panelMain.setLayout (new FlowLayout());
window.add(panelMain);
PanelTop();
PanelButtons();
PanelIcon();
}
#Override
public void actionPerformed(ActionEvent event) {
Object eventSource = event.getSource();
if (eventSource == buttonStart) {
panelMain.removeAll();
TurnBase TB = new TurnBase();
TB.CreateTurnBase();
}
}
The other class looks something like this:
public void CreateTurnBase() {
panelMain=new JPanel();
panelTurnBase =new JPanel();
setLayout(new FlowLayout());
setPreferredSize(new Dimension(750,750));
setBackground(Color.BLUE);
panelTurnBase.setLayout (new FlowLayout());
panelMain.add(panelTurnBase);
System.out.println("1");
PanelTurnBaseTop();
PanelGameScreen();
PanelTurnBaseBottom();
repaint();
revalidate();
buttonAttack = new JButton("Attack");
buttonAttack.addActionListener(this);
panelTurnBase.add(buttonAttack);
System.out.println("2");
}
The reason the panel has "just a black screen" is because you dont add anything to it, and you tell it to have a black screen.
i.e
panel.setBackground(Color.BLACK);
You never actually do anything to that first panel inside of any of those methods, which I can assume based on your representation of your second "class" (it's a method). Hence why it stays black.
You say:
panelMain=new JPanel();
panelTurnBase =new JPanel();
You're creating new JPanels every time and just call them panelMain and they just sit inside of that method, never leaving. You either need to return a JPanel or give it a JPanel as an argument.
The program is doing exactly what you tell it to do.
Also, do not compare Objects like this:
eventSource == buttonStart
You should use:
eventSource.equals(buttonStart);

Why is my frame not resizing?

When you choose a state, the frame's content pane removes its components. Then depending on the state you chose, another class takes the content pane and adds onto it. After doing so, the frame gets packed to resize accordingly.
I want free control over whats in the Frame, such as being able to put panels side by side, above one another, ect.. so I really don't want to use CardLayout. (I'd much rather have 1 panel handle both loginscreen and chat. Then, be able to display another panel next to that one).
I'm using the JFrame's content pane for my login and chat screen, but when I run my code, I get a small frame (has SOME size, but hardly any) that's white on the inside.
show frame
switch to chat
remove everything on pane (currently nothing)
add components onto pane
pack frame so it can size accordingly to the pane
revalidate if needed (not sure when I need to revalidate or not)
Please tell me what I'm doing wrong, and maybe guide me in the right direction.
PS: There are no errors
EDIT: The only thing I can think of is that since I'm passing frame.getContentPane() through the method, and methods are pass-by-value, the actual reference to frame.getContentPane() might not be noticing the changes I'm asking for. But then I don't know why the inside of the frame would be white (as if my JTextArea tried rendering), and there's padding on the inside of the frame, so there has to be something happening..
Main.java:
package main;
import ui.Frame;
public class Main {
public static Frame frame = new Frame();
public static void main(String[] args) {
frame.show();
frame.switchState(State.chat);
}
public static enum State {
login, chat;
}
}
Frame.java:
package ui;
import main.Main.State;
import javax.swing.JFrame;
public class Frame {
private Panel currentpanel; //from package ui, not AWT
private ChatPanel chatpanel = new ChatPanel();
private JFrame frame;
public Frame() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
public void show() {
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void switchState(State state) {
frame.removeAll();
switch(state) {
case chat:
currentpanel = chatpanel;
currentpanel.addComponentsTo(frame.getContentPane());
break;
}
frame.pack();
frame.revalidate();
}
}
Panel.java:
package ui;
import java.awt.Container;
public interface Panel {
public void addComponentsTo(Container pane);
}
ChatPanel.java:
package ui;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JTextArea;
public class ChatPanel implements Panel {
private JTextArea toparea = new JTextArea();
private JTextArea bottomarea = new JTextArea();
#Override
public void addComponentsTo(Container pane) {
pane.setPreferredSize(new Dimension(500, 500));
pane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
gbc.gridx = 0;
gbc.ipadx = 450;
gbc.ipady = 350;
pane.add(toparea, gbc);
gbc.gridy = 1;
gbc.ipady = 100;
pane.add(bottomarea);
}
}
I know that can be quite frustrating.
have you tried calling
pack(); or repaint();
I found the problem. It was calling frame.removeAll(); before adding anything to it.
When I tried if(frame.getComponents().length > 0), it still triggered removeAll(), but the problem wasn't fixed. Seeing how I haven't added anything yet, I checked to see what the component was (by printing out the object), and it was a JRootPane.
After that, I tried printing out frame.getContentPane().getComponents().length, it gave me 0 as expected.
Long story short: This is how switchPanel(State state) should look:
public void switchState(State state) {
if(frame.getContentPane().getComponents().length > 0)
frame.removeAll();
switch(state) {
case chat:
currentpanel = chatpanel;
currentpanel.addComponentsTo(frame.getContentPane());
break;
}
frame.pack();
frame.revalidate();
}
NOTE: I still recommend CardLayout, but if you insists in dynamically setting the frame's content pane the here it is.
The frame class
public class SwitchingFrame extends JFrame {
public static enum State {ONE, TWO}
private PanelONE panel1 = new PanelONE();
private PanelTWO panel2 = new PanelTWO();
public SwitchingFrame() {
getContentPane().setLayout(new BorderLayout());
pack();
setVisible(true);
}
public void switchState(State state) {
setVisible(false);
getContentPane().removeAll();
if (state.equals(State.ONE))
getContentPane().add(panel1, BorderLayout.CENTER);
else
getContentPane().add(panel2, BorderLayout.CENTER);
pack();
setVisible(true);
}
}
The two panel classes which are switched
public class PanelONE extends JPanel {
public PanelONE() {
add(new JLabel("ONE"));
}
}
public class PanelONE extends JPanel {
public PanelTWO() {
add(new JLabel("TWO"));
}
}
The main method which includes buttons to simulate changing the panels
public class TestSwitchingFrame {
public static void main(String[] args) {
final SwitchingFrame sframe = new SwitchingFrame();
JFrame frame = new JFrame();
JButton b1 = new JButton("ONE");
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sframe.switchState(SwitchingFrame.State.ONE);
}
});
JButton b2 = new JButton("TWO");
b2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sframe.switchState(SwitchingFrame.State.TWO);
}
});
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(b1);
frame.getContentPane().add(b2);
frame.pack();
frame.setVisible(true);
}
}
You do not need (not should) write your own interface (Panel). Your two panels should extend JPanel and set within the frames content pane. Your frame should extend JFrame and does not need to override its show method (let Swing do it for you). The specific implementation of the switchState function should eventually depend on the end result you want. There are similar ways to accomplish almost the same result.

Java JApplet with/without validate()

I have this piece of code and read that validate can refer to laying out a container's subcomponents. "Layout-related changes, such as setting the bounds of a component, or adding a component to the container, invalidate the container automatically." (source: javadoc).
However, I see no difference whatsoever between keeping validate() or removing it from this little piece of code.
Can you show me a convincing example where you can see distinct behaviour in two cases (with or without validate) to prove a point? Any other comments/advice appreciated.
public class Sw1
extends JApplet
{
JLabel lbl;
public void init()
{
lbl = new JLabel ("a label");
JPanel pan = (JPanel) getContentPane ();
pan.add(lbl);
validate();
}
}
Here is the program after I intended the push of a button to add a label. It renders an exception when I push the button:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class Sw_test
extends JApplet
implements ActionListener
{
JLabel lbl;
JButton bt ;
JPanel pan ;
JLabel l;
public void init()
{
lbl = new JLabel ("label 1");
bt = new JButton ("go ahead, press me");
bt.addActionListener(this);
JPanel pan = (JPanel) getContentPane ();
pan.setLayout(new FlowLayout());
pan.add(lbl);
pan.add(bt);
validate();
}
public void actionPerformed(ActionEvent ev)
{
l = new JLabel("new label");
pan.add(l);
}
}
You would need to call it if you add a component to a panel after it has been initialized and made visible.
Try adding a button to your applet, and on the click of the button, add a new label to the applet.
i will quote the API:
The validate method is used to cause a container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified (added to or removed from the container, or layout-related information changed) after the container has been displayed.
so as you see, it is important if you modify your layout, AFTER it has been initialized.
That is the reason why you donĀ“t see any difference
btw: here is your example :
public class TestFrame extends JFrame{
private JButton b = new JButton();
public TestFrame() {
this.setLayout(new GridLayout(5,5));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(b);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
TestFrame.this.add(new JLabel("whatever"));
//try it with and without
//validate();
}
});
this.setSize(300, 300);
this.setVisible(true);
}
public static void main(String[] args) {
new TestFrame();
}
}

MouseEvent lost in JScrollPane

This is the code I am using to show the issue which I am facing in another project.
I am not getting any line like this if I use JScrollPane as a wrapper for panel2. Why?
I want to click on JscrollPane and got event printed as following.
java.awt.event.MouseEvent[MOUSE_CLICKED,(800,469),absolute(808,499),button=1,modifiers=Button1,clickCount=1] on javax.swing.JPanel[,0,0,934x612,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.border.LineBorder#cc0e01,flags=9,maximumSize=,minimumSize=,preferredSize=java.awt.Dimension[width=880,height=630]]
If now I change
panel1.add(pane);
to
panel1.add(panel2);
Then the message above got printed.
public class LostMouseEvent {
public static void main(String[] args) {
new LostMouseEvent();
}
public LostMouseEvent() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
JScrollPane pane = new JScrollPane(panel2);
panel1.setPreferredSize(new Dimension(880, 630));
panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
panel2.setPreferredSize(new Dimension(840, 610));
panel2.setBorder(BorderFactory.createLineBorder(Color.green));
panel1.add(pane);
frame.add(panel1);
frame.pack();
frame.setVisible(true);
frame.setSize(950, 650);
panel1.addMouseListener(new MyMouseListener());
}
});
}
private class MyMouseListener extends MouseAdapter {
#Override
public void mouseClicked (MouseEvent me) {
System.out.println(me);
}
}
}
UPD: In fact in my project there is more than just one panel2. Originally, I had panel1 and many panel2 inside. Then I wanted to wrap each panel2 with JScrollPane and started to face this problem.
I need to have only one MouseListener to minimize changes to the code.
Use EDT for creation and manipulation of Swing components
Dont call setSize() rather call pack() before setting JFrame visible.
Dont call setPrefferedSize() rather override getPrefferedSize()
Your code works as expected, it will only print the message if panel1 is clicked, note panel1 is behind JScrollPane, thus anything outside the green border is panel1. To make it work for both the JScrollpane/panel2 and JPanel/panel1 simply add the MouseListener to BOTH of the required components:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LostMouseEvent {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LostMouseEvent();
}
});
}
public LostMouseEvent() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel panel1 = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(880, 630);
}
};
JPanel panel2 = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(840, 610);
}
};
JScrollPane pane = new JScrollPane(panel2);
panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
panel2.setBorder(BorderFactory.createLineBorder(Color.green));
panel1.add(pane);
frame.add(panel1);
MouseListener ml=new MyMouseListener();
//add mouse listener to panel1 and panel2
panel1.addMouseListener(ml);
panel2.addMouseListener(ml);
//alternatively add to pane
//pane.addMouseListener(ml);
frame.pack();
frame.setVisible(true);
}
});
}
private class MyMouseListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent me) {
System.out.println(me);
}
}
}
EDIT:
I personally would not recommend this, however,
To add a single listener to the JFrame that will capture all MouseEvents use Toolkit class and call addAWTEventListener like so:
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent awte) {//all mouse events will be processed here you will have to check for the mouse events you are interested in
System.out.println(awte);
}
}, AWTEvent.MOUSE_EVENT_MASK);//for Mouse events only
UPDATE 1:
You could also add the MouseListener to your JFrames glasspane via JFrame.getGlassPane().addMouseListener(ml) dont forget to set the glasspane visible after setting JFrame visible. This will allow you to only have to add a single Listener. See here:
...
MouseListener ml = new MyMouseListener();
//add mouse listener to panel1 and panel2
//panel1.addMouseListener(ml);
//panel2.addMouseListener(ml);
//alternatively add to pane
//pane.addMouseListener(ml);
frame.getGlassPane().addMouseListener(ml);
frame.pack();
frame.setVisible(true);
frame.getGlassPane().setVisible(true);
...
UPADTE 2:
The main reason for you having the problem of the MouseEvent getting lost in JScrollPane is because its a bug. See here.
The work around shown is:
public Test()
{
setUI(new javax.swing.plaf.metal.MetalScrollPaneUI(){
public void installListeners(JScrollPane scrollPane){}
});
JPanel canvas = new JPanel();
canvas.add( new JLabel("Test") );
setViewportView( canvas );
setVisible(true);
}
MouseEvent lost in JScrollPane
answer is very / quite simple, be sure that there isn't something about lost events, nor with JScrollPane,
Swing JComponent can firing event only if is there added proper Listener
you not added MouseListener to second JPanel,
this JPanel is placed into parent JPanel, this parent has added MouseListener then firing mouseEvent, sure in your case only outside of Bounds of 2nd JPanel added to this container
then 2nd JPanel is deepestComponentAt, and not possible fire event without redispatch coordinates from parent to child
you can to redispatch event programatically too,

Categories