JScrollPane (for JTable) fill and grow in MigLayout - java

It is difficult to describe what happens so here is a SSCCE:
public class TableInScrollPaneTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TableInScrollPaneTest();
}
});
}
public TableInScrollPaneTest() {
JFrame frame = new JFrame();
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
LayoutManager layout;
layout = new MigLayout("wrap 1, fill", "fill, grow", "[fill, grow 1][fill, grow 2]");
//layout = new GridLayout(2,1);
JPanel panel = new JPanel(layout);
JPanel placeHolder = new JPanel();
JPanel placeHolder2 = new JPanel();
placeHolder.setBackground(Color.GRAY);
placeHolder2.setBackground(Color.LIGHT_GRAY);
JTable table = this.createTable();
JScrollPane tableScrollPane = new JScrollPane(table,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
//table.setPreferredScrollableViewportSize(new Dimension(5000,200));
panel.add(placeHolder, "");
panel.add(tableScrollPane, "");
//panel.add(placeHolder2, "");
frame.setContentPane(panel);
frame.setVisible(true);
}
private JTable createTable() {
String[] columnNames = "Name 1,Name 2,Name 3,Name 4,Name 5".split(",");
int rows = 30;
int cols = columnNames.length;
String[][] data = new String[rows][cols];
for(int i=0; i<rows; i++) {
for(int j=0; j<cols; j++) {
data[i][j] = "R"+i+" C"+j;
}
}
JTable table = new JTable(data, columnNames);
return table;
}
}
I want the scrollPane to use all available space when the frame gets resized by the user. But additionally I want another panel (placeHolder) to grow, too. For example the heigth of the frame divided by 3, the placeHolder gets 1 part and the scrollPane gets 2 parts. The GridLayout is kinda doing what I need, but I'm looking for a solution with MigLayout.
If you uncomment panel.add(placeHolder2, ""); and comment the line above it works.
EDIT1:
I tried using the sizegroup contraint. Now the placeHolder and the scrollPane have the same size, but I want the scrollPane take twice as much space. (Additionally in my programm I dont have a placeholder like this, but rather there are some labels, checkboxes and textfields. So sizegroup probably is not what I need except there are more ways to use it)

I found the solution here:
MigLayout confused by JTable objects contained in JScrollBar objects
and here:
How to set JScrollPane to show only a sepecific amount of rows
table.setPreferredScrollableViewportSize(null);
or
table.setPreferredScrollableViewportSize(table.getPreferredSize());
I don't know what's the difference between this code snippets, but they both do what I need.

Related

Java.awt: Second TextArea is not shown

I'm trying to understand how Java.awt works (we need to create a GUI without GUI editor)
the following code does not show 2 TextAreas:
Frame fr = new Frame("Parser");
Panel buttons = new Panel();
Panel inputText = new Panel();
Panel outputText = new Panel();
String here = new String ("Insert code here...");
TextArea input = new TextArea(here, 9, 96, TextArea.SCROLLBARS_VERTICAL_ONLY);
TextArea output = new TextArea(here, 9,96,TextArea.SCROLLBARS_VERTICAL_ONLY);
public Window(){
fr.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
fr.dispose();
}
}
);
fr.setSize(700, 400);
fr.setLocation(200,100);
fr.setResizable(false);
fr.add(buttons);
fr.add(inputText);
fr.add(outputText);
buttons.setBounds(new Rectangle(0,0,700,60));
buttons.setBackground(new Color(200,200,200));
inputText.setBounds(new Rectangle(0,60,700,170));
inputText.setBackground(new Color(255,255,255));
inputText.add(input);
outputText.setBounds(new Rectangle(0,230,700,170));
outputText.setBackground(new Color(200,200,200));
outputText.add(output);
}
Obtained result:
Expected result:
Your code does not respect the layout managers that your containers are using. I believe that AWT Frames use a BorderLayout by default (edit: yes they do, per the Frame API. Suggestions:
In general avoid AWT for Swing which has much greater power and flexibility, although it too is showing its age, just less so than AWT.
Read up on and use layout managers in a smart way to do your heavy lifting for you. Here it looks like a BoxLayout could help you.
Avoid use of null layouts. While yes, that could offer you a quick and easy fix for your current code, it leads to the creation of very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain.
Avoid setting bounds, sizes or locations of any components, and again let the components and their container's layout managers set the sizes for you.
The Layout Manager Tutorial
For example:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.*;
public class MyWindow extends JPanel {
private static final int ROWS = 10;
private static final int COLS = 50;
private static final String[] BUTTON_NAMES = { "Monday", "Tuesday",
"Wednesday", "Thursday", "Friday" };
private static final int GAP = 3;
private JTextArea inputTextArea = new JTextArea(ROWS, COLS);
private JTextArea outputTextArea = new JTextArea(ROWS, COLS);
public MyWindow() {
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
for (String btnName : BUTTON_NAMES) {
buttonPanel.add(new JButton(btnName));
}
outputTextArea.setFocusable(false);
outputTextArea.setEditable(false);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(buttonPanel);
add(putInTitledScrollPane(inputTextArea, "Input Text"));
add(putInTitledScrollPane(outputTextArea, "Output Text"));
}
private JPanel putInTitledScrollPane(JComponent component,
String title) {
JPanel wrapperPanel = new JPanel(new BorderLayout());
wrapperPanel.setBorder(BorderFactory.createTitledBorder(title));
wrapperPanel.add(new JScrollPane(component));
return wrapperPanel;
}
private static void createAndShowGui() {
MyWindow mainPanel = new MyWindow();
JFrame frame = new JFrame("MyWindow");
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(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Which displays as:
Use of layout managers gives you much greater ease when it comes to changing or enhancing your GUI. For example, since I'm setting my JTextArea's width with a COL constant, if I change the COL constant, the whole GUI widens, even the buttons and the button JPanel, since the layout managers are handling all the sizing. With your code, you'd have to manually change the width of every component added to the GUI, which is prone to bug creation.
Because you are manually laying out your components, you are needed to set layout to null (setLayout(null);)
so before adding any component add this line in your code.
fr.setLayout(null);
Now you will get this :

JTable columns doesnt resize probably when JFrame resize

I got a JTable, which i applied AUTO_RESIZE_LAST_COLUMN to. It auto resizes last column when i drag the columns left or right..
However, the JTable are attached to a JPanel with a BorderLayout manager. When i resize the JFrame, the JPanel resize, and since the JTable fills the JPanel, the JTable resizes too. But when i resize the JFrame, the AUTO_RESIZE_LAST_COLUMN doesnt works, but instead it resizes all the columns.
I want it to only auto_resize the last column, when the JFrame changes size, instead of resize all columns.
code:
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class JTableResize extends JFrame {
private JTable table;
private JPanel panel;
private JScrollPane pane;
DefaultTableModel model = new DefaultTableModel();
public JTableResize() {
super("JTable - Resize Problem");
setLayout(new BorderLayout());
panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setBackground(Color.red);
add(panel, BorderLayout.CENTER);
table = new JTable(model);
//panel.add(table);
model.addColumn("Resize");
model.addColumn("Problem");
model.addColumn("........");
model.addColumn("This should resize");
pane = new JScrollPane(table);
panel.add(pane);
//this is supposed to resize last column.. It works when you drag in the columns, but not when frame are resized
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
table.getTableHeader().setReorderingAllowed(false);
table.setShowVerticalLines(false);
for (int i = 0; i <= 50; i++) {
model.addRow(new Object[] {i, i, i, i});
}
}
public static void main(String[] args) {
JTableResize jtr = new JTableResize();
jtr.setSize(500, 500);
jtr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jtr.setVisible(true);
}
}
Check out the API documentation from the doLayout() method of JTable.
Before the layout begins the method gets the resizingColumn of the tableHeader. When the method is called as a result of the resizing of an enclosing window, the resizingColumn is null. This means that resizing has taken place "outside" the JTable and the change - or "delta" - should be distributed to all of the columns regardless of this JTable's automatic resize mode.
So this behaviour is not support by default.
Overriding the doLayout() method of the JTable and setting the "resizing column" to the last column seems to do the trick:
#Override
public void doLayout()
{
if (tableHeader != null)
{
TableColumn resizingColumn = tableHeader.getResizingColumn();
// Viewport size changed. Increase last columns width
if (resizingColumn == null)
{
TableColumnModel tcm = getColumnModel();
int lastColumn = tcm.getColumnCount() - 1;
tableHeader.setResizingColumn( tcm.getColumn( lastColumn ) ) ;
}
}
super.doLayout();
}

Are GridLayout cells really all the same size?

I decided to use a GridLayout LayoutManager for my Java Swing app because each cell within the grid is supposed to be exactly the same size.
From the Java Tutorials:
A GridLayout object places components in a grid of cells. Each component takes all the available space within its cell, and each cell is exactly the same size.
And even in the description of the GridLayout class:
The GridLayout class is a layout manager that lays out a container's components in a rectangular grid. The container is divided into equal-sized rectangles, and one component is placed in each rectangle.
However, my code seems to make a certain cell twice as large as the others. I added 3 JPanels to a Container with GridLayout, and gave each JPanel a different background color. This was the result:
Clearly, the first JPanel (red background) is twice as big as the others (green and yellow). The code that produced this is the following:
public void updateListFrameContentPane(Container mainPane) {
mainPane.setLayout(new GridLayout(1,0));
JPanel listPanel = new JPanel();
listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS));
listPanel.add(friendsLabel);
listPanel.add(listScrollPane);
listPanel.setBackground(Color.RED);
mainPane.add(listPanel);
for(JPanel chatPanel : chatPanels) {
chatPanel.setBackground((Math.random()>0.5 ? Color.YELLOW : Color.GREEN));
mainPane.add(chatPanel);
}
}
All I do is set the Container's layout to GridLayout with 1 row and any number of columns, and then add 3 JPanels to that. So why is the first JPanel so much larger? Strangely this only happens when two or more chatPanels are added. When there is only one, it formats correctly.
Kiheru is right. revalidate/repaint after changing the contents of a container. Here's a rough but working example:
public class GridLayoutExample {
private JFrame frame;
private Map<String,JPanel> chatBoxes = new HashMap<String,JPanel>();
private String lastKey = "0";
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GridLayoutExample window = new GridLayoutExample();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public GridLayoutExample() {
initialize();
}
private void addChatBox() {
/*
* JPanel (border layout)
* - JPanel (Border South, Border layout)
* - - JTextField ( Border center )
* - - JButton ( Border east )
* - JLabel (Border North )
* - JTextArea (Border Center);
*/
int lk = Integer.valueOf(lastKey)+1;
lastKey = Integer.toString(lk);
JPanel np = new JPanel();
np.setLayout(new BorderLayout(0,0));
np.setBackground((lk%2 == 0) ? Color.GREEN : Color.YELLOW);
JPanel south = new JPanel();
south.setLayout(new BorderLayout(0,0));
np.add(south,BorderLayout.SOUTH);
JButton b = new JButton("New Button");
south.add(b,BorderLayout.EAST);
JTextField field = new JTextField();
south.add(field,BorderLayout.CENTER);
JLabel label = new JLabel(lastKey);
label.setHorizontalAlignment(SwingConstants.CENTER);
np.add(label,BorderLayout.NORTH);
JTextArea text = new JTextArea();
np.add(text,BorderLayout.CENTER);
chatBoxes.put(lastKey, np);
frame.getContentPane().add(np);
frame.revalidate(); // CRITICAL MISSING LINES
frame.repaint(); // CRITICAL MISSING LINES
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 923, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new GridLayout(1, 0, 0, 0));
JPanel panel = new JPanel();
panel.setBackground(Color.RED);
frame.getContentPane().add(panel);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JLabel lblNewLabel = new JLabel("Online Users");
panel.add(lblNewLabel);
JList list = new JList();
list.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
addChatBox();
}
});
list.setModel(new AbstractListModel() {
String[] values = new String[] {"Alpha", "Beta", "Gamma", "Delta", "Epsilon"};
public int getSize() {
return values.length;
}
public Object getElementAt(int index) {
return values[index];
}
});
panel.add(list);
}
}
I chose to revalidate/repaint the entire frame, but it may be possible to have it work while repainting a lesser container. Certainly without the critical lines marked above, it doesn't matter how often you click on the list elements, nothing new will show up. With those lines, every time you click, a new chatbox is added.
Huh... just noticed this. If the red area is considered two separate panels, then they're all the exactly correct size. Have you perhaps accidentally added an extra panel?

JScrollPane not working on JList

code:
list1items = new DefaultListModel();
list1items.addElement("-");
list1 = new JList(list1items);
list1.setSelectionMode (ListSelectionModel.SINGLE_SELECTION);
list1.setBounds(0,0, 100,100);
JScrollPane list1scr = new JScrollPane(list1);
list1scr.setPreferredSize(new Dimension(20, 20));
list1.setVisibleRowCount(8);
getContentPane().add (list1scr);
And no scroll-bar appears. When there are too many items, they are hidden, I cant reach them. How to solve this?
getContentPane().add(list1scr);
To expand on Michael Ardan's answer, you were adding you JList to the panel instead of the JScrollPane. The JScrollPane must be added to the panel and the JList must be added to the ScrollPane for it to work. There's really no need to use setBounds or setPreferredSize - get rid of them. JList takes care of all that when you call the setVisibleRowCount method. Here's an example of your ScrollPane working. If you still have problems, plug your own code into this example until it breaks. Then tell us what broke it. If not, accept Michael's answer.
import java.awt.*;
import javax.swing.*;
public class Temp extends JPanel{
public Temp(){
DefaultListModel list1items = new DefaultListModel();
list1items.addElement("-");
for(int i = 0; i < 200; i++)
list1items.addElement("Item " + i);
JList list1 = new JList(list1items);
list1.setSelectionMode (ListSelectionModel.SINGLE_SELECTION);
JScrollPane list1scr = new JScrollPane(list1);
list1.setVisibleRowCount(8);
add (list1scr);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new Temp());
frame.pack();
frame.setVisible(true);
}
}

How to adjust the grid size after resizing the frame?

I was wondering the following: I have a MainWindow component (which contains a frame (JFrame)) and several other JPanels. Where one JPanel, let's say gridPanel uses the gridLayout as LayoutManager. Now my problem is that I want to adjust (set the size of rows / set the size of columns) after the window has been resized. Can someone tell me how I can achieve actions that can be triggered after resizing the frame as I am not familiar with the listeners involved.
It should be the done on the most "standard" coding practices. Thank you for your response and answers!
If you want your grid to "fill up" a container, or fill up the JFrame, then key is to use proper layout managers to hold the GridLayout-using container. For instance if you add the GridLayout-using container to another container that uses FlowLayout, then the GridLayout-using container will not change size if its holding container changes size. However if you add the GridLayout-using container to another container that uses BorderLayout and to its BorderLayout.CENTER position, then the GridLayout-using container will resize as the BorderLayout-using parent container resizes.
Example:
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class ExpandingGrid extends JPanel {
private static final int GAP = 5;
public ExpandingGrid() {
// create a BorderLayout-using JPanel
JPanel borderLayoutPanel = new JPanel(new BorderLayout());
borderLayoutPanel.setBorder(BorderFactory.createTitledBorder("BorderLayout Panel"));
borderLayoutPanel.add(createGridPanel(), BorderLayout.CENTER); // add a Grid to it
// create a FlowLayout-using JPanel
JPanel flowLayoutPanel = new JPanel(new FlowLayout());
flowLayoutPanel.setBorder(BorderFactory.createTitledBorder("FlowLayout Panel"));
flowLayoutPanel.add(createGridPanel()); // add a grid to it
// set up the main JPanel
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new GridLayout(1, 0, GAP, 0)); // grid with 1 row
// and add the borderlayout and flowlayout using JPanels to it
add(borderLayoutPanel);
add(flowLayoutPanel);
}
// create a JPanel that holds a bunch of JLabels in a GridLayout
private JPanel createGridPanel() {
int rows = 5;
int cols = 5;
JPanel gridPanel = new JPanel(new GridLayout(rows, cols));
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// create the JLabel that simply shows the row and column number
JLabel label = new JLabel(String.format("[%d, %d]", i, j),
SwingConstants.CENTER);
// give the JLabel a border
label.setBorder(BorderFactory.createEtchedBorder());
gridPanel.add(label); // add to the GridLayout using JPanel
}
}
return gridPanel;
}
private static void createAndShowUI() {
JFrame frame = new JFrame("ExpandingGrid");
frame.getContentPane().add(new ExpandingGrid());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Also, if this is not helpful, then you may wish to elaborate more on your problem and post code, preferably an SSCCE.
That's why I wanted to only adjust the columns.
Maybe the Wrap Layout is what you are looking for.

Categories