Change height of JCombobox - java

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

Related

List of Buttons one below other inside JScrollPane

I want to make list of JButtons (with fixed dimensions, one beneath another) inside JScrollPane, using Swing. My idea was to make JPanel with GridBagLayout and add buttons in their suiting rows, and then create JScrollPane with that JPanel. That looks fine when number of buttons is large, but when the number of buttons is 2 or 3, I can't manage to align buttons one right below the other.
Also later I will add option to add new button (thus the + sign).
Works fine with 10 buttons
I get this empty space between button 0 and button 1 when it's just 2 buttons (this is the problem)
The code (creates upper east panel)
private JPanel createLayerPanel() {
JPanel layerPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
// Label ------------------------------------------------
JLabel layersLabel = new JLabel("Buttons");
layersLabel.setHorizontalAlignment(SwingConstants.CENTER);
layersLabel.setFont(DEFAULT_FONT);
//layersLabel.setBorder(new LineBorder(Color.red, 3));
layersLabel.setBackground(new Color(0x22222));
layersLabel.setForeground(new Color(0xFFFFFF));
layersLabel.setOpaque(true);
c.gridx = c.gridy = 0;
c.ipadx = 180;
c.weightx = 1;
c.fill = GridBagConstraints.BOTH;
layerPanel.add(layersLabel, c);
// Button ------------------------------------------------
JButton newLayerBtn = new JButton("+");
newLayerBtn.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 18));
newLayerBtn.setBackground(new Color(0x222222));
newLayerBtn.setForeground(Color.white);
newLayerBtn.setFocusable(false);
c.gridx = 1;
c.gridy = 0;
c.ipadx = 0;
c.weightx = 0;
layerPanel.add(newLayerBtn, c);
// ScrollPane ------------------------------------------------
//------------------------------------------------------------
//------------------------------------------------------------
JPanel layerListPanel = new JPanel(new GridBagLayout());
layerListPanel.setBackground(Color.BLACK);
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1.0;
gbc.weighty = 1;
gbc.ipady = 40;
gbc.gridx = 0;
gbc.anchor = GridBagConstraints.NORTH;
for (gbc.gridy = 0; gbc.gridy < 10; gbc.gridy++) {
JButton btn = new JButton("Button " + gbc.gridy);
layerListPanel.add(btn, gbc);
}
JScrollPane js = new JScrollPane(layerListPanel);
js.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
// ...
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 2;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
layerPanel.add(js, c);
return layerPanel;
}
Do you absolutely need a GridBagLayout?
I just made a demo using a simple Box.
And please have a look at How to write an SSCCE.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class YY extends JFrame {
static String[] args;
public YY() {
setSize(160, 200);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
int icnt= args.length==0 ? 5 : Integer.parseInt(args[0]);
Box box= Box.createVerticalBox();
for (int i=1; i<=icnt; i++) {
JButton btn= new JButton("Button "+i);
btn.setMaximumSize(new Dimension(150, 30));
box.add(btn);
}
JScrollPane scroll= new JScrollPane(box);
scroll.setPreferredSize(new Dimension(150, 100));
add(scroll);
setVisible(true);
}
public static void main(String... args) {
YY.args= args;
EventQueue.invokeLater(YY::new);
}
}
The below code initially displays a JFrame that contains a single JButton that displays the text Add. Each time you click the button a new JButton appears above it. The text on each newly created button is a three digit number with leading zeros that is incremented each time the Add button is clicked. And whenever a new button is added, the JFrame increases in height in order to display the newly added button.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class GridBttn implements ActionListener, Runnable {
private int counter;
private JFrame frame;
private JPanel gridPanel;
#Override
public void run() {
showGui();
}
#Override
public void actionPerformed(ActionEvent event) {
addButtonToGridPanel();
}
private void addButtonToGridPanel() {
JButton button = new JButton(String.format("%03d", counter++));
gridPanel.add(button);
frame.pack();
}
private JButton createButton(String text) {
JButton button = new JButton(text);
button.addActionListener(this);
return button;
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(createButton("Add"));
return buttonsPanel;
}
private JPanel createGridPanel() {
gridPanel = new JPanel(new GridLayout(0, 1));
return gridPanel;
}
private void showGui() {
frame = new JFrame("Grid");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(createGridPanel(), BorderLayout.CENTER);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new GridBttn());
}
}
Note the parameters to GridLayout constructor. Zero rows and one column. This means that whenever a Component is added to the JPanel it will be placed directly beneath the last Component added. In other words all the components added will appear in a single column. Also note that I call method pack() (of class JFrame) after adding a new button. This causes the JFrame to recalculate its size in order to display all the buttons.
EDIT
Due to OP's comment slightly modified above code so as to be more suitable to his requirements.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
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.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.WindowConstants;
public class GridBttn implements ActionListener, Runnable {
private int counter;
private JFrame frame;
private JPanel gridPanel;
private JPanel gridPanel2;
#Override
public void run() {
showGui();
}
#Override
public void actionPerformed(ActionEvent event) {
addButtonToGridPanel();
}
private void addButtonToGridPanel() {
JButton button = new JButton(String.format("%03d", counter++));
gridPanel2.add(button);
frame.pack();
}
private JButton createButton(String text) {
JButton button = new JButton(text);
button.addActionListener(this);
return button;
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(createButton("Add"));
return buttonsPanel;
}
private JPanel createMainPanel() {
gridPanel = new JPanel();
gridPanel.setPreferredSize(new Dimension(400, 300));
return gridPanel;
}
private JScrollPane createScrollPane() {
gridPanel2 = new JPanel();
BoxLayout layout = new BoxLayout(gridPanel2, BoxLayout.PAGE_AXIS);
gridPanel2.setLayout(layout);
JScrollPane scrollPane = new JScrollPane(gridPanel2,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setPreferredSize(new Dimension(70, 0));
return scrollPane;
}
private void showGui() {
frame = new JFrame("Grid");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.add(createScrollPane(), BorderLayout.LINE_END);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new GridBttn());
}
}

How to make scroll pane appear after dynamically adding text fields to a JPanel?

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);
}

What's the best way to organize JPanels in a list?

I've found myself writing up quite a few programs recently which all need to display some collection of data. So far the best looking approach I've thought of is make small JPanels which contain data on each item in the collection and put them all in a big JPanel which I then put in a JScrollPane. It works and looks just as intended but there's one issue: I can't seem to get the smaller JPanels to start at the top of the bigger JPanel.
The problem is only apparent when I've got a small number of small JPanels (green) added into the bigger JPanel (red).
Described below is the method I used to produce the above and I'd like to know if there's a better way I could do it (where the list starts at the top like it should):
I created a class which extends JPanel and in it add all data I want to display. We'll call it "SmallPanel.java". I don't set the size of it (that comes later).
In my main window's class (which extends JFrame):
private JScrollPane scrollPane;
private JPanel panel;
...
scrollPane = new JScrollPane();
getContentPane().add(scrollPane);
panel = new JPanel();
panel.setLayout(new GridBagLayout());
scrollPane.setViewportView(panel);
...
private void addPanel()
{
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = panel.getComponentCount(); //The new JPanel's place in the list
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.PAGE_START; //I thought this would do it
gbc.ipady = 130; //Set the panel's height, the width will get set to that of the container JPanel (which is what I want since I'd like my JFrames to be resizable)
gbc.insets = new Insets(2, 0, 2, 0); //Separation between JPanels in the list
gbc.weightx = 1.0;
SmallPanel smallPanel = new SmallPanel();
panel.add(smallPanel, gbc);
panel.revalidate();
panel.invalidate();
panel.repaint(); //Better safe than peeved
}
Call the addPanel() method every time I want to add a panel.
EDIT
Final solution (based on MadProgrammer's answer below):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.BevelBorder;
public class ListPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private JPanel fillerPanel;
private ArrayList<JPanel> panels;
public ListPanel(List<JPanel> panels, int height)
{
this(panels, height, new Insets(2, 0, 2, 0));
}
public ListPanel(List<JPanel> panels, int height, Insets insets)
{
this();
for (JPanel panel : panels)
addPanel(panel, height, insets);
}
public ListPanel()
{
super();
this.fillerPanel = new JPanel();
this.fillerPanel.setMinimumSize(new Dimension(0, 0));
this.panels = new ArrayList<JPanel>();
setLayout(new GridBagLayout());
}
public void addPanel(JPanel p, int height)
{
addPanel(p, height, new Insets(2, 0, 2, 0));
}
public void addPanel(JPanel p, int height, Insets insets)
{
super.remove(fillerPanel);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = getComponentCount();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.PAGE_START;
gbc.ipady = height;
gbc.insets = insets;
gbc.weightx = 1.0;
panels.add(p);
add(p, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = getComponentCount();
gbc.fill = GridBagConstraints.VERTICAL;
gbc.weighty = 1.0;
add(fillerPanel, gbc);
revalidate();
invalidate();
repaint();
}
public void removePanel(JPanel p)
{
removePanel(panels.indexOf(p));
}
public void removePanel(int i)
{
super.remove(i);
panels.remove(i);
revalidate();
invalidate();
repaint();
}
public ArrayList<JPanel> getPanels()
{
return this.panels;
}
public static void main(String[] args)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setMinimumSize(new Dimension(500, 500));
f.setLocationRelativeTo(null);
f.getContentPane().setLayout(new BorderLayout());
final ListPanel listPanel = new ListPanel();
for (int i = 1; i <= 10; i++)
listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
JButton btnAdd = new JButton("Add");
btnAdd.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent paramActionEvent)
{
listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
}
});
JButton btnRemove = new JButton("Remove");
btnRemove.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent paramActionEvent)
{
listPanel.removePanel(0);
}
});
f.getContentPane().add(btnRemove, BorderLayout.NORTH);
f.getContentPane().add(btnAdd, BorderLayout.SOUTH);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setViewportView(listPanel);
f.getContentPane().add(scrollPane, BorderLayout.CENTER);
f.setVisible(true);
}
public static JPanel getRandomJPanel()
{
JPanel panel = new JPanel();
panel.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
panel.add(new JLabel("This is a randomly sized JPanel"));
panel.setBackground(new Color(new Random().nextFloat(), new Random().nextFloat(), new Random().nextFloat()));
return panel;
}
}
The best solution I've found is to use VerticalLayout from the SwingLabs SwingX (which can be downloaded from here) libraries.
You "could" use a GridBagLayout with an invisible component positioned at the end, whose weighty property is set to 1, but this is a lot more additional work to manage, as you need to keep updating the x/y positions of all the components to keep it in place...
Updated with GridBagLayout example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class VerticalLayoutExample {
public static void main(String[] args) {
new VerticalLayoutExample();
}
public VerticalLayoutExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final TestPane pane = new TestPane();
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.addAnotherPane();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(pane));
frame.add(add, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel filler;
private int y = 0;
public TestPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
filler = new JPanel();
filler.setOpaque(false);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weighty = 1;
gbc.gridy = 0;
add(filler, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void addAnotherPane() {
JPanel panel = new JPanel(new GridBagLayout());
panel.add(new JLabel("Hello"));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = y++;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(4, 4, 4, 4);
add(panel, gbc);
GridBagLayout gbl = ((GridBagLayout)getLayout());
gbc = gbl.getConstraints(filler);
gbc.gridy = y++;
gbl.setConstraints(filler, gbc);
revalidate();
repaint();
}
}
}
This is just a concept. As camickr has pointed out, so long as you know the last component, you can adjust the GridBagConstraints of the component so that the last component which is in the list has the weighty of 1 instead...
As you can, you can override some of the things GridBagLayout does, for example, instead of using the preferred size of the panel, I've asked GridBagLayout to make it fill the HORIZONTAL width of the parent container...
You can use a vertical BoxLayout.
Just make sure the maximum size of the panel is equal to the preferred size so the panel doesn't grow.
Edit:
Since your class already has a custom panel all you need to do is override the getMaximumSize() method to return an appropriate value. Something like:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class VerticalLayoutExample2 {
public static void main(String[] args) {
new VerticalLayoutExample2();
}
public VerticalLayoutExample2() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final TestPane pane = new TestPane();
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.addAnotherPane();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(pane));
frame.add(add, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel filler;
private int y = 0;
public TestPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBorder( new EmptyBorder(4, 4, 4, 4) );
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void addAnotherPane() {
SmallPanel panel = new SmallPanel();
panel.setLayout( new GridBagLayout() );
panel.add(new JLabel("Hello"));
add(panel);
add(Box.createVerticalStrut(4));
revalidate();
repaint();
}
}
static class SmallPanel extends JPanel
{
#Override
public Dimension getMaximumSize()
{
Dimension preferred = super.getPreferredSize();
Dimension maximum = super.getMaximumSize();
maximum.height = preferred.height;
return maximum;
}
}
}
I know you mentioned you don't want to use a lib, but you can also look at Relative Layout. It is only a single class. It can easily mimic a BoxLayout but is easier to use because you don't need to override the getMaximumSize() method or add a Box component to the panel to give the vertical spacing.
You would set it as the layout of your panel as follow:
RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setFill( true ); // fills components horizontally
rl.setGap(4); // vertical gap between panels
yourPanel.setLayout(rl);
yourPanel.add( new SmallPanel(...) );
yourPanel.add( new SmallPanel(...) );

JScrollPane does not appear when using it on a JPanel

I have been trying for hours to find a way to solve the issue, but I had no luck with that. Here is a sample code:
import java.awt.BorderLayout;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class Example extends JFrame
{
private static final long serialVersionUID = 1L;
public Example()
{
JPanel contentPane = (JPanel) getContentPane();
contentPane.setLayout(new BorderLayout());
JPanel panTop = new JPanel(new BorderLayout());
//JPanel panBottom = new JPanel(new BorderLayout());
JPanel panTopCenter = new JPanel();
//JPanel panTopLeft = new JPanel();
//JPanel panTopRight = new JPanel();
panTop.add(panTopCenter, BorderLayout.CENTER);
//panTop.add(panTopLeft, BorderLayout.WEST);
//panTop.add(panTopRight, BorderLayout.EAST);
contentPane.add(panTop, BorderLayout.CENTER);
//contentPane.add(panBottom, BorderLayout.SOUTH);
JPanel pan = new JPanel();
pan.setLayout(new BoxLayout(pan, BoxLayout.Y_AXIS));
for(int i = 0; i < 50; i++) pan.add(new JButton("Button " + i));
JScrollPane scrollPane = new JScrollPane(pan);
panTopCenter.add(scrollPane);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new Example();
}
});
}
}
Snapshot:
I always have to set the viewport's preferred size like this.
import java.awt.*;
import javax.swing.*;
public class Example extends JFrame {
public Example() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
Box box = new Box(BoxLayout.Y_AXIS);
for (int i = 0; i < 50; i++) {
box.add(new JButton("Button " + i));
}
JScrollPane sp = new JScrollPane(box);
Dimension d = new Dimension(box.getComponent(0).getPreferredSize());
sp.getVerticalScrollBar().setUnitIncrement(d.height);
d.height *= 10; // Show at least 10 buttons
sp.getViewport().setPreferredSize(d);
add(sp, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Example e = new Example();
}
});
}
}
Don't set any sizes! The scroll-bar appears if this change is made.
JPanel panTopCenter = new JPanel(new GridLayout());
The basic problem is that FlowLayout will show components at the smallest size needed to display it, and for a scroll-pane, that is (decided to be) 0x0. By using a GridLayout with no arguments in the constructor and adding the scroll-pane as the only component, it will be forced to fill the available space.
You have to set the preferred-size, in the case that JScrollPane is single JComponent in the container or top-level container.
scrollPane.setPreferredSize(new Dimension(100,500));
Better would be to use GridLayout for the same type of JComponents.
The best solution, quick and easy, is using JXPanel from SwingX, which is quasi-standard and implements Scrollable.

remove borders inside JScrollPane

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());

Categories

Resources