I'm trying to put multiple JLists in a JPanel and the JPanel in a JScrollPane. The problem is that when I add the JScrollPane to the equation there is a space between the JList and outer border, how do I get rid of this border so it's fills horizontally?
Here is a small sample code that demonstrates this problem with a JLabel instead of JList.
/edit: It's seems to be windows thing, it runs fine on Mac OS.
import java.awt.*;
import javax.swing.*;
public class Test extends JFrame
{
public static void main(String[] args)
{
Test test = new Test();
}
public Test()
{
JPanel contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setContentPane(contentPane);
this.setMinimumSize(new Dimension(400, 300));
JScrollPane sp = new JScrollPane(createLeftPane());
sp.setBorder(BorderFactory.createLineBorder(Color.yellow));
this.add(sp, BorderLayout.WEST);
LookAndFeel.installBorder(sp, "BorderFactory.createEmptyBorder()");
StackTraceElement[] st = Thread.currentThread().getStackTrace();
for(int i = 0; i < st.length; i++) {
System.out.println(st[i]);
}
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
private JPanel createLeftPane()
{
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
JLabel label = new JLabel("TEST LABEL");
label.setBorder(BorderFactory.createLineBorder(Color.blue));
panel.add(label, c);
panel.setBorder(BorderFactory.createLineBorder(Color.red));
return panel;
}
}
Here's a screenshot (first one is without the scrollpane)
Stack Trace:
java.lang.Thread.getStackTrace(Thread.java:1479)
test.Test.<init>(Test.java:27)
test.Test.main(Test.java:10)
/edit: After some trial and error I found out that when I add a c.weightx = 0.5 to the contrains it does fill Horizontally, but when the scrollPane becomes larger than it's content it makes itself wider, which is weird. See the screenshots below:
The problem isn't because of the scrollpane. Its because you add the label to a JPanel. By default a panel uses a FlowLayot which has has a vertical/horizontal gap of 5 pixels around each component.
Change the FlowLayout to use a 0 gap, or use a different layout. Maybe a BoxLayout.
component.setBorder(null) removes any border which has been set on the component
The problem doesn't occur when I run it on my Mac, so apparently it's a Windows thing.
In addition to the expected UI defaults for background, foreground, font and opaque, BasicPanelUI installs a border, while com.apple.laf.AquaPanelUI (apparently) does not.
LookAndFeel.installBorder(p,"Panel.border");
You might try setting yours to null.
are you meaning this way ???
import java.awt.*;
import java.io.File;
import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
public class FilesInTheJList {
private static final int COLUMNS = 5;
private Dimension size;
public FilesInTheJList() {
final JList list = new JList(new File("C:\\").listFiles()) {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredScrollableViewportSize() {
if (size != null) {
return new Dimension(size);
}
return super.getPreferredScrollableViewportSize();
}
};
list.setFixedCellHeight(50);
list.setFixedCellWidth(150);
size = list.getPreferredScrollableViewportSize();
size.width *= COLUMNS;
list.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
list.setCellRenderer(new MyCellRenderer());
list.setVisibleRowCount(0);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
JFrame f = new JFrame("Files In the JList");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(list));
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
FilesInTheJList fITJL = new FilesInTheJList();
}
});
}
private static class MyCellRenderer extends JLabel implements ListCellRenderer {
private static final long serialVersionUID = 1L;
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
if (value instanceof File) {
File file = (File) value;
setText(file.getName());
setIcon(FileSystemView.getFileSystemView().getSystemIcon(file));
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setPreferredSize(new Dimension(250, 25));
setEnabled(list.isEnabled());
setFont(list.getFont());
setOpaque(true);
}
return this;
}
}
}
.
.
similair but better way by #trashgod here
The solution of trashgod worked for me with
LookAndFeel.installBorder(scrollpane,BorderFactory.createEmptyBorder().toString());
You can also remove the JScrollPane's border using
JScrollPane yourScrollPane = new JScrollPane();
yourScrollPane.setBorder(BorderFactory.createEmptyBorder());
Related
I am trying to solve the following problem: I have a program, where text fields are being added dynamically to a JPanel, but when too many fields are added, I want a scrollbar to be shown, so that the user doesn't have to resize the window in order to see all the fields. So far I can generate the fields without a problem, but adding the scrollbar seems not to be working...
I have the following code:
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.FlowLayout;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import javax.swing.JScrollBar;
public class AddRuleFrame extends JFrame implements ActionListener {
JPanel panel;
JPanel buttonPanel;
JScrollPane scroll;
private JButton btnAddType;
private JButton btnDeleteField;
private JButton btnSaveRule;
public AddRuleFrame() {
getContentPane().setLayout(new BorderLayout());
this.buttonPanel=new JPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
//Initializing the JScrollPane
scroll = new JScrollPane(this.panel);
scroll.setViewportView(this.panel);
btnAddType = new JButton("Add type");
btnAddType.addActionListener(this);
buttonPanel.add(btnAddType);
btnDeleteField = new JButton("Delete field");
btnDeleteField.addActionListener(this);
buttonPanel.add(btnDeleteField);
btnSaveRule = new JButton("Save rule");
buttonPanel.add(btnSaveRule);
this.panel = new JPanel();
this.panel.setLayout(new FlowLayout());
getContentPane().add(panel, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(538, 487);
setVisible(true);
}
public void actionPerformed(ActionEvent evt) {
if(evt.getSource()==this.btnAddType){
this.panel.add(new JTextField(20));
this.panel.revalidate();
}
if(evt.getSource()==this.btnDeleteField){
System.out.println("delete pressed");
}
if(evt.getSource()==this.btnSaveRule){
System.out.println("");
}
validate();
}
public static void main(String[] args) {
AddRuleFrame frame = new AddRuleFrame();
}
}
Thank you!
The issue with the scroll pane simply was that you were adding it to nothing and setting its viewport before the panel was intialized.
However, I noticed a few other issues.
One issue is that FlowLayout adds components horizontally, so I've changed the layout of the panel to a BoxLayout and created a small subclass of JTextField to override the maximum size. (BoxLayout uses maximum sizes to size components, so without doing that the text fields get stretched to the height of the panel.)
I also used SwingUtilities.invokeLater to start the program on the Swing thread, as show in the Initial Threads tutorial.
Instead of calling setSize on a JFrame directly, I overrode getPreferredSize and calculated a size dynamically based on the screen dimensions, then called pack() to size the components automatically. In general, Swing isn't designed for explicitly setting pixel dimensions.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class AddRuleFrame extends JFrame implements ActionListener {
private JPanel panel;
private JPanel buttonPanel;
private JScrollPane scroll;
private JButton btnAddType;
private JButton btnDeleteField;
private JButton btnSaveRule;
public AddRuleFrame() {
getContentPane().setLayout(new BorderLayout());
buttonPanel = new JPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
btnAddType = new JButton("Add type");
btnAddType.addActionListener(this);
buttonPanel.add(btnAddType);
btnDeleteField = new JButton("Delete field");
btnDeleteField.addActionListener(this);
buttonPanel.add(btnDeleteField);
btnSaveRule = new JButton("Save rule");
buttonPanel.add(btnSaveRule);
panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
scroll = new JScrollPane(panel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
getContentPane().add(scroll, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null); // this centers the window
setVisible(true);
}
#Override
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == btnAddType) {
panel.add(new BoxyTextField(20));
panel.revalidate();
}
validate();
}
class BoxyTextField extends JTextField {
BoxyTextField(int width) {
super(width);
}
#Override
public Dimension getMaximumSize() {
Dimension size = super.getMaximumSize();
size.height = getPreferredSize().height;
return size;
}
}
#Override
public Dimension getPreferredSize() {
// See my exchange with MadProgrammer in the comments for
// a discussion of whether Toolkit#getScreenSize() is an
// appropriate way to get the screen dimensions for sizing
// a window.
// Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
// This is the correct way, as suggested in the documentation
// for java.awt.GraphicsEnvironment#getMaximumWindowBounds():
GraphicsConfiguration config = getGraphicsConfiguration();
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config);
Dimension size = config.getBounds().getSize();
size.width -= insets.left + insets.right;
size.height -= insets.top + insets.bottom;
// Now we have the actual available space of the screen
// so we can compute a relative size for the JFrame.
size.width = size.width / 3;
size.height = size.height * 2 / 3;
return size;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AddRuleFrame frame = new AddRuleFrame();
}
});
}
}
For your comment, generally the correct way to add a gap between components with a BoxLayout is to use a filler component. This is discussed in the tutorial, which I already linked to.
So you might do something like this:
#Override
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == btnAddType) {
if (panel.getComponentCount() > 0) {
panel.add(Box.createVerticalStrut(10));
}
panel.add(new BoxyTextField(20));
panel.revalidate();
}
However, this creates a bit of an issue if you're planning on removing stuff dynamically, because you need to remember to remove the filler component as well:
if (evt.getSource() == btnDeleteField) {
int lastZIndex = panel.getComponentCount() - 1;
if (lastZIndex >= 0) {
panel.remove(lastZIndex);
if (lastZIndex > 0) {
panel.remove(lastZIndex - 1);
}
panel.revalidate();
}
}
validate();
panel.repaint();
}
So I think the best option is that instead of extending JTextField and adding the the text field and filler to the panel directly, extend JPanel, and do something like this:
class BoxyTextFieldCell extends JPanel {
JTextField jTextField;
BoxyTextFieldCell(int width, int margin) {
jTextField = new JTextField(width);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(jTextField);
add(Box.createVerticalStrut(margin));
}
#Override
public Dimension getMaximumSize() {
Dimension size = super.getMaximumSize();
size.height = getPreferredSize().height;
return size;
}
}
#Override
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == btnAddType) {
panel.add(new BoxyTextFieldCell(20, 10));
panel.revalidate();
}
if (evt.getSource() == btnDeleteField) {
int lastZIndex = panel.getComponentCount() - 1;
if (lastZIndex >= 0) {
panel.remove(lastZIndex);
panel.revalidate();
}
}
validate();
panel.repaint();
}
Doing something like that certainly leaves you with a lot of flexibility.
Otherwise, I think you could also use an editable JTable with a single column (which more or less behaves just like a stack of text fields) and use setRowMargin(int). I guess it might end up being easier to use a JTable if you aren't very comfortable with using layouts yet. See e.g. here for examples of adding and removing rows in a JTable.
There are two problems :
1) You never add your JScrollPane to anything.
2) You set its viewport view to a Component that is null (not yet initialized).
This is a modified version of your constructor that fixes both problems (see comments in the code) :
public AddRuleFrame() {
getContentPane().setLayout(new BorderLayout());
buttonPanel = new JPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
//Initializing the JScrollPane
btnAddType = new JButton("Add type");
btnAddType.addActionListener(this);
buttonPanel.add(btnAddType);
btnDeleteField = new JButton("Delete field");
btnDeleteField.addActionListener(this);
buttonPanel.add(btnDeleteField);
btnSaveRule = new JButton("Save rule");
buttonPanel.add(btnSaveRule);
panel = new JPanel();
panel.setLayout(new FlowLayout());
scroll = new JScrollPane(panel);
scroll.setViewportView(panel);// Set the viewport view only when the panel has been initialized
getContentPane().add(scroll, BorderLayout.CENTER);// Add the scrollpane, not the panel
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(538, 487);
setVisible(true);
}
I have a JPanel inside of a JScrollPane inside of a GridBagLayout
that is not scrolling properly. What should happen is that every time the
xxx button is pressed, a new line is added inside of the scrolling pane.
What actually happens is that if xxx is pressed, say 10 times, only the
first seven lines show up and the rest can't be scrolled to. Can anyone
suggest changes to the source code below that will make scrolling behave
properly? I have spent hours on this with no success, trying strategies all
over the Web.
Notes:
The text is split in paintComponent because drawString does
not handle end-of-line characters.
The JPanel inside of JScrollPane inside of GridBagLayout configuration is a necessary part of a much larger
piece of software with the same problem, so I have kept it here.
Thanks.
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class question {
public static void main(String[] args) {
new DrawingGUI();
}
private static class ScrollingPane extends JPanel {
private static final long serialVersionUID = 1L;
String text;
public ScrollingPane() {
setPreferredSize(new Dimension(300, 100));
text = "";
}
public void SetText(String text_x) {text = text + System.lineSeparator() + text_x;}
public void paintComponent(Graphics g) {
super.paintComponent(g);
FontMetrics fm = g.getFontMetrics();
int y = -fm.getHeight();
for (String text : text.split("\n"))
g.drawString(text, 0, y += fm.getHeight());
}
}
static class DrawingGUI extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
JPanel jp;
JScrollPane js;
ScrollingPane sp;
int LineNum;
DrawingGUI() {
LineNum = 0;
JFrame frame = new JFrame("xxx");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
addComponentsToPane(frame.getContentPane());
frame.setSize(800,800);
frame.setVisible(true);
}
public void addComponentsToPane(Container pane) {
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton button = new JButton("xxx");
button.setActionCommand("add_text");
button.addActionListener(this);
c.gridx = 0;
c.gridy = 0;
pane.add(button, c);
jp = new JPanel();
c.gridx = 0;
c.gridy = 1;
c.ipadx = 600;
c.ipady = 450;
pane.add(jp, c);
sp = new ScrollingPane();
js = new JScrollPane(sp);
js.getViewport().setPreferredSize(new Dimension(300,100));
c.gridx = 0;
c.gridy = 2;
c.ipady = 50;
pane.add(js, c);
}
public void actionPerformed(ActionEvent e) {
if ("add_text".equals(e.getActionCommand())) {
LineNum++;
sp.SetText("LineNum = " + LineNum);
sp.revalidate();
sp.repaint();
}
}
}
}
You're short circuiting the ability of your ScrollingPane JPanel from correctly sizing itself with this line:
setPreferredSize(new Dimension(300, 100));
This will fix the size of the ScrollingPane JPanel. I see that you have several possible solutions:
Difficult: override getPreferredSize() for the ScrollingPane JPanel, and calculate the appropriate preferred size based on the size of the text it holds and draws using FontMetrics.
Easier: Don't add text as you're doing, but rather have ScrollingPane use a GridLayout(0, 1) (one column, variable number of rows), and add JLabels to the ScrollingPane when new text is needed to be added. Then call revalidate() and repaint() on it.
Easier still: Don't use a ScrollingPane JPanel but rather a JTextArea, that looks like a JPanel and that can't be edited. Add that to the JScrollPane, and again, do not restrict its size
Easiest: Just use a JList as that's the functionality you are using here.
For instance, either of these would work and would look similar:
private static class ScrollingPane2 extends JPanel {
private static final long serialVersionUID = 1L;
private JTextArea textArea = new JTextArea(6, 20);
public ScrollingPane2() {
setLayout(new BorderLayout());
add(textArea, BorderLayout.CENTER);
textArea.setEditable(false);
textArea.setFocusable(false);
textArea.setBackground(null);
}
public void SetText(String text_x) {
textArea.append(text_x + "\n");
}
}
private static class ScrollingPane3 extends JPanel {
private static final long serialVersionUID = 1L;
private DefaultListModel<String> listModel = new DefaultListModel<>();
private JList<String> jList = new JList<>(listModel);
public ScrollingPane3() {
setLayout(new BorderLayout());
add(jList, BorderLayout.CENTER);
jList.setBackground(null);
}
public void SetText(String text_x) {
listModel.addElement(text_x);
}
}
There seems to be an issue with aligning certain characters to the center of a BoxLayout along the y-axis in Java. I don't know what could cause this, & I've created an SSCCE to demonstrate the effect. In the example, I only use the character 'a', & I draw a line down the direct middle of each JPanel to demonstrate how far off each case is from the center. The case with bold text seems to line up fine, but normal formatting & italics are both grossly off-center, despite using both setAlignmentX & setHorizontalAlignment. Any help on understanding this effect is appreciated.
In the case that somehow the problem is with Java on my specific computer, this is an image of what displays on my screen when I run the SSCCE, which loads three different JPanels with BoxLayouts along the y-axis & puts a centered JLabel with only the character 'a' in each:
& here is the code for the SSCCE:
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
public class AlignmentTest extends JPanel
{
public AlignmentTest(char label, int style)
{
JLabel l = new JLabel(Character.toString(label));
setBorder(BorderFactory.createLineBorder(Color.BLACK,1));
setBackground(Color.WHITE);
setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
setPreferredSize(new Dimension(300,50));
add(Box.createVerticalGlue());
add(l);
l.setFont(l.getFont().deriveFont(style));
l.setAlignmentX(CENTER_ALIGNMENT);
l.setHorizontalAlignment(JLabel.CENTER);
add(Box.createVerticalGlue());
}
public static void main(String[] args)
{
JFrame f = new JFrame("Alignment Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1,0,5,5));
f.add(new AlignmentTest('a',Font.PLAIN));
f.add(new AlignmentTest('a',Font.BOLD));
f.add(new AlignmentTest('a',Font.ITALIC));
f.pack();
f.setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawLine(getWidth()/2,0,getWidth()/2,getHeight());
}
}
Another way to avoid "Box Layout Features: … Any extra space appears at the right of the container", you would need to override the JLabel#getMinimumSize() method to return the same Dimension as JLabel#getPreferredSize().
Sorry, I misunderstood.
As #camickr has already said,
I would guess there is some weird rounding error in the layout. This seems like a bug to me.
is quite correct.
Fixed example:
//MinimumSize checkbox
//selected true: set min width = 100px
//selected false: set min width = 7px(default "a" width)
//Here's my attempt(I am running JDK 1.7.0_72 on Windows 7):
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class AlignmentTest4 extends JPanel {
private static boolean FLAG = false;
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
}
#Override public Dimension getPreferredSize() {
return new Dimension(300, 80);
}
public static JLabel makeLabel(String label, int style) {
JLabel l = new JLabel(label) {
#Override public Dimension getPreferredSize() {
return new Dimension(120, 30);
}
#Override public Dimension getMinimumSize() {
Dimension d = super.getMinimumSize();
if (FLAG) {
d.width = 100;
} else {
d.width = 7;
}
return d;
//if (FLAG) {
// return this.getPreferredSize();
//} else {
// return super.getMinimumSize();
//}
}
};
l.setOpaque(true);
l.setBackground(Color.ORANGE);
l.setFont(l.getFont().deriveFont(style));
l.setAlignmentX(Component.CENTER_ALIGNMENT);
l.setAlignmentY(Component.CENTER_ALIGNMENT);
l.setVerticalAlignment(SwingConstants.CENTER);
l.setVerticalTextPosition(SwingConstants.CENTER);
l.setHorizontalAlignment(SwingConstants.CENTER);
l.setHorizontalTextPosition(SwingConstants.CENTER);
return l;
}
public static JComponent makePanel() {
JPanel p = new JPanel(new GridLayout(0, 1, 5, 5));
JPanel p1 = new AlignmentTest4();
p1.setBorder(BorderFactory.createTitledBorder("BoxLayout.X_AXIS"));
p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
p1.add(Box.createHorizontalGlue());
p1.add(makeLabel("a", Font.PLAIN));
p1.add(Box.createHorizontalGlue());
JPanel p2 = new AlignmentTest4();
p2.setBorder(BorderFactory.createTitledBorder("BoxLayout.Y_AXIS"));
p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
p2.add(Box.createVerticalGlue());
p2.add(makeLabel("a", Font.PLAIN));
p2.add(Box.createVerticalGlue());
for (JPanel c : Arrays.asList(p1, p2)) {
c.setBackground(Color.WHITE);
p.add(c);
}
return p;
}
public static JComponent makeUI() {
final JPanel p = new JPanel(new BorderLayout());
p.add(makePanel());
p.add(new JCheckBox(new AbstractAction("MinimumSize") {
#Override public void actionPerformed(ActionEvent e) {
FLAG = ((JCheckBox) e.getSource()).isSelected();
SwingUtilities.updateComponentTreeUI(p);
}
}), BorderLayout.SOUTH);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame("Alignment Test");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(makeUI());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Using JDK7 on Windows 7 none of the characters are center aligned.
I made some changes to display a JTextField and I played with the columns of the JTextField (1, 3, 5). As the columns increased the center aligned improved and was reasonable at columns 5 and above.
So the problem is somehow related to the width of the component.
I would guess there is some weird rounding error in the layout. This seems like a bug to me.
In case you are interested in a layout that provides some similar functionality to the BoxLayout you can check out the Relative Layout. The changes to your example are minor:
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
public class AlignmentTest extends JPanel
{
public AlignmentTest(char label, int style)
{
JLabel l = new JLabel(Character.toString(label));
setBorder(BorderFactory.createLineBorder(Color.BLACK,1));
setBackground(Color.WHITE);
// setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
setLayout(new RelativeLayout(RelativeLayout.Y_AXIS));
setPreferredSize(new Dimension(300,50));
// add(Box.createVerticalGlue());
add(Box.createVerticalGlue(), new Float(1));
add(l);
l.setFont(l.getFont().deriveFont(style));
l.setAlignmentX(CENTER_ALIGNMENT);
l.setHorizontalAlignment(JLabel.CENTER);
// add(Box.createVerticalGlue());
add(Box.createVerticalGlue(), new Float(1));
}
public static void main(String[] args)
{
JFrame f = new JFrame("Alignment Test");
JScrollPane scroller = new JScrollPane();
JPanel panel = new JPanel(new GridLayout(1,0,5,5));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1,0,5,5));
f.add(new AlignmentTest('a',Font.PLAIN));
f.add(new AlignmentTest('a',Font.BOLD));
f.add(new AlignmentTest('a',Font.ITALIC));
f.pack();
f.setVisible(true);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawLine(getWidth()/2,0,getWidth()/2,getHeight());
}
}
The effect you observe appears to be an artifact of the way BoxLayout works. Interpolating from How to Use BoxLayout: Box Layout Features, "When a BoxLayout lays out components from left to right, … Any extra space appears at the right of the container." When the enclosing container's initial size is a small multiple of the label's (fixed) size, as shown below, the anomaly is minimal; stretch the frame horizontally to see how it grows. One work-around would be to minimize the degree to which the enclosing container's preferred size is artificially enlarged.
import javax.swing.*;
import java.awt.*;
public class AlignmentTest extends JPanel {
private final JLabel l;
public AlignmentTest(String label, int style) {
setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
setBackground(Color.WHITE);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
l = new JLabel(label, JLabel.CENTER);
l.setFont(l.getFont().deriveFont(style));
l.setAlignmentX(CENTER_ALIGNMENT);
l.setOpaque(true);
l.setBackground(Color.cyan);
add(Box.createVerticalGlue());
add(l);
add(Box.createVerticalGlue());
}
#Override
public Dimension getPreferredSize() {
int w = l.getPreferredSize().width;
int h = l.getPreferredSize().height;
return new Dimension(w * 3, h * 3);
}
public static void main(String[] args) {
JFrame f = new JFrame("Alignment Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(1, 0, 5, 5));
f.add(new AlignmentTest("aMa", Font.PLAIN));
f.add(new AlignmentTest("aMa", Font.BOLD));
f.add(new AlignmentTest("aMa", Font.ITALIC));
f.pack();
f.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
}
}
Is it possible to increase the height of a JComboBox?(not refering to the popup menu)
Have tried:
comboBox.setPreferredSize(new Dimension(200, 100));
and
Component[] comp = comboBox.getComponents();
for (int i = 0; i < comp.length; i++) {
if (comp[i] instanceof JButton) {
JButton btn = (JButton) comp[i];
btn.setPreferredSize(new Dimension(200, 100));
}
}
But no luck.
Then I tried to fix the problem with layout managers:
JPanel panel = new JPanel(new GridBagLayout());
panel.setPreferredSize(new Dimension(100, 100));
GridBagConstraints c = new GridBagConstraints();
c.weighty = 1;
c.fill = GridBagConstraints.VERTICAL;
panel.add(cbox, c);
But this dose not seems to change the size of the JComboBox button.
public class ComboBoxFontChange extends JFrame {
public ComboBoxFontChange() {
// CREATE BOX
JComboBox<String> cbox = new JComboBox<String>();
cbox.setFont(cbox.getFont().deriveFont(30.0f));
// TRY CHANGE SIZE: DOSE NOT WORK..
cbox.setPreferredSize(new Dimension(200, 100));
cbox.setSize(new Dimension(200, 100));
cbox.setMinimumSize(new Dimension(200, 100));
// TRY CHANGE SIZE ON BUTTON INSTEAD: DOSE NOT WORK..
Component[] comp = cbox.getComponents();
for (int i = 0; i < comp.length; i++) {
if (comp[i] instanceof JButton) {
JButton btn = (JButton) comp[i];
btn.setPreferredSize(new Dimension(200, 100));
btn.setSize(new Dimension(200, 100));
btn.setMinimumSize(new Dimension(200, 100));
}
}
cbox.addItem("Quarter");
cbox.addItem("Nickel");
cbox.addItem("Penny");
JPanel panel = new JPanel();
panel.add(cbox);
getContentPane().add(panel);
}
public static void main(String[] args) {
ComboBoxFontChange frame = new ComboBoxFontChange();
frame.setSize(300, 150);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Here:
JPanel panel = new JPanel();
panel.add(cbox);
By default JPanel has a FlowLayout as layout manager and this one doesn't honor the components preferred size. It just fits the components using their minimum possible size. As #alex2410 says in his comment you need to manage components size and position by using a proper Layout manager.
Also take a look to this topic: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Take a look at this code. Using custom ListCellRenderer I set the preferred size the of the visible cell.
You can use the following code class CustomComboBox and just change the dimension of the getPreferredSize of the JLabel.
And then for your comboBox just set the renderer comboBox.setRenderer(newCustomComboBox());
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
public class ComboBoxDemo extends JFrame {
JComboBox cbo = new JComboBox(new String[] {"Hello, StackOverflow"});
public ComboBoxDemo(){
cbo.setRenderer(new CustomComboBox());
add(cbo, BorderLayout.SOUTH);
add(new JLabel("Hello"), BorderLayout.CENTER);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new ComboBoxDemo();
}
});
}
static class CustomComboBox extends JLabel implements ListCellRenderer {
#Override
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
JLabel label = new JLabel(){
public Dimension getPreferredSize(){
return new Dimension(200, 100);
}
};
label.setText(String.valueOf(value));
return label;
}
}
}
ListCellRenderer javadoc | JComboBox/ListCellRenderer tutorial
I want to add multiple jpanels to jpanel.So i added a root panel to jscrollpane.and then added all individual jpanels to this root panel.I made jscrollpane's scrolling policy as needed.i.e HORIZONTAL_SCROLLBAR_AS_NEEDED,VERTICAL_SCROLLBAR_AS_NEEDED.
But the problem is all individual panels are not shown inside root panel.
Code:
JScrollPane scPanel=new JScrollPane();
JPanel rootPanel=new JPanel();
rootPanel.setLayout(new FlowLayout());
JPanel indPanel = new JPanel();
rootPanel.add(indPanel);
JPanel indPanel2 = new JPanel();
rootPanel.add(indPanel2);
//.....like this added indPanals to rootPanel.
scPanel.setViewPortView(rootPanel);
//scPanel.setHorizontalScrollPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
And one more thing is, as i scroll the scrollbar the panels are going out of jscrollpane area.
I am not able to see all individual panels,
Please suggest me.
Edit: code snippet from double post:
MosaicFilesStatusBean mosaicFilesStatusBean = new MosaicFilesStatusBean();
DefaultTableModel tableModel = null;
tableModel = mosaicFilesStatusBean.getFilesStatusBetweenDates(startDate, endDate);
if (tableModel != null) {
rootPanel.removeAll();
rootPanel.setLayout(new BoxLayout(rootPanel, BoxLayout.PAGE_AXIS));
for (int tempRow = 0; tempRow < tableModel.getRowCount(); tempRow++) {
int fileIdTemp = Integer.parseInt(tableModel.getValueAt(tempRow, 0).toString());
String dateFromTemp = tableModel.getValueAt(tempRow, 3).toString();
String dateToTemp = tableModel.getValueAt(tempRow, 4).toString();
int processIdTemp = Integer.parseInt(tableModel.getValueAt(tempRow, 5).toString());
int statusIdTemp = Integer.parseInt(tableModel.getValueAt(tempRow, 6).toString());
String operatingDateTemp = tableModel.getValueAt(tempRow, 7).toString();
MosaicPanel tempPanel =
new MosaicPanel(fileIdTemp, dateFromTemp, dateToTemp, processIdTemp, statusIdTemp, operatingDateTemp);
rootPanel.add(tempPanel);
}
rootPanel.revalidate();
}
The main reason, why you couldn't see your JPanel is that you are using FlowLayout as the LayoutManager for the rootPanel. And since your JPanel added to this rootPanel has nothing inside it, hence it will take it's size as 0, 0, for width and height respectively. Though using GridLayout such situation shouldn't come. Have a look at this code example attached :
import java.awt.*;
import javax.swing.*;
public class PanelAddition
{
private void createAndDisplayGUI()
{
JFrame frame = new JFrame("Panel Addition Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPane = new JPanel();
contentPane.setLayout(new GridLayout(0, 1));
JScrollPane scroller = new JScrollPane();
CustomPanel panel = new CustomPanel(1);
contentPane.add(panel);
scroller.setViewportView(contentPane);
frame.getContentPane().add(scroller, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
for (int i = 2; i < 20; i++)
{
CustomPanel pane = new CustomPanel(i);
contentPane.add(pane);
contentPane.revalidate();
contentPane.repaint();
}
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new PanelAddition().createAndDisplayGUI();
}
});
}
}
class CustomPanel extends JPanel
{
public CustomPanel(int num)
{
JLabel label = new JLabel("" + num);
add(label);
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(200, 50));
}
}
Don't use FlowLayout for the rootPanel. Instead consider using BoxLayout:
JPanel rootPanel=new JPanel();
// if you want to stack JPanels vertically:
rootPanel.setLayout(new BoxLayout(rootPanel, BoxLayout.PAGE_AXIS));
Edit 1
Here's an SSCCE that's loosely based on your latest code posted:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class PanelsEg extends JPanel {
private static final int MAX_ROW_COUNT = 100;
private Random random = new Random();
private JPanel rootPanel = new JPanel();
public PanelsEg() {
rootPanel.setLayout(new BoxLayout(rootPanel, BoxLayout.PAGE_AXIS));
JScrollPane scrollPane = new JScrollPane(rootPanel);
scrollPane.setPreferredSize(new Dimension(400, 400)); // sorry kleopatra
add(scrollPane);
add(new JButton(new AbstractAction("Foo") {
#Override
public void actionPerformed(ActionEvent arg0) {
foo();
}
}));
}
public void foo() {
rootPanel.removeAll();
// rootPanel.setLayout(new BoxLayout(rootPanel, BoxLayout.PAGE_AXIS)); // only need to set layout once
int rowCount = random.nextInt(MAX_ROW_COUNT);
for (int tempRow = 0; tempRow < rowCount ; tempRow++) {
int fileIdTemp = tempRow;
String data = "Data " + (tempRow + 1);
MosaicPanel tempPanel =
new MosaicPanel(fileIdTemp, data);
rootPanel.add(tempPanel);
}
rootPanel.revalidate();
rootPanel.repaint(); // don't forget to repaint if removing
}
private class MosaicPanel extends JPanel {
public MosaicPanel(int fileIdTemp, String data) {
add(new JLabel(data));
}
}
private static void createAndShowGui() {
PanelsEg mainPanel = new PanelsEg();
JFrame frame = new JFrame("PanelsEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
This SSCCE works, in that it easily shows removing and adding JPanels to another JPanel that is held by a JScrollPane. If you're still having a problem, you should modify this SSCCE so that it shows your problem.