Widgets behaving strangely when using "setlayout(null)" - java

I'm doing the following call in my code:
...
setLayout(null);
...
I'm trying to place a button and a textfield by specifying their x and y coordinates.
The problem when I run the program (either with Eclipse or BlueJ) is that I need to run on the panel up to the position of the button and the textfield in order to see respectively the button and the textfield.
When I find the textfield, it is small. Only when I start writing it assumes the size I specified.
Does anyone know how to solve it?

Avoid setLayout (null), unless you have a very good reason for it. You can learn about layout managers here: http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html
If you still want to use a null layout, you have to set the width and height of the component, not just its x and y position (see the setSize method).
From the link mentioned above:
Although we strongly recommend that you use layout managers, you can
perform layout without them. By setting a container's layout property
to null, you make the container use no layout manager. With this
strategy, called absolute positioning, you must specify the size and
position of every component within that container. One drawback of
absolute positioning is that it does not adjust well when the
top-level container is resized. It also does not adjust well to
differences between users and systems, such as different font sizes
and locales.

I'd recommend using the setBounds method instead of the setLocation
JTextField tf = new JTextField(10);
Dimension d = tf.getPreferredSize();
tf.setBounds(x, y, d.width, d.height);
Of course, if you're using a null Layout manager, you also need to take care of your preferredSize. Here's an example that incorporates all the major aspects:
import java.awt.*;
import javax.swing.*;
public class TestProject extends JPanel{
public TestProject(){
super(null);
JTextField tf = new JTextField(10);
add(tf);
Dimension d = tf.getPreferredSize();
tf.setBounds(10, 20, d.width, d.height);
}
#Override
public Dimension getPreferredSize(){
//Hard coded preferred size - but you'd probably want
//to calculate it based on the panel's content
return new Dimension(500, 300);
}
public static void main(String args[])
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setContentPane(new TestProject());
frame.pack();
frame.setVisible(true);
}
});
}
}

Related

Java JComponents being cut off near edge of JFrame

I have recently begun working with JComponents to create GUI systems. Everything is working but the bottom and right sides of the JFrame do not get painted over and remain white.
Screenshot of running GUI:
In the screenshot you can see the 'drknBtn' is displayed correctly; this is because I hovered over it with the mouse before taking the picture. Hovering over the buttons refreshes them and they appear as normal. Due to this, I would assume the panel that holds them, 'bottomPnl' is covering that white space, but that panels background is not showing at the bottom portion. Any ideas on what could cause this? I have tried calling 'bottomPnl.repaint()' directly before calling pack(), but no change.
My code is below.
Note: For each JComponent, I created a class extending that component. This way I could set default values for the components in the constructors of these classes instead of doing each one individually. I'll list the relevant properties of the Frame and Panels.
Frame: setSize(width,height); setResizeable(false); setLocationRelativeTo(null);
Panel: setLayoutManager(from contructor); setPreferredSize(new Dimension(width,height)); same for setMinimumSize and setMaximumSize.
public Display(String title, int w, int h){
width=w;
height=h;
frame = new FrameUI(title,w,h);
//parent panel
parentPnl= new PanelUI(width,height, new FlowLayout(FlowLayout.CENTER,0,0));
parentPnl.setBackground(new Color(100,175,175));
//top panel
topPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
topPnl.setBackground(new Color(100,175,175));
chooseFileBtn = new ButtonUI("Browse...",topPnl.getWidth()/4,(int)(topPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
fc = new FileChooserUI();
fc.setFileFilter(new FileNameExtensionFilter("Image files", ImageIO.getReaderFileSuffixes()));
int result = fc.showOpenDialog(null);
try {
if (result == JFileChooser.APPROVE_OPTION) {
picture.setIcon(new ImageIcon(ImageIO.read(fc.getSelectedFile()).getScaledInstance(picture.getWidth(),picture.getHeight(), 0)));
}
} catch (Exception iOException) {
}
}
});
//middle panel
midPnl= new PanelUI((int)(width*.85),(int)(height*.7), new FlowLayout(FlowLayout.CENTER,0,0));
midPnl.setBackground(new Color(75,125,125));
picture = new LabelUI("",midPnl.getWidth(),midPnl.getHeight());
picture.setBackground(new Color(75,125,125));
picture.setVisible(true);
picture.setOpaque(true);
picture.setIcon(null);
//bottom panel
bottomPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
bottomPnl.setBackground(new Color(100,175,175));
ltnBtn = new ButtonUI("Lighten Picture",bottomPnl.getWidth()/3,(int)(bottomPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
}
});
ltnBtn.setBackground(Color.LIGHT_GRAY);
ltnBtn.setForeground(Color.BLACK);
drknBtn = new ButtonUI("Darken Picture",bottomPnl.getWidth()/3,(int)(bottomPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
}
});
drknBtn.setBackground(Color.DARK_GRAY);
drknBtn.setForeground(Color.WHITE);
//add UI Objects
topPnl.add(chooseFileBtn);
midPnl.add(picture);
bottomPnl.add(ltnBtn);
bottomPnl.add(drknBtn);
parentPnl.add(topPnl);
parentPnl.add(midPnl);
parentPnl.add(bottomPnl);
Container contentPane = frame.getContentPane();
contentPane.add(parentPnl);
frame.pack();
frame.setVisible(true);
}
}
topPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
looks to me like you are manually trying to control the size of the panels and therefore the size of the components added to your panels. Your calculations are wrong and some components aren't displayed properly. Also all your sizes are fixed at creation time and will not adjust if the size of the frame ever changes.
Don't try to control the sizes manually. Use layout managers to dynamically size components based on the properties of the component.
I fail to see why you would want a button to be 15% of the space available to the frame.
If you want the button to be larger than normal you can set extra empty space around the text of the button by using:
button.setMargin( new Insets(50, 50, 50, 50) );
Then just add the button to a panel using a FlowLayout and let the layout manager do its job.
The default layout for a frame is a BorderLayout, so you can then add the "topPnl" to the frame using:
frame.add(topPnl, BorderLayout.PAGE_START);
The other panels can then be added using:
frame.add(midPnl, BorderLayout.CENTER);
frame.add(bottomPnl, BorderLayout.PAGE_END);
This is how Swing was designed to be used with layout managers.
Read the section from the Swing tutorial on How to Use BorderLayout for more information and examples.
The main point is use methods like setMargin(...), to provide hints to the component on what their preferred size should be.
I fixed the problem by removing the 'setSize()' method in the FrameUI constructor. However, I still do not understand how you could dynamically size panels as you said while still maintaining the proportions I want for them. Thank you #camickr for the pointers, my original problem is fixed. I'll look into more javadocs and tutorials on layout managers and such.

Using Java pack() method

I can't make the pack() method work. I tried several things. My code looks like this at the moment:
Class 1:
public static void main( String[] args )
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
JavaGui mygui = new JavaGui();
// mygui.setSize(1154, 753);
mygui.setVisible(true);
mygui.pack();
Class 2:
public class JavaGui extends javax.swing.JFrame
{
public JavaGui()
{
getContentPane().setLayout(null);
..
getContentPane().add(panelLeft);
...
getContentPane().add(panelRight);
I tried putting the pack method in everywhere, but it's not going to work with this way of adding gui elements. Any suggestions why? I also tried adding everything to a JFrame instead of the getContentPane(), but I can't make that work either.
Don't use null layouts together with pack(). The pack method tells the layout managers and components to size themselves optimally, and if you instead use null layouts, then the gui risks shrinking to a minimal size, since there is no layout to hold it together.
Don't use null layouts at all for the most part. Using these risk your creating rigid GUI's that are almost impossible to extend, improve, debug.
Don't use setSize(...) and pack(). The layouts mostly respect the preferred sizes of components, not their sizes.
Instead:
Use a pleasing and sensible combination of nested JPanels, each using its own layout manager.
Let the components and the layout managers size themselves.
Then pack should help.
The general order that I do is to add all components to the GUI, then call pack(), then setLocationByPlatform(true) (I think), then setVisible(true).
For better help, please check out the Swing Layout Manager Tutorials.
Here are a couple examples to other questions on this site that use various layout managers:
A combination of BorderLayout and GridLayout to create a calculator
BorderLayout and BoxLayout Combination for labels and JTextFields
Using GridBagLayout to create flexible label/textfield grid
I would recommened beginners on building up swing guis to use a good ide with a builtin gui designer like eclipse and windowbuilder or netbeans with matisse. It will help you building up a prototype of your desired gui and gives you an insight how the layouting is done in the source code.
Experiment with the differenet layouts and what is happening when some values are changed.
one does not simply build up a well behaving gui without understanding how the layout works, so doing the recommended tutorials and looking at examples as already posted by Hovercraft Full Of Eels is absolutely necessary.
For your case i just guess what you were up to. Because youre mentioning left and right panels i suggest a JSplitPane which let you divide your screen in two areas which are customizable in size and orientation.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
public class JavaGui extends JFrame {
//SerialVersionId http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
//Calls to Gui Code must happen on the event dispatch thread that the gui does not get stuck
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new JavaGui().setVisible(true);
}
});
}
public JavaGui() {
// Set the desired size of the frame to determine the maximum size of its components
setPreferredSize(new Dimension(1024, 768));
// Set the default close operation, if press x on frame, destroy the frame and exit the application - others are just destroy the frame or just hide the
// frame
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// BorderLayout because we just need a centric gui with one component, here JSplitPane in full size
getContentPane().setLayout(new BorderLayout(0, 0));
// JsplitPane is a bit special as it depends on the divider location between both panels, for the sake of a small example we take the default -1,
JSplitPane splitPane = new JSplitPane();
// 0.5 divides extra space equally to left and right component when resizing the frame - so specifiying a size for the left and right component is not
// necessary
// use the divider location default -1 to let the width of the left component decide where the right component begins, in that case because of the
// resize weight half and half
splitPane.setDividerLocation(-1);
splitPane.setResizeWeight(0.5);
getContentPane().add(splitPane, BorderLayout.CENTER);
// For the panels the same layout as default as the intention is not stated in your question
JPanel leftPanel = new JPanel();
splitPane.setLeftComponent(leftPanel);
leftPanel.setLayout(new BorderLayout(0, 0));
JPanel rightPanel = new JPanel();
splitPane.setRightComponent(rightPanel);
rightPanel.setLayout(new BorderLayout(0, 0));
// Add a button Panel to the south for doing something - flow layout for letting the components flow to the right side
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
// Close Button for closing the frame
JButton btnExit = new JButton("Destroy this frame, but let application run");
btnExit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
buttonPanel.add(btnExit);
// Set every component to its preferred size
pack();
// Make it visible
setVisible(true);
}
}
If you want your JFrame to work with a null layout, rearrange your code so that it looks like this:
public class JavaGui extends javax.swing.JFrame
{
public JavaGui()
{
setMinimumSize(1154, 753); // Make sure you do setMinimumSize() instead of setSize() when using pack() so that the JFrame does not shrink to 0 size
setLayout(null);
add(panelLeft);
add(panelRight);
pack();
}
// Next is main method
Main:
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
new JavaGui().setVisible(true);
// Do not do any formatting for your JFrame here
}
});
Before, you were modifying the JFrame after it was set visible, so that usually does not work, except for pack(). All components and settings for your JFrame should not be in the main method if you are using an anonymous inner class.
You can also use other layouts. Null layouts are for getting pixels in precise locations, which is used for advanced GUI design such as creating a custom GUI, but it seems that you are making a generic GUI with JPanels. For this, I would recommend using a GridBagLayout, which keeps everything centered if the frame is resized and is easy to use. To use a GridBagLayout, you have to replace setLayout(null); with setLayout(new GridBagLayout()); and set GridBagConstraints. Here is some example code of making a panel with a component and a GridBagLayout:
JPanel pane = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
if (shouldFill) {
//natural height, maximum width
c.fill = GridBagConstraints.HORIZONTAL;
}
//For each component to be added to this container:
//...Create the component...
//...Set instance variables in the GridBagConstraints instance...
pane.add(theComponent, c);
// Source: Oracle Docs

How to use setBounds method for a JLabel?

I'm making a window application with Swing. I am using the setBounds() method for the JLabel spacing but it's not working.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FullScreenJFrame extends JFrame
{
public FullScreenJFrame( String title )
{
super(title);
//JFrame frame = new JFrame();
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setUndecorated(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds(0,0,screenSize.width, screenSize.height);
getContentPane()
.add(new JLabel(" HIGHCOURT OF JUDICATURE AT ALLHAHABAD"), BorderLayout.NORTH);
JLabel label = new JLabel("JJ");
label.setBounds(20, 20, 150, 20);
// label.setText(s);
add(label);
}
public static void main( String[] args )
{
FullScreenJFrame frame = new FullScreenJFrame("");
//JFrame frame1 = new JFrame();
//JLabel label = new JLabel("dd");
//label.setBounds(370, 340, 150, 20);
//frame1.add(label);
frame.setVisible(true);
}
}
One advice, It seems you are going to show your application using all screen so try to avoid the use of absolute positions like setBoundMethod.
You should fit your application within a layout that uses other layouts within it. Check this link.
http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
Don't use setBounds() to resize a component. Use a Layout Manager and you won't be worried about manually doing this.
Why?
Why are going for absolute positioning, when Layout Managers do it for you.
One more thing, If your JFrame does not contain any title so there is no need to add empty title as it is bad practice.
Replace
FullScreenJFrame frame = new FullScreenJFrame("");
By
FullScreenJFrame frame = new FullScreenJFrame();
And
public FullScreenJFrame( String title )
By
public FullScreenJFrame()
No need to call
super(title);
Or in order to set your layout as null right click on your form and click set layout then click null layout.
Are null layouts recommended? As other have suggested, the answer is NO.
See Laying out Components in a Container to get some practice in with LayoutManagers
Will I still answer your question? Sure. Just so you know.
"I am using the setBounds() method for the JLabel spacing but it's not working. Please, can any one tell me why it is not working?"
Yes, your bounds aren't working because the JFrame has a default Borderlayout. In order for setBounds to work, the layout needs to be null.
setLayout(null);
Also, keep in mind that when you do use a null layout, any components in which you don't setBounds for will not appear.
See Laying out Components in a Container to get some practice in with LayoutManagers

Java Swing JXTaskPane: how to set bg and border?

I have written the following example code:
import org.jdesktop.swingx.*;
import javax.swing.*;
import java.awt.*;
public class TaskPaneExample{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TaskPaneExample();
}});
}
public TaskPaneExample() {
JFrame frame = new JFrame("TaskPane Example 1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(doInit(), BorderLayout.CENTER);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
private Component doInit() {
JXTaskPaneContainer taskpanecontainer = new JXTaskPaneContainer();
taskpanecontainer.setLayout(new VerticalLayout(2));
final JXTaskPane taskpane1 = new JXTaskPane(){
public void setCollapsed(boolean w){
super.setCollapsed(w);
}};
taskpane1.setTitle("First TaskPane");
JPanel panel1 = new JPanel();
panel1.setBackground(Color.red);
panel1.setSize(100,100);
taskpane1.add(panel1);
taskpanecontainer.add(taskpane1);
JXTaskPane taskpane2 = new JXTaskPane(){
public void setCollapsed(boolean w){
super.setCollapsed(w);
}};
taskpane2.setTitle("My Tasks");
JPanel panel2 = new JPanel();
panel2.setBackground(Color.blue);
panel2.setSize(100,100);
taskpane2.add(panel2);
taskpanecontainer.add(taskpane2);
taskpanecontainer.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
return taskpanecontainer;
}
}
}
What I need are two things:
how do I change the bgcolor of the title? I think it is done with the taskpane2.setUI(..) option but I had no luck working with it.
how to set the border between the JXTaskPane and the Jpanel to zero?
Originally, the JXTaskPane was designed as a kind of "fixed-properties" component - it should look exactly like the corresponding native component (then of WinXP): custom appearance wasn't meant to be supported. As a consequence, the implementation of the title/border is deeply hidden in the XXTaskPaneUI - actually, everything boils down to be a Border.
1) as dogbane already mentioned, some of the properties can be changed on a per-application basis. Just beware: those are implementation details which might change or not be supported by concrete ui implementations (Nimbus is always a good candidate to not respecting them, even our not-really-synth implementation might not, forgot)
2) the "gap" is the border of the contentPane, you can set your own. Again a beware: might not survive an updateUI (could be that the ui delegates override them unconditionally, if so, please file an issue in the SwingX issuetracker)
((JComponent) taskpane2.getContentPane()).setBorder(BorderFactory.createEmptyBorder());
BTW: those panel.setSize have exactly no effect - layoutManagers rule ;-)
To change the bgcolour of the title, you can try setting the start and end background gradient colours in the UIManager:
UIManager.put("TaskPane.titleBackgroundGradientStart", Colors.White.color());
UIManager.put("TaskPane.titleBackgroundGradientEnd", Color.GREEN);
I also found an open swingx jira task for this: SWINGX-731 Support to define the color to JXTaskPane header.

JPanel won't resize

My Code:
public MyConstructor() {
view = new JPanel(new GridLayout(3, 1));
header = new JPanel(new GridLayout(2, 1));//2 ROWS 1 COLUMN
foot = new JLabel("Copyright...");
content = new JPanel();
info = new JLabel("");
logo = new JLabel() {
BufferedImage img;
#Override
public void paint(Graphics g) {
try {
img = ImageIO.read(new File("logo.jpg"));
} catch (IOException e) {
}
g.drawImage(img, 0, 0, null);
}
};
window.add(view);
header.add(logo);
header.add(info);
view.add(header);
view.add(content);
view.add(foot);
window.setLocation(width / 2, 100);
window.setSize(width, height);
window.setPreferredSize(new Dimension(width, height));
content.setSize(window.getWidth(), height-70);
content.setPreferredSize(new Dimension(window.getWidth(), height-70));
}
"window" is the frame...the class is not extending JFrame
My class is going to be a super class to others, the subclasses inherit the public content JPanel. In my super class i'm trying to set the width and height of the 3 sections of my GridLayout, the logo and info components add up to 70 for their height...I've set the other components (view,header,info,logo) private so that subclasses can't access them...
When the application runs a window is shown for the login, this displays and resizes properly. once logged in an instance of one of the subclasses is created, the login window is then hidden site setVisible(false)
when the new window is shown however, the JFrame is the correct size but the header,content and footer are not the currect sizes.
I've tried setting the size and preferred size each of the components but still not working...I've also tried calling repaint and validate/revalidate
Any ideas?
It sounds like your design could be improved. Why do these variables need to be attributes of a superclass? Why not just have a method that constructs the panels that you need and a constructor that adds them so that you get fresh panels for each instance?
In fact, why not just create the header and footer classes and reuse them instead of having to subclass a frame just to get the same header and footer?
Why are you overriding the label to paint an image?
that is what the setIcon() method is for
you should never read a file in the custom painting code since this method is invoked multiple times
custom painting, when necessary, is done in the paintComponent() method.
Try setting preferred sizes before adding components to containers, and adding components "bottom-up". Otherwise, try calling pack(), revalidate(), repaint() etc.to adjust things.
Read up on Layout Managers, too, you're not using them correctly.
Also, Swing sucks. Try Netbeans, it makes it a little bit more bearable. It helps a lot when you need to manually place and resize things, too.

Categories

Resources