Problem - Container changing size automatically in Java - java

I've found that my container is actually changing it's size a short while after being constructed
When it's constructed, I set my components to be at the place I want (like 30px away from the right edge) but later after a short while, I find that it turns from 1008x730 to 1018x740...
(My JFrame is 1024x768)
Does anyone know why this happens and how I can stop this auto-resizing thing?
Thank you.
I just did a -
while (true) {
System.out.println(c.getSize());
}
And the size changed after a few iterations.
~Edited

It sounds like you're doing something that changes a component's size and revalidates after calling pack() on the JFrame. Also, rather than calling setSize(), it's often better to set a component's preferred size and let the LayoutManager arrange things.
Addendum: It's generally better to invoke pack() on a top-level container in order to get the initial sizes correct. Also, the content pane of a JFrame defaults to BorderLayout, which may not be what you want.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class MyPanel extends JPanel {
public MyPanel() {
this.setLayout(new GridLayout(2, 2));
this.setPreferredSize(new Dimension(320, 240));
for (int i = 0; i < 4; i++) {
JLabel label = new JLabel(String.valueOf(i));
label.setHorizontalAlignment(JLabel.CENTER);
label.setBorder(BorderFactory.createLineBorder(Color.blue));
this.add(label);
}
}
private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
}

If you can run this from your IDE or attach remotely with the debugger it would be pretty easy to just set a breakpoint on the methods that set the size.
Or alternately, you could subclass JFrame with your own class and similarly override those methods and do
try { throw new Exception("here!"); } catch(Exception e) { e.printStackTrace(); }
That would tell you what is causing the change in size.

Sounds like the layout manager kicking in. Try running with the Swing Explorer to see what it thinks of the world.

Related

JButton and JTextField

What's wrong? ImageIcon and the frame's size are working properly.
But the JTextField and the JButton aren't.
I need the solution.
import javax.swing.*;
import javax.swing.ImageIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Frame {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("Alkalmazás");
frame.setVisible(true);
frame.setSize(500,500);
frame.setResizable(false);
JTextField field = new JTextField();
field.setBounds(40,250, 300,35);
JButton button = new JButton(new ImageIcon("table.png"));
button.setBounds(40,400, 250,25);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
tf.setText(""something);
}
});
frame.add(field);
frame.add(button);
}
}
You didn't mention what's "not working properly", but there are a few errors with your code:
Don't call your class Frame, it may confuse you or others about java.awt.Frame, something that may work would be MyFrame
Right now all your class is inside the main method and it's not placed inside the Event Dispatch Thread (EDT), to fix this, create an instance of your class and call a method createAndShowGUI (or whatever you want to name it) inside SwingUtilities.invokeLater()
For Example:
public static void main(String args[]) {
SwingUtilities.invokeLater(new MyFrame()::createAndShowGUI)
}
Or if using Java 7 or lower, use the code inside this answer in point #2.
setVisible(true) should be the last line in your code, otherwise you may find some visual glitches that may be resolved until you move your mouse above your window or something that triggers the call to repaint() of your components.
Instead of calling setSize(...) directly, you should override getPreferredSize(...) of your JPanel and then call pack() on your JFrame, see this question and the answers in it: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
You're adding 2 components to the CENTER of BorderLayout, which is a JFrame's default layout manager, there are other layout managers and you can combine them to make complex GUI's.
setBounds(...) might mean that you're using null-layout, which might seem like the easiest way to create complex layouts, however you will find yourself in situations like this one if you take that approach, it's better to let Swing do the calculations for you while you use layout managers. For more, read: Why is it frowned upon to use a null layout in Swing?
With all the above tips now in mind, you may have a code similar to this one:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class MyFrame {
private JFrame frame;
private JPanel pane;
private JTextField field;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MyFrame()::createAndShowGUI);
}
private void createAndShowGUI() {
frame = new JFrame("Alkalmazás");
pane = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
};
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
field = new JTextField(10);
button = new JButton("Click me");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
field.setText("something");
}
});
pane.add(field);
pane.add(button);
frame.add(pane);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Now you have an output similar to this one:
What about you want the JTextField to have a more "normal" size? Like this one:
You'll have to embed field inside another JPanel (with FlowLayout (the default layout manager of JPanel)), and then add that second JPanel to pane, I'm not writing the code for that as I'm leaving that as an exercise to you so you learn how to use multiple layout managers

Add Textfields on Jframe at Runtime

I am trying to create text-fields on frame by getting input at run-time. Is it possible? Or I have to create another frame for that. I tried this code, but it's not working. Please Help me out, and tell me what's wrong with this code.
import java.awt.BorderLayout;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Check extends JFrame implements ActionListener
{
JTextField txtqty;
JTextField[] tfArr;
JPanel p1,p2;
JButton bsmbt;
public Check()
{
GUIDesign();
}
public void GUIDesign()
{
p1 = new JPanel();
txtqty = new JTextField(10);
JButton bsmbt= new JButton("OK");
p1.add(txtqty);
p1.add(bsmbt);
p2=new JPanel();
p2.setLayout(null);
add(p1,BorderLayout.NORTH);
setSize(500, 500);
setVisible(true);
setLocation(100, 100);
bsmbt.addActionListener(this);
}
public static void main(String[] args)
{
new Check();
}
public void TFArray(JTextField[] temp)
{
int x,y,width,height;
x=10;y=30;width=50;height=20;
int no_of_textboxes = Integer.parseInt(txtqty.getText());
temp=new JTextField[no_of_textboxes];
for(int i=0;i<no_of_textboxes;i++)
{
temp[i]= new JTextField(10);
temp[i].setBounds(x, y, width, height);
x+=(width+10);
p2.add(temp[i]);
}
add(p2);
}
#Override
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(this, txtqty.getText());
TFArray(tfArr);
}
}
->Method TFArray() isn't working.
You have many errors in your code:
public void TFArray(JTextField[] temp): method names should start with lowerCamelCase
You're extending JFrame, you shouldn't extend JFrame, because when you extend it your class is a JFrame, JFrame is rigid so you can't place it inside anything else, instead you might consider creating a JFrame instance and if you ever need to extend JComponent extend from JPanel.
JButton bsmbt= new JButton("OK"); the variable bsmbt is a local variable inside your constructor, your global variable bsmbt is not used anywhere, and if you try to use it later you'll get a NullPointerException, instead change that line to:
bsmbt= new JButton("OK");
You're using null layout for p2, instead use a proper Layout manager and read Null layout is evil and Why is it frowned upon to use a null layout in swing?. Swing was designed to work with different PLAFs, screen sizes and resolutions, while pixel perfect GUIs (with setBounds()) might seem like the best and faster way to create a complex GUI in Swing, the more GUIs you make, the more errors you'll get due to this.
To solve your problem call revalidate() and repaint()
The above code creates 2 textfields. but when I again put some value and submit it, it doesn't seem to reflect any changes.
That might be because you're overriding x, y, height and width variables each time you enter TFArray method. But that is a guess, if you want a real answer, follow the suggestions above and post a proper and valid Minimal, Complete, and Verifiable example

Java Swing: problems with width

I have problems with understanding the behavior of my application. I want to create a simple window (1000x700px), divided into two parts (250px and 750px width respectively). I tried the following code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example extends JFrame
{
private static final long serialVersionUID = 1L;
public Example()
{
this.setSize(1000, 700);
this.setTitle("Example");
this.setResizable(false);
this.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
JPanel navigation_panel_wrap = new JPanel();
JPanel content_panel_wrap = new JPanel();
navigation_panel_wrap.setPreferredSize(new Dimension(250, 700));
content_panel_wrap.setPreferredSize(new Dimension(750, 700));
content_panel_wrap.setBackground(Color.green);
navigation_panel_wrap.setBackground(Color.red);
this.getContentPane().add(navigation_panel_wrap);
this.getContentPane().add(content_panel_wrap);
}
public static void main(String[] args)
{
Example example = new Example();
example.setVisible(true);
}
}
As you can see I manually set layout manager for JFrame (FlowLayout instead of BorderLayout with zero horizontal and vertical gaps). Of course, I can just use BorderLayout and than use add() method with BorderLayout.EAST and BorderLayout.WEST parameters, but I want to understand what's wrong with FlowLayout.
When I run my application, I get the following (no green JPanel):
If I decrease width of, for example, content_panel_wrap and make it 744px instead of 750px, everything works correctly.
So the question is - what are these strange 6 pixels? I'm not sure this value is constant for all operating systems, so I want to understand its origin.
There's nothing wrong with FlowLayout but you will need to call pack() for all components to be sized.
As for your codes problem (+1 to #Reimeus) calling pack() is the solution.
as per docs:
Causes this Window to be sized to fit the preferred size and layouts
of its subcomponents. If the window and/or its owner are not yet
displayable, both are made displayable before calculating the
preferred size. The Window will be validated after the preferredSize
is calculated.
Tips:
Dont extend JFrame unnecessarily.
Use Event Dispatch Thread when creating and changing UI components:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// create UI components etc here
}
});
Dont call setPreferredSize() rather override getPrefferedSize() of component.
Dont call setSize(...) on JFrame rather call JFrame#pack() before setting it visible.
Dont forget to call JFrame#defaultCloseOperation(..) or your initial/EDT thread will not be terminated when JFrame is closed.
Here is an example combining my advice and your code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Example {
private final JFrame frame;
public Example() {
frame = new JFrame();
frame.setTitle("Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//app exited when frame closes
frame.setResizable(false);
frame.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
JPanel navigation_panel_wrap = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 700);
}
};
JPanel content_panel_wrap = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(750, 700);
}
};
content_panel_wrap.setBackground(Color.green);
navigation_panel_wrap.setBackground(Color.red);
frame.add(navigation_panel_wrap);
frame.add(content_panel_wrap);
//pack frame (size components to preferred size)
frame.pack();
frame.setVisible(true);//make frame visible
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Example();
}
});
}
}

AWT Scrollpane scrollbars flash on resize

I am having issues with the java.awt.ScrollPane class's SCROLLBARS_AS_NEEDED display policy handling component resize. Ideally, if I have a ScrollPane that contains a Component that is significantly smaller than the Scrollpane and I shrink the Scrollpane to a size that is still greater than the child component, no scrollbars will appear. However, in practice both scrollbars seem to flicker while the resize operation is occurring and may even persist until the next redraw after the operation completes. Here is a small example to demonstrate what I mean:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.ScrollPane;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Tester implements Runnable{
/**
* #param args
*/
public static void main(String[] args) {
System.setProperty("sun.awt.noerasebackground", "true");
Tester t = new Tester();
SwingUtilities.invokeLater(t);
}
#SuppressWarnings("serial")
#Override
public void run() {
JFrame frame = new JFrame("Tooltip tester");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 800);
Canvas c = new Canvas(){
#Override
public void paint(Graphics g){
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
};
c.setPreferredSize(new Dimension(400, 400));
ScrollPane s = new ScrollPane();
s.add(c);
frame.add(s);
frame.setVisible(true);
}
}
Shrinking the window in the application above should cause scrollbars to flash. I believe this is caused by a bug in the implementation of the peer of the ScrollPane. (Source Here) I've copied the method that I believe has the error (line 145 in the link).
Dimension getChildSize() {
ScrollPane sp = (ScrollPane)target;
if (sp.countComponents() > 0) {
Component c = sp.getComponent(0);
return c.size();
} else {
return new Dimension(0, 0);
}
}
To me, it seems like the getChildSize() method should call c.getPreferredSize() rather than c.size(). If the ScrollPane child's size is greater than its current preferred size, it should be able to shrink without scroll bars showing up (in my opinion). To test this theory, I overrode the size() method in my example from above:
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.ScrollPane;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Tester implements Runnable{
/**
* #param args
*/
public static void main(String[] args) {
System.setProperty("sun.awt.noerasebackground", "true");
Tester t = new Tester();
SwingUtilities.invokeLater(t);
}
#SuppressWarnings("serial")
#Override
public void run() {
JFrame frame = new JFrame("Tooltip tester");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 800);
Canvas c = new Canvas(){
private Dimension prefSize = new Dimension();
#Override
public void paint(Graphics g){
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
#Override
public void setPreferredSize(Dimension preferredSize) {
prefSize = preferredSize;
super.setPreferredSize(preferredSize);
}
#Override
public Dimension size() {
return prefSize;
}
};
c.setPreferredSize(new Dimension(400, 400));
ScrollPane s = new ScrollPane();
s.add(c);
frame.add(s);
frame.setVisible(true);
}
}
This code behaves exactly how I want it to. However, overriding the size() method to do something other than what the documentation says it should do isn't a particularly elegant way to fix this problem. I feel like I must be doing something wrong here. Is it possible that a fairly basic functionality of the ScrollPane peer for X11 is broken?
A few other related notes:
-Use of java.awt.Canvas is non-negotiable for my purposes. In my actual application, I am drawing something very quickly using Java AWT Native Interface.
-While I read the documentation about new improvements to light/heavyweight compatibility in Java 7 and later versions of Java 6, I didn't really see an improvement in either case so I decided to stick with awt.ScrollPane over swing.JScrollPane. Either way, a ScrollPane should work correctly in the simple case I showed above.
hard to help you without SSCCE, for future readers
However, in practice both scrollbars seem to flicker while the resize operation is occurring and may even persist until the next redraw after the operation completes
used LCD/LED panel caused those flickering (including MsExcell e.g., btw many times discused),
same flickering is for Swing JFrame with JScrollPane
not presented on CRT or Plasma display
if you playing FullHD video on PC, then every good multimedia players waiting until resize ended, thenafter fill available space into its container
you have to add ComponentListener, put there Swing Timer with small delay (KMPlayer show me 350-500milisecs), untill resize continue call for Timer#restart,
Canvas is good workaround for CAD / CAM, OpenGL(CL), good Java video players are based on AWT
you have to avoiding mixing Swing Container with AWT contents, lets everything is based on AWT (undecorated container)

automatically causing a subclassed JPanel's resources to be released

Suppose I subclass JPanel, and my subclass uses a lot of memory.
What is the right way for me to design this class so that the memory resources are freed when my JPanel is used as a component in a larger system?
Seems like there's a few options:
subclass finalize() (red flags all over the place -- the literature I've read says you shouldn't get into the finalizing business)
add an explicit dispose() or destroy() or whatever, for consumers of my class to use
add some kind of listener to my JPanel that gets notified when the parent gets disposed
override some method of JPanel which will automatically get called when their parent window gets disposed
In the example below I used the finalize() option, which works but only when garbage collection gets called, and there are instances where I'd rather the cleanup happens when the JPanel is no longer needed.
Option #2 is nice, but then I have to rely on consumers to call this method, and the Swing philosophy seems to be just stick components into a window, and let all the components get destroyed when the window gets closed. So if my JPanel is inside a JTable in a JScrollPane in a JPanel in a JFrame, chances are, the consumer of my class isn't going to call my dispose() or destroy() method.
Options #3 or #4 would be my choice but I can't find anything that seems to apply here.
Any suggestions?
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class JPanelMemoryHog extends JPanel
{
final private String title;
final private Object space;
public JPanelMemoryHog(String title)
{
super();
this.title = title;
this.space = new byte[5*1000*1000];
setBorder(BorderFactory.createTitledBorder(title));
setPreferredSize(new Dimension(300,200));
}
#Override protected void finalize()
{
System.out.println("finalized "+this.title);
}
public static void main(String[] args) {
JFrame frame = new JFrame("example");
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JButton button = new JButton("create frame");
button.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
createFrame();
}
});
panel.add(button, BorderLayout.CENTER);
frame.setContentPane(panel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static int panelcount = 0;
protected static void createFrame() {
final String title = "panel"+(++panelcount);
final JFrame frame = new JFrame(title);
frame.setContentPane(new JPanelMemoryHog(title));
frame.pack();
frame.addWindowListener(new WindowAdapter() {
#Override public void windowClosing(WindowEvent e) {
System.out.println("closing "+title);
frame.dispose();
}
});
frame.setVisible(true);
}
}
The best solution is to add a HierarchyListener to the JPanel and then check for HierarchyEvent.DISPLAYABILITY_CHANGED (which get triggered when the parent dialog is shown and disposed) and then check for isDisplayable() to false which is the condition set when it is in the process of disposing.
public void hierarchyChanged(HierarchyEvent e) {
//check for Hierarchy event
if(e.getChangeFlags() == HierarchyEvent.DISPLAYABILITY_CHANGED)
{
//do the required action upon close
if(!this.isDisplayable())
Notifier.removeListener(this);
}
}
Like you mentioned, don't call finalize(), this is just dangerous and hard to get right.
I've always added dispose methods that take care of listener cleanup and removing any memory intensive resources. I think the trick to doing this properly, is there should be a manager of some sort that handles destroying such objects.
If you're talking about more generic components that would be used in a more general framework this might not be the correct solution

Categories