I have a JPanel with a GridBagLayout inside of a JScrollPane. I also have an 'add' button within the JPanel which, when clicked, will be removed from the JPanel, adds a new instance of a separate component to the JPanel, then adds itself back to the JPanel. This sort of makes a growing list of components, followed by the 'add' button.
Adding new components works fine, the JPanel stretches to accommodate the new components, and the JScrollPane behaves as expected, allowing you to scroll through the entire length of the JPanel.
This is how the add works:
jPanel.remove(addButton);
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = GridBagConstraints.RELATIVE;
jPanel.add(new MyComponent(), c);
jPanel.add(addButton, c);
jPanel.validate();
jPanel.repaint();`
Removal works by clicking a button inside the added components themselves. They remove themselves from the JPanel just fine. However, the JPanel keeps it's stretched-out size, re-centering the list of components.
This is how removal works:
Container parent = myComponent.getParent();
parent.remove(myComponent);
parent.validate();
parent.repaint();`
The question is, why does my GridBagLayout JPanel resize when adding components, but not when removing components?
You have to revalidate and repaint the JScrollPane, here is an example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SwingTest {
public static void main(String[] args) {
final JPanel panel = new JPanel(new GridBagLayout());
for (int i = 0; i < 25; i++) {
JTextField field = new JTextField("Field " + i, 20);
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridy = i;
panel.add(field, constraints);
}
final JScrollPane scrollPane = new JScrollPane(panel);
JButton removeButton = new JButton("Remove Field");
removeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (panel.getComponentCount() >= 1) {
panel.remove(panel.getComponentCount() - 1);
scrollPane.revalidate();
scrollPane.repaint();
}
}
});
JFrame frame = new JFrame("Swing Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setSize(640, 480);
frame.setLocation(200, 200);
frame.getContentPane().add(scrollPane);
frame.getContentPane().add(removeButton, BorderLayout.SOUTH);
frame.setVisible(true);
}
}
Related
I'm aware there are several questions with the same title, and I've tried their answers but to no avail.
I'm getting the following result with my code:
It does not scroll as it should, and there's this empty little space to the right.
Here is the main code for the frame, mainpanel, list panel and the buttons panel that are inside the mainpanel.
public Tester2() {
JPanel mainPanel = new JPanel(); /////// MAIN PANEL
mainPanel.setLayout(new BorderLayout());
mainPanel.setPreferredSize(new Dimension(200, 400));
JPanel upperListPnl = new JPanel(); ////// LIST PANEL
upperPnlSetup(upperListPnl);
JPanel lowerBtnsPnl = new JPanel(); ///// BUTTONS PANEL
lowerBtnsPnl.setLayout(new BorderLayout());
JButton testButton = new JButton("Test the list");
testButton.addActionListener(e -> {
exampleModel.addElement("List has been tested");
updateExampleData();
});
lowerBtnsPnl.add(testButton);
mainPanel.add(upperListPnl, BorderLayout.CENTER);
mainPanel.add(lowerBtnsPnl, BorderLayout.SOUTH);
JFrame frame = new JFrame();
frame.add(mainPanel);
frame.setVisible(true);
frame.pack();
frame.setLocationRelativeTo(null);
}
Here is the code for the JList and the JScrollpane (upper panel):
public void upperPnlSetup(JPanel panel) {
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
exampleList = new JList<>(exampleModel);
JScrollPane jsp = new JScrollPane(exampleList, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
c.fill = GridBagConstraints.BOTH;
c.weighty = 1;
c.weightx = 0.75;
panel.add(exampleList, c);
c.weightx = 0.25;
panel.add(jsp, c);
}
And then the JList and model list as well as the data updating method:
private JList<String> exampleList;
private DefaultListModel<String> exampleModel = new DefaultListModel<>();
public void updateExampleData() {
exampleList.setModel(exampleModel);
}
I tried a FloatLayout, a BorderLayout, and a GridLayout (0,2 and 0,1) all of which didn't work. Finally, I settled for the GridBagLayout since it's seemingly always used for JLists and/or JScrollpanes, and I played with the GridBagConstraints as well as the positioning of the code but seem to always land on the same problem. I've tried giving the scroll pane a preferredSize, didn't do anything.
Okay so, apparently the problem was that I was adding the list to the panel. Adding it to the scrollpane, then adding the scrollpane to the panel was enough, so commenting out the
panel.add(exampleList, c);`
fixed the whole thing.
I am new to Java and currently trying to learn it. I am working on a simple program to display a number of buttons on a frame. I also want to make the panel scroll vertically but instead it scrolls horizontally.
Here is my code so far:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class GridView {
public static void main(String[] args) {
// TODO Auto-generated method stub
JFrame frame = new JFrame("Display Buttons");
frame.setBounds(30, 30, 300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GridLayout grid = new GridLayout(3, 4, 30, 20);
Container content = frame.getContentPane();
content.setLayout(grid);
JPanel panel = new JPanel();
JButton button = null;
for (int i = 1; i <= 100; i++) {
panel.add(button = new JButton(" Press " + i));
}
content.add(panel);
frame.add(panel, BorderLayout.NORTH);
frame.add(new JScrollPane(panel), BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
Can anyone tell me why my scroll is shown horizontally and how to fix it? Any kind of response will be much appreciated.
EDIT:
I am sorry. My question was incomplete. I want to make it something like this, but with vertical scroll.
//GridLayout grid = new GridLayout(3, 4, 30, 20);
GridLayout grid = new GridLayout(0, 4, 30, 20);
If you want columns of component then don't specify the rows value in the layout manager. Just specify the columns and the components will wrap when required
//content.setLayout(grid);
//JPanel panel = new JPanel();
JPanel panel = new JPanel(grid);
You add the buttons to the panel so you need to set the layout manager of the panel. Otherwise the JPanel will use the default FlowLayout, which display all the buttons on a single row.
It shows horizontally because the buttons are added horizontally so the width of the panel exceeds the view port.
If you added the buttons vertically, by using a different layout manager, the scroll pane would show vertically. For example:
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); //or setLayout(grid); if you meant to use tge gridlayout for the buttons
/*JButton button = null;*/ //never used
for (int i = 1; i <= 100; i++) {
panel.add(new JButton(" Press " + i));
}
Edit to answer the edited question. See comments:
public static void main(String[] args) {
JFrame frame = new JFrame("Display Buttons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GridLayout grid = new GridLayout(0, 4, 30, 20);
JPanel panel = new JPanel(grid);
for (int i = 1; i <= 100; i++) {
panel.add(new JButton(" Press " + i));
}
JScrollPane sp = new JScrollPane(panel);
//by default scrollpane will appear as needed, vertically AND horizontally
//to prevent it from showing horizontally :
sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
frame.add(sp, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
I am having issue where my JPanel is not setting up the size. I am not sure if is something to do with my JTab or JFrame. I am using GridBagLayout layout management. And for some reason are not able to set the size.
Here is a dummy code, following the same logic to my original source code:
FirstPanel.java
import javax.swing.*;
import java.awt.*;
public class FirstPanel extends JPanel {
private JLabel label1 = new JLabel("Label 1");
private JTextField textField1 = new JTextField();
private GridBagConstraints c = new GridBagConstraints();
public FirstPanel() {
//Size is not overriding
Dimension size = getPreferredSize();
size.width = 100;
setPreferredSize(size);
setBorder(BorderFactory.createTitleBorder("Border Title");
setLayout(new GridBagLayout());
addComponents();
}
private void addComponents() {
c.gridx = 0;
c.gridy = 0;
c.anchor = GridBagConstraints.NORTHWEST;
c.insets = new Insets(5, 0, 0, 0);
add(label1, c);
c.gridx = 1;
add(textField1, c);
c.weightx = 1;
c.weighty = 1;
add(new JLabel(""), c);
}
}
MainPanel.java
import javax.swing.*;
import java.awt.*;
public class MainPanel {
private JFrame frame = new JFrame("App");
private JPanel panel1 = new JPanel(new GridBagLayout());
private GridBagConstraints c = new GridBagConstraints();
private JTabbedPane tabPane = new JTabbedPane();
public MainPanel() {
addComponents();
frame.add(tabPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 350);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
}
private void addComponents() {
tabPane.addTab("Tab 1", new FirstPanel());
}
}
Main.java
public class Main {
public static void main(String[] args) {
new MainPanel();
}
}
Or at least have two JPanels,
Exactly.
Frist you create a main panel using a BorderLayout that you add to the tabbed pane.
Then you have a second panel for your labels and text fields (using whatever layout manager you want). Then you add this panel to the BorderLayout.LINE_START.
Then you add your scrollpane containing the JTable to the BorderLayout.CENTER of the main panel.
Read the tutorial on Layout Manager. Nest panels with different layout managers as required.
want to have JTable taking 50% of the other side.
Picking a random number like 50% is not the way to design a GUI. What happens if the frame is made smaller/larger. What happens to the space? Design the layout with flexibility in mind, just like your browser window is designed. There are always fixed areas where the size is determined by the components added and there is a flexible area that grows/shrinks as desired.
I have a JPanel(named mainJP) which has a few buttons and labels (uses BorderLayout). Next it adds another JPanel (named JP1) and inside it a ScrollPane with a JTable. I want to be able to resize JP2 and in turn all its child components (ScrollPane and JTable). So that I can see few more rows of the JTable without having to scroll. Also inorder to resize JP1, other siblings of JP1 should adjust themselves. Not sure how to achieve that.
As the image shows I already have a few features implemented - to entirely delete JP1, to expand/collapse JP1 view, to delete and add rows in the JTable.
So basically I want to be able to drag the mouse at bottom border of JP1 to vertically increase the size of JP1 and its child components (ScrollPane and JTable).
As described in a few of the below solutions, I am still confused at which level should I incorporate a JSpiltPane - as it allows only adding 2 components. I think all the JP1 should be in the JSplitPane. However there can be more than one JP1 components and they are dynamically added.
To add extra components to the JSplitPane is easy. Put a JPanel in each pane you want to show your components, then add the components to this panel -- you can customize the layout as needed.
Something like this will put a JSplitPane in an already create JFrame, add a JPanel to each Pane, and then add some JLabels to the left side, a JTextField to the right. The splitpane will expand to the size of the JFrame it's in.
JSplitPane splitPane = new JSplitPane();
JPanel leftPanel = new JPanel();
JPanel rightPanel = new JPanel();
JLabel label1 = new JLabel();
JLabel label2 = new JLabel();
JTextField textField = new JTextField();
GridBagConstraints gBC = new GridBagConstraints();
getContentPane().setLayout(new GridBagLayout());
leftPanel.setLayout(new GridBagLayout());
rightPanel.setLayout(new GridBagLayout());
// I'm not going to bother doing any layout of the label or textfield here
leftPanel.add(label1, new GridBagConstraints());
leftPanel.add(label2, new GridBagConstraints());
rightPanel.add(textField, new GridBagConstraints());
splitPane.setLeftComponent(leftPanel);
splitPane.setRightComponent(rightPanel);
gBC.fill = GridBagConstraints.BOTH;
gBC.weightx = 1.0;
gBC.weighty = 1.0;
getContentPane().add(splitPane, gBC);
pack();
These are two different problems. Resizing a JPanel on a mouse drag -- the easiest way for you is going to be to use two nested JSplitPanes. You would use one so you can drag horizontally, and another to drag vertically.
Alternatively, if you don't want split panes, you can try something like this method and create a custom JPanel. The borders are there to make the effect more visible. I personally don't like it as it's overly complicated.
public class ResizablePanel extends JPanel {
private boolean drag = false;
private Point dragLocation = new Point();
public ResizablePanel() {
setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
setPreferredSize(new Dimension(500, 500));
final JFrame f = new JFrame("Test");
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
drag = true;
dragLocation = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
drag = false;
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
if (drag) {
if (dragLocation.getX()> getWidth()-10 && dragLocation.getY() > getHeight()-10) {
System.err.println("in");
setSize((int)(getWidth()+(e.getPoint().getX()-dragLocation.getX())),
(int)(getHeight()+(e.getPoint().getY()-dragLocation.getY())));
dragLocation = e.getPoint();
}
}
}
});
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(this,BorderLayout.CENTER);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
new ResizablePanel();
}
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
As for the children resizing with it's parent, that one is pretty easy with GridBagLayout.
The important parts to remember in the GridBagConstraints are the fill, weightx and weighty.
You can just do something like this in your frame -- it will put a scrollpane with a JTable in it that will resize with the frame:
JScrollPane jScrollPane1 = new JScrollPane();
JTable jTable1 = new JTable();
GridBagConstraints gBC = new GridBagConstraints();
getContentPane().setLayout(new GridBagLayout());
jScrollPane1.setViewPortView(jTable1);
gBC.fill = GridBagConstraints.BOTH;
gBC.weightx = 1.0;
gBC.weighty = 1.0;
getContentPane().add(jScrollPane1, gBC);
My question is similar to this one (How to get JScrollPanes within a JScrollPane to follow parent's resizing), but that question wasn't clear and the answer there didn't help me..
I have this SSCCE (using MigLayout):
public static final int pref_height = 500;
public static void main(String[] args) {
JPanel innerPanel = new JPanel(new MigLayout());
innerPanel.setBorder(new LineBorder(Color.YELLOW, 5));
for(int i = 0; i < 15; i++) {
JTextArea textArea = new JTextArea();
textArea.setColumns(20);
textArea.setRows(5);
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
JScrollPane jsp = new JScrollPane(textArea);
innerPanel.add(new JLabel("Notes" + i));
innerPanel.add(jsp, "span, grow");
}
JScrollPane jsp = new JScrollPane(innerPanel) {
#Override
public Dimension getPreferredSize() {
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
Dimension dim = new Dimension(super.getPreferredSize().width + getVerticalScrollBar().getSize().width, pref_height);
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
return dim;
}
};
jsp.setBorder(new LineBorder(Color.green, 5));
JPanel outerPanel = new JPanel();
outerPanel.setBorder(new LineBorder(Color.RED, 5));
outerPanel.add(jsp);
JFrame frame = new JFrame();
JDesktopPane jdp = new JDesktopPane();
frame.add(jdp);
jdp.setPreferredSize(new Dimension(800, 600));
frame.pack();
JInternalFrame jif = new JInternalFrame("Title", true, true, true, true);
jif.pack();
jif.add(outerPanel);
jdp.add(jif);
jif.pack();
jif.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
I want the JScrollPane to resize whenever the parent JPanel is resized. Basically, I want the green border to line up with the red border. Right now, the green border stays the same size no matter the red border (unless you resize too small).
JPanel outerPanel = new JPanel();
A JPanel uses a FlowLayout by default which always respects the size of the component added to it. As a guess, maybe you can use:
JPanel outerPanel = new JPanel( new BorderLayout() );
A BorderLayout give all the space available to the component added to the panel. By default a JInternalFrame also uses a BorderLayout. So since all the parent components of your scroll pane use a BorderLayout all the space should go to the scroll pane.
When you post a SSCCE you should post code using classes from the JDK that simulates your problem so that everybody can test your SSCCE.
I noticed this did not have an answer that uses the original layout so here is one.
In order to make the JScrollPane resize when the parent JPanel is resized you need to do two things.
1) Set the layout of the panel to grow. This can be using the following code.
new MigLayout("", //Layout Constraints
"grow", //Column Constraints
"grow"); //Row Constraints
2) Set the component to grow. This is as simple as adding an extra argument in the add() function.
add(jsp, "grow");
ExtraIn order to make the JTextArea column grow when you resize the JScrollPane you can change the layout to only make the second column change. For example
new MigLayout("", //Layout Constraints
"[/*Column 1*/][grow /*Column 2*/]", //Column Constraints
""); //Row Constraints
Also, I would recommend you use wrap instead of span to use the next row as span refers using so many columns. For example span 2 //Means use 2 columns for this component. This would mean when you add your jsp to innerPanel it would become
innerPanel.add(jsp, "wrap, grow");
Edited SSSCE
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import net.miginfocom.swing.MigLayout;
public class JSPR extends JFrame {
public static final int pref_height = 500;
public static void main(String[] args) {
JPanel innerPanel = new JPanel(new MigLayout("", "[][grow]", ""));
innerPanel.setBorder(new LineBorder(Color.YELLOW, 5));
for(int i = 0; i < 15; i++) {
JTextArea textArea = new JTextArea();
textArea.setColumns(20);
textArea.setRows(5);
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
JScrollPane jsp = new JScrollPane(textArea);
innerPanel.add(new JLabel("Notes" + i));
innerPanel.add(jsp, "wrap, grow");
}
JScrollPane jsp = new JScrollPane(innerPanel) {
#Override
public Dimension getPreferredSize() {
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
Dimension dim = new Dimension(super.getPreferredSize().width + getVerticalScrollBar().getSize().width, pref_height);
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
return dim;
}
};
jsp.setBorder(new LineBorder(Color.green, 5));
JPanel outerPanel = new JPanel(new MigLayout("", "grow", "grow"));
outerPanel.setBorder(new LineBorder(Color.RED, 5));
outerPanel.add(jsp, "grow");
JFrame frame = new JFrame();
JDesktopPane jdp = new JDesktopPane();
frame.add(jdp);
jdp.setPreferredSize(new Dimension(800, 600));
frame.pack();
JInternalFrame jif = new JInternalFrame("Title", true, true, true, true);
jif.pack();
jif.add(outerPanel);
jdp.add(jif);
jif.pack();
jif.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}