Best practice to resize JScrollPane - java

I read some answered questions in this forum (this one for example) where it is strictly recommended to avoid the use of setXXXSize() methods to resize components in swing applications.
So, coming to my problem, i would like to know how to best resize a JScrollPane in order to avoid its parent panel to increase its size without any control.
Before writing some code, i want to describe the real situation, since i will post a "toy example".
In my JFrame i'm currently using a border layout for my content pane. At BorderLayout.CENTER there is a JPanel where i do some custom painting.
At BorderLayout.EAST there is a JPanel (say eastPanel) containing some components inside another panel (this panel will be added to eastPanel at BorderLayout.NORTH), and a JScrollPane which contains a JTable (added to eastPanel at BorderLayout.CENTER). This table will have a lot of rows.
Since i want eastPanel's height to be the same as centerPanel's height, i need some way to avoid the JScrollPane to increase its size in order to try to display as much rows as possible.
For now i wasn't be able to find another solution apart from calling setPreferredSize on the eastPanel containing the scrollpane, but i have to admit that i hate this kind of solution.
Sample Code
In this code sample i added some random labels at the north of eastPanel and inside the JScrollPane, since my purpose was to post a short sample of code.
However, the situation is very similar to the one i have described above.
I wasn't be able to solve my problem without using this "terrible" line of code :
eastPanel.setPreferredSize(new Dimension(eastPanel.getPreferredSize().width, centerPanel.getPreferredSize().height));
I would like to avoid a more complex layout for a simple situation like this. Am i missing something ? Also, is setting that empty border an acceptable way to set the size of the panel where i will do some custom painting?
Code :
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class Test
{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new TestFrame().setVisible(true);
}
catch(Exception exception) {
JOptionPane.showMessageDialog(null, "Fatal error while initialiing application", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
}
}
class TestFrame extends JFrame
{
public TestFrame() {
super("Test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel pane = new JPanel(new BorderLayout(20, 0));
pane.setBorder(new EmptyBorder(20, 20, 20, 20));
JPanel centerPanel = new JPanel();
centerPanel.setBackground(Color.WHITE);
centerPanel.setBorder(new EmptyBorder(400, 400, 0, 0));
// centerPanel.setPreferredSize(new Dimension(400, 400));
JPanel eastPanel = new JPanel(new BorderLayout(0, 20));
JPanel labelsContainer = new JPanel(new GridLayout(0, 1));
for(int i=0;i<7;i++) labelsContainer.add(new JLabel(String.valueOf(i)));
eastPanel.add(labelsContainer, BorderLayout.NORTH);
JPanel moreLabelsContainer = new JPanel(new GridLayout(0, 1));
for(int i=7;i<70;i++) moreLabelsContainer.add(new JLabel(String.valueOf(i)));
JScrollPane scroll = new JScrollPane(moreLabelsContainer, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
eastPanel.add(scroll, BorderLayout.CENTER);
eastPanel.setPreferredSize(new Dimension(eastPanel.getPreferredSize().width, centerPanel.getPreferredSize().height));
pane.add(centerPanel, BorderLayout.CENTER);
pane.add(eastPanel, BorderLayout.EAST);
setContentPane(pane);
pack();
setLocationRelativeTo(null);
}
}
Thanks for your help !

I am not aware of a layout manager that restricts the height of the panel based on the height of a specific component in the panel.
One way is to customize the behaviour of the parent panel that contains the two child components.
The code might be something like:
#Override
public Dimension getPreferredSize()
{
Dimension d = super.getPreferredSize();
BorderLayout layout = (BorderLayout)getLayout();
Component center = layout.getLayoutComponent(BorderLayout.CENTER);
int centerHeight = center.getPreferreidSize().height;
if (d.height > centerHeight)
d.height = centerHeight;
return d;
}
This approach will allow for dynamic calculation of the height based on the component in the center.
Another option is to write you own layout manager. Then you can control this type of logic from within the layout manager.
Also, is setting that empty border an acceptable way to set the size of the panel where i will do some custom painting?
I override the getPreferredSize() to return the appropriate dimension.
By using the EmptyBorder you lose the ability to add a true Border to the panel, so I wouldn't recommend it.

Related

Second JPanel and JTexField look too cramped

I have an application where I use two JPanels. One of them is a PaintPanel. The second panel,the jtextfield and the jtextarea work fine but they look too cramped against the right side. I tried changing the sizes with setSize() but it didn't work.
The code for the paintpanel
public void center() {
jpCenter = new PaintPanel();
jpCenter.addMouseListener(this);
jpCenter.setSize(100, 100);
jpCenter.setBackground(Color.white);
add(jpCenter, BorderLayout.CENTER);
}
The code for the panel of the chatbox
public void east() {
// CREATE EAST Panel
gl = new GridLayout(4, 1);
jpEast = new JPanel();
jpEast.setSize(200, 200);
jpEast.setLayout(gl);
jpEast.setBackground(Color.white);
label = new JLabel("Number of shapes: ");
jpEast.add(label);
// ADD TEXT FIELD
jtf = new JTextField();
jtf.setText("");
jtf.setSize(200, 200);
jpEast.add(jtf);
// ADD BUTTON
jbSend = new JButton("Send");
jbSend.setEnabled(false);
jbSend.setSize(20, 60);
jpEast.add(jbSend);
jbSend.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
send(jtf.getText());
jtf.setText("");
}
});
// ADD TEXT AREA
jta = new JTextArea("");
jta.setSize(100, 100);
jpEast.add(jta);
// ADD EAST panel
add(jpEast, BorderLayout.EAST);
}
Avoid setting the size of components since it can make them not work well on all platforms, and with JTextArea in particular, it will not allow it to expand correctly if held within a JScrollPane (which is where a JTextArea belongs). Note also that most layout managers don't even respect a component's size but rather its preferred size.
Instead, set the row and column properties of your JTextAreas (done most easily via the JTextArea(int row, int column) constructor), the column property of your JTextField, the font sizes of other components (if need be). Then allow your container (JPanel) layout managers and component's own preferred sizes size all appropriately when you call pack() on your top-level window (often a JFrame), after adding all components but before setting it visible.
For more specific help, consider posting an image of the GUI you're getting vs. the one you're trying to achieve.

How do I put a JPanel in center of a JFrame irrespective the size of JFrame (of course its size is greater than jpanel)? [duplicate]

I'm using the NetBeans GUI builder to handle my layout (I'm terrible with LayoutManagers) and am trying to place a simple JLabel so that it is always centered (horizontally) inside its parent JPanel. Ideally, this would maintain true even if the JPanel was resized, but if that's a crazy amount of coding than it is sufficient to just be centered when the JPanel is first created.
I'm bad enough trying to handle layouts myself, but since the NetBeans GUI Builder autogenerates immutable code, it's been impossible for me to figure out how to do this centering, and I haven't been able to find anything online to help me.
Thanks to anybody who can steer me in the right direction!
Here are four ways to center a component:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
class CenterComponent {
public static JLabel getLabel(String text) {
return getLabel(text, SwingConstants.LEFT);
}
public static JLabel getLabel(String text, int alignment) {
JLabel l = new JLabel(text, alignment);
l.setBorder(new LineBorder(Color.RED, 2));
return l;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JPanel p = new JPanel(new GridLayout(2,2,4,4));
p.setBackground(Color.black);
p.setBorder(new EmptyBorder(4,4,4,4));
JPanel border = new JPanel(new BorderLayout());
border.add(getLabel(
"Border", SwingConstants.CENTER), BorderLayout.CENTER);
p.add(border);
JPanel gridbag = new JPanel(new GridBagLayout());
gridbag.add(getLabel("GridBag"));
p.add(gridbag);
JPanel grid = new JPanel(new GridLayout());
grid.add(getLabel("Grid", SwingConstants.CENTER));
p.add(grid);
// from #0verbose
JPanel box = new JPanel();
box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS ));
box.add(Box.createHorizontalGlue());
box.add(getLabel("Box"));
box.add(Box.createHorizontalGlue());
p.add(box);
JFrame f = new JFrame("Streeeetch me..");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(p);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
});
}
}
By using Borderlayout, you can put any of JComponents to the CENTER area. For an example, see an answer to Stack Overflow question Get rid of the gap between JPanels. This should work.
Even with BoxLayout you can achieve that:
JPanel listPane = new JPanel();
listPane.setLayout(new BoxLayout(listPane, BoxLayout.X_AXIS ));
JLabel label = new JLabel();
listPane.add(Box.createHorizontalGlue());
listPane.add(label);
listPane.add(Box.createHorizontalGlue());
mKorbel's solution is perfect for your goal. Anyway I always like to suggest BoxLayout because it's very flexible.
Mara: "thanks for your response, however the NetBeans GUI Build uses GroupLayout and this is not overridable."
Not true! Right click anywhere inside JFrame (or any other GUI container) in NetBeans GUI builder and select "Set Layout". By default is selected "Free Design", which is Group layout, but you can select any other layout including Border layout as advised by mKorbel.
There's many ways to do this, depending on the layout manager(s) you use. I suggest you read the Laying Out Components Within a Container tutorial.
I believe the following will work, regardless of layout manager:
JLabel.setHorizontalAlignment(SwingConstants.CENTER)

Adding JScrollPane to a JTextArea in a JPanel

I am trying to add JScrollPane to my large JTextArea, but whenever I include the JScrollPane code, my whole JTextArea disappears.
public myGUI() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 1174, 656);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
contentPane.setVisible(true);
JTextArea textArea_1 = new JTextArea();
textArea_1.setBounds(203, 5, 869, 440);
textArea_1.setEditable(true);
textArea_1.setVisible(true);
contentPane.add(textArea_1);
JScrollPane scroll = new JScrollPane (textArea_1);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
contentPane.add(scroll);
}
Several problems with your code:
You're trying to add a component, here your JTextArea called textArea_1, to multiple containers, here the contentPane and the JScrollPane. You can't do this in Swing as a component can be added to only one component. Add it only to the JScrollPane and not to the contentPane, and then add the scroll pane to the GUI.
You're constraining the size of your JTextArea via setBounds which almost guarantees that the JScrollPane will not work since doing this prevents the JTextArea from expanding when it holds more text than is shown. Instead set the JScrollPane's rows and columns properties and not its bounds. This will be the number of rows and columns that it should display within the scrollpane
You're using null layouts but not specifying the size of the JScrollPane, and so it defaults to a size of [0, 0] -- and this is why your JTextArea disappears. Null layouts require complete specification of all component sizes and positions.
You're using null layouts to set up your GUI. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
For example:
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyGuiPanel extends JPanel {
// some row and column values for our JTextArea
private static final int TXT_AREA_ROWS = 25;
private static final int TXT_AREA_COLS = 80;
// create the JTextArea, passing in the rows and columns values
private JTextArea textArea = new JTextArea(TXT_AREA_ROWS, TXT_AREA_COLS);
public MyGuiPanel() {
// create the JScrollPane, adding our JTextArea
JScrollPane scroll = new JScrollPane(textArea);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
// lets add some buttons to the bottom of the GUI just for fun
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 0));
buttonPanel.add(new JButton("Save"));
buttonPanel.add(new JButton("Open"));
buttonPanel.add(new JButton("Delete"));
buttonPanel.add(new JButton("Exit"));
// let's add a title to the top:
JLabel title = new JLabel("This is my Applications's Title", SwingConstants.CENTER);
title.setFont(title.getFont().deriveFont(Font.BOLD, 24)); // and make
// the text
// *BIG*
// use a BorderLayout for our GUI
setLayout(new BorderLayout(5, 5));
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
add(scroll, BorderLayout.CENTER); // add the scrollpane to the center
add(buttonPanel, BorderLayout.PAGE_END); // the button panel to the
// bottom
add(title, BorderLayout.PAGE_START); // and the title JLabel to the top
}
private static void createAndShowGui() {
// create our GUI JPanel
MyGuiPanel mainPanel = new MyGuiPanel();
// create a JFrame to add it to
JFrame frame = new JFrame("My GUI");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel); // add the GUI to the JFrame
frame.pack(); // tell the layout managers to do their work
frame.setLocationByPlatform(true);
frame.setVisible(true); // display the GUI
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Also, if your program extends JFrame, understand that you are painting yourself in a corner by doing this, forcing you to create and display JFrames, when often more flexibility is called for. In fact, I would venture that most of the Swing GUI code that I've created and that I've seen does not extend JFrame, and in fact it is rare that you'll ever want to do this. More commonly your GUI classes will be geared towards creating JPanels, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. This will greatly increase the flexibility of your GUI coding.
You do not need setBounds for your JTextArea. Because you are using a null layout and the JScrollPane has no bounds, nothing shows up. Your JTextArea is also added to two places which would cause some problems. I would recommend any of swings layout managers. As an example using BorderLayout which is one of the easiest managers:
public mygui() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setSize(700, 500);
setLayout(new BorderLayout());
JTextArea textArea_1 = new JTextArea();
textArea_1.setEditable(true);
JScrollPane scroll = new JScrollPane(textArea_1);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
add(scroll, BorderLayout.CENTER);
}

JTextField in the whole JFrame instead of my determined Area

yes I'm a total beginner in Java... Could somebody tell me, why the JTextField is located in the whole JFrame instead of just the space between (300,50) to (450,75) like I've inputted in setBounds?
import java.awt.*;
import javax.swing.*;
public class Chat extends JFrame {
JTextField t=new JTextField("");
public Chat() {
setVisible(true);
setSize(900, 300);
t = new JTextField();
t.setBounds(300, 50, 150, 25);
add(t);
}
}
Cause the JFrame default layout is BorderLayout and when you add the components if you don't specify it will put in the center. I recommend to use another layout like
GridBagLayout.
Example:
public Chat() {
setSize(900, 300);
t = new JTextField();
t.setPreferredSize(new Dimension(x,y));
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 6;
gridBagConstraints.gridy = 7;
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
panel.add(t,gridBagConstraints);
add(panel);
pack(); // this sizes the frame
setVisible(true); // call set visible after adding components
}
Should consider read this Using Layout Managers
setBounds method works with only with null Layout and default JFrame's layout is BorderLayout. Invoking JFrame's add method with BorderLayout and without specifying location defaults to BorderLayout.CENTER and centers the component, using its maximum size property as bounds. That means that setting prefered size of the component won't work with BorderLayout.CENTER. You can either change the frame's layout to null, using setLayout(null), which is considered a bad practice, because it, among other things, limits portability of the code, or use other layout manager.

Centering a JLabel on a JPanel

I'm using the NetBeans GUI builder to handle my layout (I'm terrible with LayoutManagers) and am trying to place a simple JLabel so that it is always centered (horizontally) inside its parent JPanel. Ideally, this would maintain true even if the JPanel was resized, but if that's a crazy amount of coding than it is sufficient to just be centered when the JPanel is first created.
I'm bad enough trying to handle layouts myself, but since the NetBeans GUI Builder autogenerates immutable code, it's been impossible for me to figure out how to do this centering, and I haven't been able to find anything online to help me.
Thanks to anybody who can steer me in the right direction!
Here are four ways to center a component:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
class CenterComponent {
public static JLabel getLabel(String text) {
return getLabel(text, SwingConstants.LEFT);
}
public static JLabel getLabel(String text, int alignment) {
JLabel l = new JLabel(text, alignment);
l.setBorder(new LineBorder(Color.RED, 2));
return l;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JPanel p = new JPanel(new GridLayout(2,2,4,4));
p.setBackground(Color.black);
p.setBorder(new EmptyBorder(4,4,4,4));
JPanel border = new JPanel(new BorderLayout());
border.add(getLabel(
"Border", SwingConstants.CENTER), BorderLayout.CENTER);
p.add(border);
JPanel gridbag = new JPanel(new GridBagLayout());
gridbag.add(getLabel("GridBag"));
p.add(gridbag);
JPanel grid = new JPanel(new GridLayout());
grid.add(getLabel("Grid", SwingConstants.CENTER));
p.add(grid);
// from #0verbose
JPanel box = new JPanel();
box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS ));
box.add(Box.createHorizontalGlue());
box.add(getLabel("Box"));
box.add(Box.createHorizontalGlue());
p.add(box);
JFrame f = new JFrame("Streeeetch me..");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(p);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
});
}
}
By using Borderlayout, you can put any of JComponents to the CENTER area. For an example, see an answer to Stack Overflow question Get rid of the gap between JPanels. This should work.
Even with BoxLayout you can achieve that:
JPanel listPane = new JPanel();
listPane.setLayout(new BoxLayout(listPane, BoxLayout.X_AXIS ));
JLabel label = new JLabel();
listPane.add(Box.createHorizontalGlue());
listPane.add(label);
listPane.add(Box.createHorizontalGlue());
mKorbel's solution is perfect for your goal. Anyway I always like to suggest BoxLayout because it's very flexible.
Mara: "thanks for your response, however the NetBeans GUI Build uses GroupLayout and this is not overridable."
Not true! Right click anywhere inside JFrame (or any other GUI container) in NetBeans GUI builder and select "Set Layout". By default is selected "Free Design", which is Group layout, but you can select any other layout including Border layout as advised by mKorbel.
There's many ways to do this, depending on the layout manager(s) you use. I suggest you read the Laying Out Components Within a Container tutorial.
I believe the following will work, regardless of layout manager:
JLabel.setHorizontalAlignment(SwingConstants.CENTER)

Categories