I have a class that extends a JPanel called Row. I have a bunch of Row added to a JLabel, the code is the following:
JFrame f=new JFrame();
JPanel rowPanel = new JPanel();
//southReviewPanel.setPreferredSize(new Dimension(400,130));
rowPanel.setLayout(new BoxLayout(rowPanel, BoxLayout.Y_AXIS));
rowPanel.add(test1);
rowPanel.add(test1);
rowPanel.add(test2);
rowPanel.add(test3);
rowPanel.add(test4);
rowPanel.setPreferredSize(new Dimension(600, 400));
rowPanel.setMaximumSize(rowPanel.getPreferredSize());
rowPanel.setMinimumSize(rowPanel.getPreferredSize());
f.setSize(new Dimension(300,600));
JScrollPane sp = new JScrollPane(rowPanel);
sp.setSize(new Dimension(300,600));
f.add(sp);
f.setVisible(true);
where test1...etc is a Row. However when I resize the window the layout of the Row somehow becomes messy (it resizes as well)... how can I prevent this from happening?
Read the Swing tutorial on Using Layout Managers. Each layout manager has its own rules about what happens when the container is resized. Experiment and play.
In the case of a BoxLayout it should respect the maximum size of the components added to the panel so you can do:
childPanel.setMaximumSize( childPanel.getPreferredSize() );
If you need more help post your SSCCE demonstrating the problem.
I took the code in http://download.oracle.com/javase/tutorial/uiswing/examples/layout/BoxLayoutDemoProject/src/layout/BoxLayoutDemo.java and adapted it with what you are trying to do, only using buttons instead of custom JPanels:
public class BoxLayoutDemo {
public static void addComponentsToPane(Container pane) {
JPanel rowPanel = new JPanel();
pane.add(rowPanel);
rowPanel.setLayout(new BoxLayout(rowPanel, BoxLayout.Y_AXIS));
rowPanel.add(addAButton("Button 1"));
rowPanel.add(addAButton("Button 2"));
rowPanel.add(addAButton("Button 3"));
rowPanel.add(addAButton("Button 4"));
rowPanel.add(addAButton("5"));
rowPanel.setPreferredSize(new Dimension(600, 400));
rowPanel.setMaximumSize(rowPanel.getPreferredSize());
rowPanel.setMinimumSize(rowPanel.getPreferredSize());
}
private static JButton addAButton(String text) {
JButton button = new JButton(text);
button.setAlignmentX(Component.CENTER_ALIGNMENT);
return button;
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("BoxLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set up the content pane.
addComponentsToPane(frame.getContentPane());
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
The end result is this:
As you can see, the button row is perfectly aligned. If you resize the JFrame, they stay aligned. Is that what you are looking for?
Related
Is there a way such that a component that is added to the BorderLayout.PAGE_START area will become scrollable if (and only if) there's not enough space?
I have attached a minimal example. If you resize the complete frame the label in the center will become scrollable, the label at the top won't.
Unfortunately, I can't change the BorderLayout.PAGE_START positioning, as this is given by a framework. However I do have full control over the creation of myComponent.
public static void main(String[] args){
JPanel panel = new JPanel(new BorderLayout());
JComponent myComponent = new JScrollPane(new JLabel("<html>START-START<br><br>START-START</html>"));
panel.add(myComponent, BorderLayout.PAGE_START);
panel.add(new JScrollPane(new JLabel("<html>CENTER-CENTER<br><br>CENTER-CENTER</html>")), BorderLayout.CENTER);
final JFrame mainframe = new JFrame("Test");
mainframe.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainframe.getContentPane().add(panel);
javax.swing.SwingUtilities.invokeLater(() -> {
mainframe.pack();
mainframe.setVisible(true);});
}
Is there a way such that a component that is added to the BorderLayout.PAGE_START area will become scrollable if there's not enough space?
Yes there is. Make its preferred height less than its actual height. BorderLayout respects the preferred height of the component contained at PAGE_START, so it doesn't matter how many lines you put in your JLabel, it will be displayed at its preferred height – without scrollbars.
Try the following.
public static void main(String[] args) {
JPanel panel = new JPanel(new BorderLayout());
JComponent myComponent = new JScrollPane(new JLabel("<html>START-START<br><br>START-START</html>"));
myComponent.setPreferredSize(new Dimension(100, 20));
panel.add(myComponent, BorderLayout.PAGE_START);
panel.add(new JScrollPane(new JLabel("<html>CENTER-CENTER<br><br>CENTER-CENTER</html>")),
BorderLayout.CENTER);
final JFrame mainframe = new JFrame("Test");
mainframe.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainframe.getContentPane().add(panel);
javax.swing.SwingUtilities.invokeLater(() -> {
mainframe.pack();
mainframe.setVisible(true);
});
}
EDIT
The below code doesn't give the best result but I think it is the way to go. You just need to play around with the different sizes of the different components.
I added a ComponentListener to the content pane of the JFrame. When the JFrame is resized, you need to recalculate the heights of the top component and the center component according to your needs and then update the relevant component sizes. Note that the below code is not a complete solution but hopefully enough to allow you to arrive at a complete solution.
public static void main(String[] args) {
final JFrame mainframe = new JFrame("Test");
JLabel topLabel = new JLabel("<html>START-START<br><br>START-START");
JScrollPane topPane = new JScrollPane(topLabel);
mainframe.add(topPane, BorderLayout.PAGE_START);
JLabel centerLabel = new JLabel("<html>CENTER-CENTER<br><br>CENTER-CENTER");
centerLabel.setMinimumSize(centerLabel.getPreferredSize());
JPanel centerPane = new JPanel();
centerPane.add(centerLabel);
mainframe.add(centerPane, BorderLayout.CENTER);
javax.swing.SwingUtilities.invokeLater(() -> {
mainframe.pack();
mainframe.setVisible(true);
final Dimension centerPaneDim = centerPane.getPreferredSize();
final Dimension topLabelDim = topLabel.getPreferredSize();
mainframe.getContentPane().addComponentListener(new ComponentListener() {
#Override
public void componentShown(ComponentEvent e) {
// Do nothing.
}
#Override
public void componentResized(ComponentEvent e) {
Dimension size = e.getComponent().getSize();
if (size.height < centerPaneDim.height + topLabelDim.height + 10) {
topPane.setPreferredSize(new Dimension(topLabelDim.width, 10 + size.height - centerPaneDim.height));
}
else {
topPane.setPreferredSize(new Dimension(topLabelDim.width, topLabelDim.height + 10));
}
e.getComponent().revalidate();
e.getComponent().repaint();
}
#Override
public void componentMoved(ComponentEvent e) {
// Do nothing.
}
#Override
public void componentHidden(ComponentEvent e) {
// Do nothing.
}
});
});
}
I'm trying to get a JTextArea with a "save" JButton centered underneath it, maybe with a small bit of padding between the components as well as the components to the frame if possible. I've tried messing around with layout managers, panels, etc. and can't seem to get the result i want. Just looking for the simplest way to do this. Thanks.
Suggestions:
The overall layout of the GUI container could be BorderLayout.
Add the JScrollPane that holds your JTextArea BorderLayout.CENTER.
Create a JPanel just to hold the JButton and don't give it a specific layout manager. It will now use JPanel's default FlowLayout and will center components in the horizontal direction.
Add your JButton to this last JPanel.
Add that same JPanel to the GUI in the BorderLayout.PAGE_END (bottom) position.
For example:
import java.awt.BorderLayout;
import javax.swing.*;
public class SimpleLayout extends JPanel {
private static final int ROWS = 20;
private static final int COLS = 60;
private JTextArea textArea = new JTextArea(ROWS, COLS);
private JButton button = new JButton("Button");
public SimpleLayout() {
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
setLayout(new BorderLayout());
add(new JScrollPane(textArea), BorderLayout.CENTER);
add(buttonPanel, BorderLayout.PAGE_END);
}
private static void createAndShowGui() {
SimpleLayout mainPanel = new SimpleLayout();
JFrame frame = new JFrame("SimpleLayout");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
I have a panel with layout (for example, BorderLayout) and a JScrollPane on its center. JScrollPane has content inside it (a JPanel)
The thing is, that when this JScrollPane resizes, I do not want its content to resize. For example, if layout increases the JScrollPane, I want its content to be as small it was (and occupy only part of the pane), but it resizes to fit pane.
I also need an opportunity to reduce content inside the pane and increase it manually (there is no problems with increasing, they are in reducing).
So, how can I achieve content size independency? Of course, I need to save scrolling features, if content will be bigger than JScrollPane.
Here is a simple example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TestScroll extends JFrame {
public TestScroll() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(500, 500);
init();
}
private void init() {
setLayout(new BorderLayout());
// Inner panel
final JPanel innerPanel = new JPanel();
innerPanel.setOpaque(true);
innerPanel.setBackground(Color.BLUE);
innerPanel.setPreferredSize(new Dimension(200, 200));
// Scroll
final JScrollPane scrollPane = new JScrollPane(innerPanel);
add(scrollPane, BorderLayout.CENTER);
// Buttons
JPanel buttonPanel = new JPanel();
JButton extendButton = new JButton("Extend inner panel");
extendButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
innerPanel.setPreferredSize(new Dimension(innerPanel.getWidth() * 2,
innerPanel.getHeight() * 2));
innerPanel.revalidate();
}
});
buttonPanel.add(extendButton);
JButton reduceButton = new JButton("Reduce inner panel");
reduceButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
innerPanel.setPreferredSize(new Dimension(innerPanel.getWidth() / 2,
innerPanel.getHeight() / 2));
innerPanel.revalidate();
}
});
buttonPanel.add(reduceButton);
add(buttonPanel, BorderLayout.NORTH);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new TestScroll();
}
});
}
}
So, problem statement:
I do not want inner panel to stretch to the pane (but pane can be resized by outer layout, so the panel must just keep its size).
I want to be able to reduce inner panel manually so it can occupy only a part of the scroll pane.
And, of course, I want to save scrolling functionality when inner panel is larger than scroll pane.
Did you follow Andrew's link about using layout managers to achieve your goal?
Here is another simple example:
//final JScrollPane scrollPane = new JScrollPane(innerPanel);
JPanel outer = new JPanel();
outer.add( innerPanel );
final JScrollPane scrollPane = new JScrollPane(outer);
Could you tell me please, why components like JPanel etc. are not visible when added to a JFrame? Here is my code:
public class GUI{
static JPanel panel = new JPanel();
private void createAndShowGUI() {
final ImageIcon zielonaikona = new ImageIcon("kulazielona.png");
JFrame frame1 = new JFrame("MasterMind");
JRadioButton zielony = new JRadioButton(zielonaikona);
zielony.setSelected(true);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton akceptuj = new JButton("Akceptuj");
akceptuj.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
JLabel label2 = new JLabel(zielonaikona);
panel.add(label2);
}
});
BoxLayout layout = new BoxLayout(panel, BoxLayout.Y_AXIS);
panel.add(akceptuj);
panel.setLayout(layout);
panel.add(zielony);
JLabel label = new JLabel (zielonaikona);
panel.add(label);
frame1.getContentPane().add(panel);
frame1.getContentPane().add(akceptuj);
frame1.getContentPane().add(zielony);
frame1.setSize(200, 300);
frame1.setVisible(true);
}
public static void main(String[] args) {
GUI kk = new GUI();
kk.createAndShowGUI();
}
}
You add your controls to the JFrame as well as the JPanel panel, so they will only appear in the last container to which they were added, namely the frame. Also because you add them in the default BorderLayout.CENTER position each one displaces the last so you are only left with one component displayed (the JRadioButton zielony)
To fix, remove the lines:
frame1.getContentPane().add(akceptuj);
frame1.getContentPane().add(zielony);
Aside: When adding new components on the fly (i.e. the JLabel added in the ActionListener), don't forget to call:
panel.revalidate();
panel.repaint();
The button and the radio button are added twice, to the panel and to the frame. You didn't set the layout on the frame but I think it has a default one. I just don't remember what kind.
Here is your code that is wrong.
panel.add(akceptuj);
panel.add(zielony);
and
frame1.getContentPane().add(akceptuj);
frame1.getContentPane().add(zielony);
I am trying to implement JTabbedPane. In the following code I have presented a case very similar to what I want to implement. I have created a tab by adding a JPanel to the JTabbedPane. I have added a JButton and JScrollPane to the JPanel. On click of the JButton I want to add a new JPanel having some JRadioButtons to the JScrollPane. But these are not shown even after refreshing the JScrollPane or main JPanel. Please help. The code is given below.
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Test {
static JFrame frame;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
//Create and set up the window.
frame = new JFrame("DynamicTreeDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTabbedPane tp = new JTabbedPane();
final JScrollPane jsp = new JScrollPane();
JPanel jp = new JPanel();
JButton jb = new JButton("Refresh");
jb.setActionCommand("Show");
jb.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equalsIgnoreCase("Show")){
JRadioButton jrb1 = new JRadioButton("First Option");
JRadioButton jrb2 = new JRadioButton("Second Option");
JRadioButton jrb3 = new JRadioButton("Third Option");
ButtonGroup bg = new ButtonGroup();
bg.add(jrb1);
bg.add(jrb2);
bg.add(jrb3);
JPanel p = new JPanel(new GridLayout(0,1));
p.add(jrb1);
p.add(jrb2);
p.add(jrb3);
jsp.add(p);
jsp.revalidate();
jsp.repaint();
}
}
});
jp.setLayout(new GridLayout(0,1));
jp.add(jb);
jp.add(jsp);
tp.add("First Tab", jp);
frame.getContentPane().add(tp);
//Display the window.
frame.pack();
frame.setVisible(true);
}
}
To add something to JScrollPane use its JViewport rather than directly calling add(). In your example replace:
jsp.add(p);
with:
jsp.getViewport().add(p);
Alternatively, initialize JScrollPane with a JPanel that holds other components. Based on your example:
final JPanel panel = new JPanel();
final JScrollPane jsp = new JScrollPane(panel);
panel.add(new JRadioButton("First Option"));
panel.add(new JRadioButton("Second Option"));
panel.add(new JRadioButton("Third Option"));
See How to Use Scroll Panes for more details.
The components should be added to the JPanel called jp rather than directly to the scroll pane.
JScrollPane only works with a single "View". You cannot add components to the scrollPane. If you want, you can change the "View" using setViewPortView(). To achieve the behaviour you are looking for, do the following:
JPanel centralView = new JPanel();
// possibly configure that central view with appropriate layout and other stuffs
JScrollPane jsp = new JScrollPane(centralView);
...
// Now you can add your components to centralView instead of your jsp.add(...) calls.
You should add the JPanel to the JScollPanes viewport using getViewport(), then repack the JFrame to get the sizing issue sorted using pack();:
jsp.getViewport().add(p);
frame.pack();
instead of:
jsp.add(p);
jsp.revalidate();
jsp.repaint();