How to control JPanel setSize when using BorderLayout - java

I have a ComponentListener for the main JFrame that sends an event when it is resized but by the time the event is fired, the Jpanel has already been resized.
public class Test implements ComponentListener {
private JFrame frame;
private JPanel panel;
public Test() {
frame = new JFrame();
panel = new JPanel();
frame.setPreferredSize(new Dimension(300, 300));
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.addComponentListener(this);
frame.pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
Test test = new Test();
test.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
#Override
public void componentResized(ComponentEvent e) {
System.out.println(frame.getSize() + " : " + panel.getSize());
frame.revalidate();
}
#Override
public void componentHidden(ComponentEvent e) {}
#Override
public void componentMoved(ComponentEvent e) {}
#Override
public void componentShown(ComponentEvent e) {}
}
How do I control JPanel setSize during a JFrame resize?
Edit: I understand BorderLayout sets components to fill the border. How does it set the size of the component? Is it calling setSize? if yes, why an overridden setSize called?

You can't control a components size directly when it is added to a container under a LayoutManager's control. That defeats the purpose of using a layout manager.
If you want to be notified when a components size has changed, add a ComponentListener to the component (not the container).

Is it calling setSize?
Layout manager use getPreferredSize(), getMinimumSize() and getMaximum() size as hints. The layout manager and use or ignore these hints. When you add a component to the CENTER of the BorderLayout all the hints are ignored and the component is sized to the space available. So, yes, the layout manager will invoke the setSize() (and setLocation) methods of a component. You should not invoke the setSize() method directly
why isn't my overridden setSize called?
You did not override the setSize() method. When you execute your code you invoked the setSize() method. Then later the layout manager is invoked when the frame is packed() and the layout manager will invoke the setSize() method on the panel.

You cannot resize a component in a BorderLayout if it's constraint is BorderLayout.CENTER.
The component takes automatically all the available space.
More informations : http://docs.oracle.com/javase/tutorial/uiswing/layout/border.html

Related

JPanel wont appear until being resized [duplicate]

I subclass JPanel to overwrite paintComponent(Graphics), I want to draw an image onto jpanel in a jframe.
But my image hasn't shown up until I make a change to jframe's size.
This is my code:
public class ImagePanel extends JPanel{
public void setImage(BufferedImage bi)
{
image = bi;
revalidate();
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(image != null)
{
g.drawImage(image, 0, 0, this);
}
}
}
Verify that you invoke setVisible() after adding components and calling pack(), as discussed in this related example. You may also need to adopt an appropriate layout. Invoking repaint(), as suggested here, may fix the symptom but not the underlying cause.
Take a look at the docs for JPanel.add(), which it inherits from java.awt.Container:
Appends the specified component to the end of this container. This is a convenience method for addImpl(java.awt.Component, java.lang.Object, int).
This method changes layout-related information, and therefore, invalidates the component hierarchy. If the container has already been displayed, the hierarchy must be validated thereafter in order to display the added component.
Emphasis added.
Therefore, if you modify a Container after it's already been displayed, you must call validate() in order for it to show up. Just invoking repaint() is not enough. You may have noticed that calling setVisible(true) also works; this is because it calls validate() internally.
If you want to "refresh" the JPanel then you should call repaint(), which will call your paintComponent(). This should fix your problem:
public void setImage(BufferedImage bi)
{
image = bi;
EventQueue.invokeLater(new Runnable()
{
public void run()
{
repaint();
}
});
}
Its good practice to update and change the GUI using the EDT. Heres more info on the EDT if you're interested:
How does the event dispatch thread work?
repaint doesn't need to be called from the EDT. If you're changing the GUI, such as setting text to a JLabel, it should be inside of the EDT. Heres more information on what can be called outside of the EDT (courtesy of nIcE cOw):
Safe to use Component.repaint() outside EDT?
I had the same problem and fixed it by call setVisible(true); the JFrame I was using.
Example : if your JFrame does not update after using :
jframe.setContentPane(new MyContentPane());
fix it with :
jframe.setContentPane(new MyContentPane());
jframe.setVisible(true);
I know that it sounds silly to do this even though your JFrame is already visible, but that's the only way I've found so far to fix this problem (the solution proposed above didn't work for me).
Here is a complete example. Run it and then uncomment the "f.setVisible(true);" instructions in classes Panel1 and Panel2 and you'll see the difference. Don't forget the imports (Ctrl + Shift + O for automatic imports).
Main class :
public class Main {
private static JFrame f;
public static void main(String[] args) {
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(new Panel1(f));
f.pack();
f.setVisible(true);
}
}
Panel1 class :
public class Panel1 extends JPanel{
private JFrame f;
public Panel1(JFrame frame) {
f = frame;
this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
JButton b = new JButton("Panel 1");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
f.setContentPane(new Panel2(f));
// Uncomment the instruction below to fix GUI "update-on-resize-only" problem
//f.setVisible(true);
}
});
add(b);
}
}
Panel2 class :
public class Panel2 extends JPanel{
private JFrame f;
public Panel2(JFrame frame) {
f = frame;
this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
JButton b = new JButton("Panel 2");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
f.setContentPane(new Panel1(f));
// Uncomment the instruction below to fix GUI "update-on-resize-only" problem
//f.setVisible(true);
}
});
add(b);
}
}
Hope that helps.
Regards.
I also had same problem but I found a solution. Just create a jframe object on top and call jframe methods at the bottom like jf.pack(), jf.setVisible(), jf.setSize(), jf.setDefaultCloseOpetion() should be at the bottom of the all UIs added in that frame you will find it work great.

Change content pane of JFrame and new component not displaying

I'm making a game and I'm trying to switch between different JPanels to display the menu, world etc.
public static void setComponent(Component component){
frame.getContentPane().removeAll();
frame.getContentPane().add(component, 0);
}
This is the method to change the component of the JFrame frame, this works for the first time with whichever JPanel component I give it, but when I try to change it from inside any class from a keyListener method say, the content pane doesn't display anything.
#Override
public void mousePressed(MouseEvent arg0) {
Frame.setComponent(new MenuPanel());
}
Method to create the JFrame and the thread that repaints the components
public static void createFrame(){
frame = new JFrame();
frame.setSize(size);
frame.setTitle(title);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setLayout(new GridLayout(1,1,0,0));
new Thread(){
public void run(){
while(true){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(frame.getComponentCount() != 0){
//System.out.println("?");
frame.getContentPane().repaint();
System.out.println(frame.getContentPane().getComponentAt(1, 1));
}
}
}
}.start();
setComponent(new MenuPanel());
frame.setVisible(true);
}
Getting this result from the Output Stream when first loading
Menu.MenuPanel[,0,0,794x571,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]
Then after using the setComponent method from another class
javax.swing.JPanel[null.contentPane,0,0,794x571,invalid,layout=java.awt.GridLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=9,maximumSize=,minimumSize=,preferredSize=]
When you call your setComponent method the first time, you do that before you call frame.setVisible(true) that forces the frame to validate all its components and show them.
When you change the components later you need to revalidate your frame manually so it gets aware of its components. Add these lines to your setComponent method and it should work.
public static void setComponent(Component component){
frame.getContentPane().removeAll();
frame.getContentPane().add(component, 0);
frame.revalidate(); // revalidate all the frame components
frame.repaint(); // and repaint the frame
}

JPanel doesn't update until resize Jframe

I subclass JPanel to overwrite paintComponent(Graphics), I want to draw an image onto jpanel in a jframe.
But my image hasn't shown up until I make a change to jframe's size.
This is my code:
public class ImagePanel extends JPanel{
public void setImage(BufferedImage bi)
{
image = bi;
revalidate();
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(image != null)
{
g.drawImage(image, 0, 0, this);
}
}
}
Verify that you invoke setVisible() after adding components and calling pack(), as discussed in this related example. You may also need to adopt an appropriate layout. Invoking repaint(), as suggested here, may fix the symptom but not the underlying cause.
Take a look at the docs for JPanel.add(), which it inherits from java.awt.Container:
Appends the specified component to the end of this container. This is a convenience method for addImpl(java.awt.Component, java.lang.Object, int).
This method changes layout-related information, and therefore, invalidates the component hierarchy. If the container has already been displayed, the hierarchy must be validated thereafter in order to display the added component.
Emphasis added.
Therefore, if you modify a Container after it's already been displayed, you must call validate() in order for it to show up. Just invoking repaint() is not enough. You may have noticed that calling setVisible(true) also works; this is because it calls validate() internally.
If you want to "refresh" the JPanel then you should call repaint(), which will call your paintComponent(). This should fix your problem:
public void setImage(BufferedImage bi)
{
image = bi;
EventQueue.invokeLater(new Runnable()
{
public void run()
{
repaint();
}
});
}
Its good practice to update and change the GUI using the EDT. Heres more info on the EDT if you're interested:
How does the event dispatch thread work?
repaint doesn't need to be called from the EDT. If you're changing the GUI, such as setting text to a JLabel, it should be inside of the EDT. Heres more information on what can be called outside of the EDT (courtesy of nIcE cOw):
Safe to use Component.repaint() outside EDT?
I had the same problem and fixed it by call setVisible(true); the JFrame I was using.
Example : if your JFrame does not update after using :
jframe.setContentPane(new MyContentPane());
fix it with :
jframe.setContentPane(new MyContentPane());
jframe.setVisible(true);
I know that it sounds silly to do this even though your JFrame is already visible, but that's the only way I've found so far to fix this problem (the solution proposed above didn't work for me).
Here is a complete example. Run it and then uncomment the "f.setVisible(true);" instructions in classes Panel1 and Panel2 and you'll see the difference. Don't forget the imports (Ctrl + Shift + O for automatic imports).
Main class :
public class Main {
private static JFrame f;
public static void main(String[] args) {
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(new Panel1(f));
f.pack();
f.setVisible(true);
}
}
Panel1 class :
public class Panel1 extends JPanel{
private JFrame f;
public Panel1(JFrame frame) {
f = frame;
this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
JButton b = new JButton("Panel 1");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
f.setContentPane(new Panel2(f));
// Uncomment the instruction below to fix GUI "update-on-resize-only" problem
//f.setVisible(true);
}
});
add(b);
}
}
Panel2 class :
public class Panel2 extends JPanel{
private JFrame f;
public Panel2(JFrame frame) {
f = frame;
this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
JButton b = new JButton("Panel 2");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
f.setContentPane(new Panel1(f));
// Uncomment the instruction below to fix GUI "update-on-resize-only" problem
//f.setVisible(true);
}
});
add(b);
}
}
Hope that helps.
Regards.
I also had same problem but I found a solution. Just create a jframe object on top and call jframe methods at the bottom like jf.pack(), jf.setVisible(), jf.setSize(), jf.setDefaultCloseOpetion() should be at the bottom of the all UIs added in that frame you will find it work great.

Dynamically Resize a JScrollPane?

I Have two files. One extends JFrame, and another Extends JPanel.
Whenever I change the size of the frame, whether it be maximizing, dragging, whatever, i want the ScrollPane to fit itself to the current size of the frame.
There's more to it, there's a top menubar and a bottom bar as well, but i left those out for simplicity.
Essentially, i want it to work like notepad.
right now, I use a ComponentListener on the frame that calls a setSize method in the the other class.
The setSize method is just:
public void resize(int x, int y)
{
textA.setPreferredSize(new Dimension(x, y-50));
areaScrollPane.setPreferredSize(new Dimension(x,y-50));
}
also, for reference:
public void componentResized(ComponentEvent e)
{
textA.resize(panel.getWidth(),panel.getHeight());
}
FYI, it extends JPanel because of the way I add it to the frame:
panel = (JPanel) this.getContentPane();
panel.setLayout(new BorderLayout());
panel.add(textA, BorderLayout.CENTER);
so what's the best way to do this?
Thanks!
Edit: Here's the scrollpane file. It's called textA in my main.
public class TextArea extends JPanel
{
JTextArea textA=new JTextArea(500,500);
JScrollPane areaScrollPane = new JScrollPane(textA);
Toolkit toolkit = Toolkit.getDefaultToolkit ();
Dimension dim = toolkit.getScreenSize();
Dimension dim2=(new Dimension((int)(dim.getWidth()),(int)(dim.getHeight()-120)));
public TextArea()
{
//textA.setLineWrap(true);
//textA.setWrapStyleWord(true);
textA.setEditable(true);
textA.setForeground(Color.WHITE);
textA.setBackground(Color.DARK_GRAY);
this.setFont(null);
areaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
areaScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
areaScrollPane.setMinimumSize(new Dimension(300,300));
areaScrollPane.setSize(new Dimension(800,800));
textA.setPreferredSize(dim2);
areaScrollPane.setPreferredSize(dim2);
areaScrollPane.setMaximumSize(dim2);
add(areaScrollPane);
}
#Override
public void resize(int x, int y)
{
textA.setPreferredSize(new Dimension(x, y-50));
areaScrollPane.setPreferredSize(new Dimension(x,y-50));
}
}
and the main:
public class JEdit extends JFrame implements ComponentListener
{
TextArea textA=new TextArea();
JPanel panel;
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
JEdit run=new JEdit();
}
public JEdit()
{
setTitle("JEdit");
setLayout(new BorderLayout());
setSize(1100, 1000);
this.setMinimumSize(new Dimension(100,100));
//setBackground(Color.BLACK);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
System.out.println("error1");
} catch (InstantiationException ex) {
System.out.println("error2");
} catch (IllegalAccessException ex) {
System.out.println("error3");
} catch (UnsupportedLookAndFeelException ex) {
System.out.println("error4");
}
panel = (JPanel) this.getContentPane();
panel.setLayout(new BorderLayout());
//TopBar top=new TopBar();
// PositionBar posB=new PositionBar();
panel.add(textA, BorderLayout.CENTER);
// add(top,BorderLayout.NORTH);
// add(posB,BorderLayout.SOUTH);
addComponentListener(this);
setVisible(true);
}
public void componentResized(ComponentEvent e)
{
textA.resize(panel.getWidth(),panel.getHeight());
}
public void componentMoved(ComponentEvent e) {
textA.resize(panel.getWidth(),panel.getHeight());
}
public void componentShown(ComponentEvent e) {
textA.resize(panel.getWidth(),panel.getHeight());
}
public void componentHidden(ComponentEvent e) {
textA.resize(panel.getWidth(),panel.getHeight());
}
}
Regarding the code you've posted, for one get rid of all calls to setSize -- these are generally not honored when using layout managers and get rid of all of your ComponentListener stuff as it's superfluous since you are using layout managers to resize things. The biggest problem I see though is that your allow your TextArea JPanel to use its default layout, which is FlowLayout, and doing so will prevent the JScrollPane that it holds from resizing. Give this class a BorderLayout (or better simply return a JScrollPane from the class), and you're set. e.g. with quick modifications and with renaming of classes to prevent clashes with the standard Java classes,
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class JEdit2 extends JFrame {
TextArea2 textA = new TextArea2();
JPanel panel;
public static void main(String[] args) {
new JEdit2();
}
public JEdit2() {
setTitle("JEdit 2");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = (JPanel) this.getContentPane();
panel.add(textA, BorderLayout.CENTER);
pack(); //!! added
setLocationRelativeTo(null);
setVisible(true);
}
}
#SuppressWarnings("serial")
class TextArea2 extends JPanel {
JTextArea textA = new JTextArea(500, 500); // !! this is one friggin' huge JTextArea!
JScrollPane areaScrollPane = new JScrollPane(textA);
public TextArea2() {
textA.setEditable(true);
textA.setForeground(Color.WHITE);
textA.setBackground(Color.DARK_GRAY);
this.setFont(null);
setLayout(new BorderLayout()); //!! added
add(areaScrollPane, BorderLayout.CENTER);
}
}
By default a JFrame uses a BorderLayout (so there is no need to reset it). All you need to do is add the JScrollPane to the CENTER of the BorderLayout and it will resize automatically.
And the basic code would be:
JTextArea textArea = new JTextArea(...);
JScrollPane scrollPane = new JScrollPane();
frame.add(scrollPane, BorderLayout.CENTER);
I'm not sure why you are adding the text area to the center.
There is no need to use setPreferredSize() on any component or use a listener on any component.
If you need more help then you need to post a SSCCE.

Why/When ComponentListener.componentShown() get invoked?

Why this code never prints "Hello2" ?
public class Test4 {
public static void main(String[] args) {
JFrame f = new JFrame();
JPanel p = new JPanel();
f.getContentPane().add(p);
JLabel x = new JLabel("Hello");
p.add(x);
p.addComponentListener(new ComponentListener() {
public void componentResized(ComponentEvent arg0) {
System.err.println("Hello1");
}
public void componentMoved(ComponentEvent arg0) {
}
public void componentShown(ComponentEvent arg0) {
System.err.println("Hello2");
}
public void componentHidden(ComponentEvent arg0) {
}
});
f.setVisible(true);
f.pack();
}
}
I would guess that it's called when the visibility state of the actual object changes.
in this case, you change the visibility of the Frame, not of the Panel.
(by default, Frame starts hidden, but panels are visible)
try to add the listener to the frame.
AWT's definition of "visible" may be a bit counter-intuitive. From the Javadoc of java.awt.Component#isVisible:
"Components are initially visible, with the exception of top level components such as
Frame objects."
According to this description, p is already visible before you add the ComponentListener. In fact, you can verify this if you insert a
System.out.println(p.getVisible());
anywhere before you call f.setVisible(true). In that sense, the visibility is not changed when displaying the frame and hence componentShown(..) is not called.
From Java Tutorials
The component-hidden and
component-shown events occur only as
the result of calls to a Component 's
setVisible method. For example, a
window might be miniaturized into an
icon (iconified) without a
component-hidden event being fired.

Categories

Resources