Hi I am writing a simple program to display a frame.
However the frame turns out real small when I type setLayout(null);
But if i ignore this command, the button is always at the top center
Can some one point out my error?
import java.io.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class theframe {
private static void create() {
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Insets insets = frame.getInsets();
frame.setSize(500 + insets.left + insets.right, 350 + insets.top + insets.bottom);
add(frame.getContentPane()); //my function add
frame.pack();
frame.setVisible(true);
}
public static void add(Container pane) {
pane.setLayout(null);
Insets insets = pane.getInsets();
JPanel p1 = new JPanel();
p1.setPreferredSize(new Dimension(500, 350));
JButton b1 = new JButton("one");
Dimension size = b1.getPreferredSize();
b1.setBounds(25 + insets.left, 5 + insets.top, size.width, size.height);
pane.add(p1);
p1.add(b1);
}
public static void main(String Args[]) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
create();
}
});
}
}
If you see the pack() function it is not in JFrame.java but Window.java
public void pack() {
Container parent = this.parent;
if (parent != null && parent.getPeer() == null) {
parent.addNotify();
}
if (peer == null) {
addNotify();
}
Dimension newSize = getPreferredSize();
if (peer != null) {
setClientSize(newSize.width, newSize.height);
}
if(beforeFirstShow) {
isPacked = true;
}
validateUnconditionally();
}
now if you see getPreferredSize() function in Container.java
#Deprecated
public Dimension preferredSize() {
/* Avoid grabbing the lock if a reasonable cached size value
* is available.
*/
Dimension dim = prefSize;
if (dim == null || !(isPreferredSizeSet() || isValid())) {
synchronized (getTreeLock()) {
prefSize = (layoutMgr != null) ?
layoutMgr.preferredLayoutSize(this) :
super.preferredSize();
dim = prefSize;
}
}
if (dim != null){
return new Dimension(dim);
}
else{
return dim;
}
}
Everything boils down to layoutMgr.preferredLayoutSize(this);
Since you are not setting the layout(or null) you are getting that issue. Either remove that statement or use some layout.
You're calling pack() method with absolute positioning (null layout), you should use LayoutManager instead of using setting the layout to null.
Just remove this line:
pane.setLayout(null);
NOTE: take my advice and learn yourself LayoutManagers, because if you refuse to learn, you'll probably go to null layout, yes,it is easier but highly recommended to not use it.
Some basic things you must know:
The default layout of JFrame is BorderLayout, which there only five locations you can put your components in as shown in the link.
FlowLayout is the default layout manager for every JPanel. It simply lays out components in a single row, starting a new row if its container is not sufficiently wide.
After all, read more about LayoutManagers before starting using swing, belive me it makes your work very easier, just try it, it's amazing.
Java GUIs might have to work on a number of platforms, on different screen resolutions & using different PLAFs. As such they are not conducive to exact placement of components.
To organize the components for a robust GUI, instead use layout managers, or combinations of them, along with layout padding & borders for white space.
See this answer for tips on how to:
Combine layouts.
Provide white space using layout constructor arguments & borders.
I had the same issue luckly, I solved with one line code. May this will also help you try to follow these steps:
1- Goto in your file code
2- In start of code find this line:
initComponents();
3- Just add this new code after this line:
this.setMinimumSize(new Dimension(300, 300));
The keyword "this" here representing your current top-level component, so new dimensions as (300, 300) is applied here. You may change this 300 values according to your required frame / component size.
Related
Whats the reason that a JInteralFrame with a GridLayout(x, y) doesn't fill up the entire window although I'm adding x*y buttons to it? Why is there white space around it like in the first picture below?
If i resize it a bit, I'm able to remove all the white space around the grid layout, like in the second picture below, but I do not understand why that's not always the case.
Why is there white space around it like in the first picture below?
A GridLayout assigns exactly the same width or height to every component, but for a GUI 20 (for example) components wide, it is only possible to do that every 20 pixels that the GUI is stretched. It arranges any 'left over' pixels of space to the left and right most components.
To get around that, you might instead use a GridBagLayout and adjust the weights of rows and columns to allow some components to take over the remaining space in a way that is almost unnoticeable (the difference in component sizes) to the user.
Why is there white space around it like in the first picture below?
Andrew's answer explains the problem with the GridLayout.
However I don't know if this can be fixed with any of the other standard JDK layout managers.
Check out the Relative Layout. It is a layout manager that allows you to give components a size relative to one another, so it is easy to make all components the same size. When there are extra pixels you can set the "rounding policy" to allocate the pixels to different components.
The layout is a little more complex because it can't be done with a single layout manager. In the example below you need a panel that uses the RelativeLayout for vertical layout. Then you need to create a separate panel for each row and those panels will use a RelativeLayout with a horizontal layout.
import java.awt.*;
import javax.swing.*;
public class SSCCE extends JPanel
{
SSCCE()
{
int size = 10;
setBackground( Color.RED );
Float constraint = new Float(1.0f);
RelativeLayout vertical = new RelativeLayout(RelativeLayout.Y_AXIS);
vertical.setRoundingPolicy( RelativeLayout.EQUAL );
vertical.setFill(true);
setLayout( vertical );
for (int i = 0; i < size; i++)
{
RelativeLayout horizontal = new RelativeLayout(RelativeLayout.X_AXIS);
horizontal.setRoundingPolicy( RelativeLayout.EQUAL );
horizontal.setFill(true);
JPanel row = new JPanel( horizontal );
for (int j = 0; j < size; j++)
{
row.add(new JButton(), constraint);
}
add(row, constraint);
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SSCCE());
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
Basically, I'm trying to make a button that has the text aligned to the left (so I'm using setHorizontalAlignment(SwingConstants.LEFT)) and the image on the right border of the button, far from the text.
I already tried setHorizontalTextAlignment(SwingConstants.LEFT), but that just makes the text go relativity to the left of the icon, which is not exactly what I want, since I needed the icon to be secluded from it.
Also, I can't make any fixed spacing because it's a series of buttons with different texts with different sizes.
I can't make any fixed spacing because it's a series of buttons with different texts with different sizes.
You can dynamically change the spacing with code like:
JButton button = new JButton("Text on left:")
{
#Override
public void doLayout()
{
super.doLayout();
int preferredWidth = getPreferredSize().width;
int actualWidth = getSize().width;
if (actualWidth != preferredWidth)
{
int gap = getIconTextGap() + actualWidth - preferredWidth;
gap = Math.max(gap, UIManager.getInt("Button.iconTextGap"));
setIconTextGap(gap);
}
}
};
button.setIcon( new ImageIcon("copy16.gif") );
button.setHorizontalTextPosition(SwingConstants.LEADING);
This is a derivative of camickr's answer to allow editing in a GUI builder as well as placing it in a dynamic layout. I also removed the UIManager.getInt("Button.iconTextGap") so the gap will shrink to 0 if necessary.
I called it a 'Justified' button in analogy with justified text alignment (stretches a paragraph to left & right by growing width of space characters).
public class JustifiedButton extends JButton {
#Override
public void doLayout() {
super.doLayout();
setIconTextGap(0);
if (getHorizontalTextPosition() != CENTER) {
int newGap = getSize().width - getMinimumSize().width;
if (newGap > 0)
setIconTextGap(newGap);
}
}
#Override
public Dimension getMinimumSize() {
Dimension minimumSize = super.getMinimumSize();
if (getHorizontalTextPosition() != CENTER)
minimumSize.width -= getIconTextGap();
return minimumSize;
}
#Override
public Dimension getPreferredSize() {
Dimension preferredSize = super.getPreferredSize();
if (getHorizontalTextPosition() != CENTER)
preferredSize.width -= getIconTextGap();
return preferredSize;
}
}
This is not exactly production-ready and needs some field-testing. If I find anything, I'll edit the code.
[edit] Now works for vertical text alignments. Also simplified a bit.
[edit2] Also manipulate getPreferredSize to play nice with scroll pane (otherwise it keeps growing and never shrinks again)
You can add a layout manager to your button.
JButton btn = new JButton();
btn.add(new JLabel(text));
btn.add(new JLabel(img));
btn.setLayout(/*best layout choice here*/);
btn.setPreferredSize(new Dimension(x,y));
btn.setMaximumSize(new Dimension(maxX, minY));
btn.setMinimumSize(new Dimension(minX, minY)); //this one is most important when it comes to layoutmanagers
Sorry I can't be much help when it comes to picking out a good layout - But this will eventually get you what you want. Maybe someone else can comment on which one to use.
Given my requirements:
Single vertical column of JPanels.
Set the vertical location* of the JPanel without using the properties of a sibling.
Component position and size are fixed when the frame is resized.
Keep other layout aspects automatic (such as preferred size calculation), as much as possible
(*) Location: I mean location as in Component.setLocation(x, y).
is there a solution which is obvious, and if this is GridBagLayout, how to do this?
Details:
I want to put components vertically in a column container (like a vertical Box) by specifying their vertical location only. What is the best way to do this without loosing the other benefits of a layout such as BoxLayout?
In a vertical Box, setting the vertical position of a component must be done using a filler, or by adjusting the size of the component just above, there is no such possibility like:
panel.setLocation(getLocation().x, y)
On the other hand using a no layout container puts on me the task manage:
The initial size of the component
The container resizing events.
Here the solution of null layout is recommended, here this is a custom one, and here this is GridBagLaout. Also MIGLayout appears to be universal one (but I'd prefer no adding another library to my project).
I have written the following program for someone who was also looking for the same requirements.
Note: Make sure to add the first element to 0 position because there will be no more components and no position will be available other than 0, 2nd to 0 or 1, 3rd to 0 or 1 or 2 and so on
public class VerticalList extends JFrame {
JPanel pnl = null;
TextField tf = new TextField(10);
Box center = Box.createVerticalBox();
JScrollPane jsp = new JScrollPane(center);
JPanel ctrl = new JPanel(new FlowLayout());
JButton send = new JButton("Send");
public VerticalList() {
jsp.setAutoscrolls(true);
ctrl.add(send);
ctrl.add(new JLabel("Position:"));
ctrl.add(tf);
Container cnt = getContentPane();
cnt.add(jsp, BorderLayout.CENTER);
cnt.add(ctrl, BorderLayout.SOUTH);
send.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pnl = new JPanel(new FlowLayout());
pnl.setBorder(BorderFactory.createLineBorder(Color.red));
pnl.add(new JLabel("Added to Position: "+tf.getText()));
pnl.setMaximumSize(new Dimension(Integer.MAX_VALUE, (int)pnl.getPreferredSize().getHeight()));
try{
int index = Integer.parseInt(tf.getText());
center.add(pnl, index);
}catch(Exception ex){
JOptionPane.showMessageDialog(null, "Please Provide a Valid position", "Position Error", JOptionPane.ERROR_MESSAGE);
}
validate();
}
});
setPreferredSize(new Dimension(300, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
pack();
}
public static void main(String[] args) {
new VerticalList();
}
}
I'm having an issue creating an empty JTabbedPane where the only portion to be seen on the GUI are the row of tabs.
Everytime I add a new tab with an "empty" component, the height of the JTabbedPane increases, but why?
The current workaround is to override getPreferredSize(), but it seems kludgy to me. Comment out the overridden method to see what I mean.
Am I missing something obvious?
Background:
We need a JTabbedPane where the tabbed pane starts off with 2 tabs, but the user can add more tabs as needed, up to 10. In addition, each tab contains the same components, but with different data. The decision was made to fake the look of a JTabbedPane, by implementing an empty JTabbedPane solely for the look, and to use a single fixed JPanel whose contents will be refreshed based on the tab clicked.
(Normally, I could just recreate the JPanel n-times, but that would nightmarish for the presenter classes who control the UI, which is beyond the scope of my question.)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CustomTabbedPane implements Runnable
{
static final int MAX_TABS = 11; // includes the "add" tab
JPanel pnlTabs;
JTabbedPane tabbedPane;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new CustomTabbedPane());
}
public void run()
{
JPanel p = buildPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(p);
frame.setSize(800,400);
frame.setVisible(true);
}
private JPanel buildPanel()
{
tabbedPane = new JTabbedPane()
{
#Override
public Dimension getPreferredSize()
{
Dimension dim = super.getPreferredSize();
dim.height = getUI().getTabBounds(this, 0).height + 1;
return dim;
}
};
tabbedPane.addTab("Tab 1", getEmptyComp());
tabbedPane.addTab("Tab 2", getEmptyComp());
tabbedPane.addTab("+", new TabCreator());
tabbedPane.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
addTab();
}
});
JScrollPane scroll = new JScrollPane(new JTable(5,10));
JPanel p = new JPanel(new BorderLayout());
p.add(tabbedPane, BorderLayout.NORTH);
p.add(scroll, BorderLayout.CENTER);
p.setBorder(BorderFactory.createLineBorder(Color.BLUE.darker(), 1));
return p;
}
private void addTab()
{
if (tabbedPane.getSelectedComponent() instanceof TabCreator)
{
int selIndex = tabbedPane.getSelectedIndex();
if (tabbedPane.getComponentCount() < MAX_TABS)
{
if (selIndex == tabbedPane.getComponentCount()-1)
{
String title = "Tab " + (selIndex + 1);
tabbedPane.insertTab(title, null, getEmptyComp(), "", selIndex);
tabbedPane.setSelectedIndex(selIndex);
if (tabbedPane.getComponentCount() == MAX_TABS)
{
tabbedPane.setEnabledAt(MAX_TABS-1, false);
}
}
}
}
}
private Component getEmptyComp()
{
return Box.createVerticalStrut(1);
}
class TabCreator extends JLabel {}
}
Great question! But it's fairly straightforward to get a hint on what's happening.
The problem is that your content does not have a minimum width, preferred size is not set, tab placement is top/bottom and the UI is default.
Since preferred size is not set, then when the layout is revalidated the calculations of space required go into the BasicTabbedPaneUI method Dimension calculateSize(false).
That reads:
int height = 0;
int width = 0;
<other vars>
// Determine minimum size required to display largest
// child in each dimension
<actual method>
Here it calculates the minimum size to accommodate any child and stores it into height/width. In your case this yields something like 10,10 (because of the single Label tab creator I think, I didn't follow that one).
Then happens the magic:
switch(tabPlacement) {
case LEFT:
case RIGHT:
height = Math.max(height, calculateMaxTabHeight(tabPlacement));
tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
width += tabExtent;
break;
case TOP:
case BOTTOM:
default:
width = Math.max(width, calculateMaxTabWidth(tabPlacement));
tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
height += tabExtent;
}
What happens here is it sets the preferred width to be the maximum of the largest tab width and the largest child width. In your case it's around 44 for the tab text. The tabExtent is then calculated to see just how many rows of tabs are needed to support this preferred width. In your case - it's 1 extra row of tabs for each tab. That's where the extra height in preferredSize().height comes from. Essentially because for horizontal tab placement it cares about width first, then height.
How to fix:
Set a preferred size :) I know a lot of people say don't set the preferred size, but in this case this will just work. Since a preferred size is set (via actually setting it, not overriding getPreferredSize()), the code will never get to counting tabs.
Give at least one of your children a size (via setPreferredSize or overriding getPreferredSize). If one of the childrens width is that of the frame, or, say, the table at the bottom the TabbedPane will not be allocating an extra row for each tab, since a single row will fit everything.
Make your own UI for the tabbed pane. It may be easier to make your own tabbed pane though really, I've never done this.
EDIT:
After thinking about this a bit more, I realized that solution number 1 AND your own solution suffer from the flaw that, if the tabbed pane actually does require multiple rows for the tabs (hello frame resizes), bad things will happen. Don't use it.
So, I have a grid layout which stores JScrollPane's in each cell. These are also put into an array for other purposes. The "View" extends "JPanel" so it's just a regular panel with image support. The application starts up with cell's filled with scrollPane's that contain the View which doesn't have a image yet.
At that point I see no scrollbar, but that doesn't matter since there is nothing inside the JPanel. As soon as I open an image and use drawImage in the paintComponenet of the JPanel I don't see scrollbar's showing up. Here's how I create the grid and the Scrollpane
private void createContentPane()
{
GridLayout gridLay = new GridLayout(GRID_ROWS, GRID_COLUMNS);
perspectiveTbl = new JScrollPane[NUM_PERSPECTIVE];
mainPane = new JPanel();
mainPane.setLayout(gridLay);
int idx = 0;
while(idx < perspectiveTbl.length)
{
perspectiveTbl[idx] = new JScrollPane(new View(modelImage));
mainPane.add(perspectiveTbl[idx]);
idx++;
}
this.getContentPane().add(mainPane, BorderLayout.CENTER);
}
I'm not exactly sure why the scrollbar's aren't showing up, should they have been set inside the panel for the image?
Here's an image of the application, as you can see the picture of the shoe does not receive scrollbar's so there is no way to view the rest of the picture:
Picture
You can either user not JPanel with image but usual JLabel with the image
or
call setPreferredSize() for the panels to reflect the image's size.
Thanks for the hint Stanislav, I actually figured it out and got it working an hour ago, but you did give the right path to fix it with the preferredSize attribute. I ended up re-implemented getPreferredSize with the size of the image inside the panel, added revalidate to the paint event so that the bars show up as soon as the image is loaded.
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
this.getViewScrollPane().revalidate();
}
public Dimension getPreferredSize()
{
if(image != null)
return new Dimension(image.getWidth(),image.getHeight());
else
return super.getPreferredSize();
}