Creating Cells for GridLayout in Java - java

I'm trying to create a grid for my snakes and ladders game in java, but I have a small problem where there is an unwanted space in the grids I've created
Does anyone know how I can get rid of that?
This is the code I have for the client side (Client.java):
//Initialize Grid Cells
private Cell[][] cell = new Cell[10][10];
//Create Grid Layout
GridLayout GameBoard = new GridLayout(10, 10, 1, 1); //Create GridLayout
GameArea.setLayout(GameBoard); //Add GridLayout
GameArea.setPreferredSize(new Dimension(590,560));
GameArea.setOpaque(false);
//Add Cells to Grid
for (int v = 0; v < 10; v++)
for (int h = 0; h < 10; h++)
GameArea.add(cell[v][h] = new Cell(v, h, this));
//Individual Image on Each Cell
cell[1][0].add(new JLabel(GreenGrid));
This is the code I have for the cells(Cells.java), it also extends JPanel:
//Indicate the row and column of this cell in the board
private int GridRow;
private int GridColumn;
private Client parent;
public Cell(int GridRow, int GridColumn, Client GUI) {
this.GridRow = GridRow;
this.GridColumn = GridColumn;
this.parent = GUI;
setBorder(new LineBorder(Color.orange, 1)); // Set cell's border
setBackground(Color.gray);
}

I can't see your image, but I suspect that you have a layout issue. Does your Cell extend JPanel by the way? Do you set its layout manager or do you use the default FlowLayout?
Consider:
You should not call setPreferredSize(...) on the GameBoard as that will then dictate your grid cell size, which is too big.
Rather, the Cells themselves should be dictating the size of themselves and the entire grid.
Consider having Cell override getPreferredSize() and returning the dimension of the JLabel's image (if one is present) or else returning the super's result.
Be sure to call pack() on your top-level window after adding all components and before setting it visible.
Consider having Cell use a BorderLayout and adding your JLabel to the BorderLayout.CENTER position so that it fills the cell (if that's what you want it to do).
For more help and better help, consider creating and posting an sscce.

Your first problem is here...
GridLayout GameBoard = new GridLayout(10, 10, 1, 1); //Create GridLayout
As described in the JavaDocs...
public GridLayout(int rows,
int cols,
int hgap,
int vgap)
Creates a grid layout with the specified number of rows and columns.
All components in the layout are given equal size.
In addition, the horizontal and vertical gaps are set to the specified
values. Horizontal gaps are placed between each of the columns.
Vertical gaps are placed between each of the rows.
One, but not both, of rows and cols can be zero, which means that any
number of objects can be placed in a row or in a column.
What this means is, you are supplying the gap by supplying non-zero values to the hgap and vgap parameters.
If you used something like...
GridLayout GameBoard = new GridLayout(10, 10); //Create GridLayout
You would end up with something like...
As has already been mentioned, I would avoid using GameArea.setPreferredSize(new Dimension(590,560)); and instead, override the getPreferredSize method the Cell class. Because of the way that GridLayout works, this won't stop the cells from been resized, but this might be desirable anyway...

Related

Java - JScrollPane Doesn't align with JTable

I am trying to make a JTable that has a width of 500. The problem is that the JScrollPane associated with the table doesn't appear next to the table.
Here is the relevant code:
// Create authorsPanel
JPanel authorsPanel = new JPanel(new BorderLayout(5,5));
authorsPanel.setBorder( new TitledBorder("Author Selection") );
// Configure author table
DefaultTableModel authorstableModel = new DefaultTableModel(new String[80][1], new String[]{"Authors"}) {
#Override
public boolean isCellEditable(int row, int column) {
//all cells false
return false;
}
};
JTable authorsTable = new JTable(authorstableModel);
authorsTable.getColumnModel().getColumn(0).setPreferredWidth(500);
authorsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
try {
authorsTable.setAutoCreateRowSorter(true);
} catch(Exception continuewithNoSort) {
}
JScrollPane tableScroll = new JScrollPane(authorsTable);
Dimension tablePreferred = tableScroll.getPreferredSize();
tableScroll.setPreferredSize(new Dimension(500, tablePreferred.height/3));
authorsPanel.add(tableScroll);
Here is a screenshot:
When I get rid of the line:
authorsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
Then the table returns to being the full width of the panel, so it seems like I need this line.
The java docs on BorderLayout states :
The components are laid out according to their preferred sizes and the
constraints of the container's size. The NORTH and SOUTH components
may be stretched horizontally; the EAST and WEST components may be
stretched vertically; the CENTER component may stretch both
horizontally and vertically to fill any space left over.
You have used authorsPanel.add(tableScroll) to add the JScrollPane to the JPanel. So you are basically adding it to the center. So this is going to occupy the whole space that is lying vacant.
The solution to your problem lies in choosing a different layout. I could suggest MigLayout which is very versatile and you can get all kinds of effects using it.
You add your scroll pane to a panel with BorderLayout. BorderLayout does not care about preferred size. Use e.g GridBagLayout with proper constraints or you can BoxLayout (with horizontal flow) placing the table first and an empty panel second as place holder.

JPanel: extra spaces between borders of nested GridLayout tables

I'm working on my first Java Swing project (really my first GUI project, unless you count client-side web programming), and I'm having an aesthetic issue. I'm making a Sudoku board in a JPanel. The panel is laid out with a 3x3 GridLayout, with each of its nine cells containing JPanel filled with another 3x3 GridLayout. The outer grid has wider borders to indicate the "boxes" of a Sudoku board, and the inner grid thinner borders to display the "cells" (here's a screenshot if you need help visualizing it).
The problem is that I'm getting extra space between the borders of the inner and outer grids, which looks just terrible. I've tried setting the insets to zero explicitly, which didn't do anything. The only way I can think to fix it is to eliminate the GridLayout nesting altogether and instead use one 9x9 grid, setting the individual cell borders to be thicker at the top/bottom/left/right where appropriate; however, each cell's border changes color and thickness when it gains focus, and I can't think of a graceful way to implement that strategy. I've also been looking into other layouts, but GridLayout seems perfect for this purpose (each cell should be the same size).
Here's the constructor for BoardPanel (extends JPanel), I tried to omit any extraneous code:
BoardPanel(Board newBoardData)
{
/**
* Instance variable initializations, call to super(), etc. omitted.
*/
// Outer grid initialization
// In pictures, .getBoxWidth() and .getBoxHeight both return (int)3
setLayout(new GridLayout(boardData.getBoxWidth(), boardData.getBoxHeight(), 0, 0));
setBorder(new LineBorder(colorGridBorders, 2));
// Inner grid array; .getBoardSize() returns (int)9
JPanel innerBoxGridPanels[] = new JPanel[boardData.getBoardSize()];
GridLayout innerBoxGridLayout = new GridLayout(boardData.getBoxHeight(), boardData.getBoxWidth(), 0, 0);
LineBorder innerBoxLineBorder = new LineBorder(colorGridBorders, 1);
for (int columnIndex = 0; columnIndex < boardData.getBoardSize(); ++columnIndex)
{
for (int rowIndex = 0; rowIndex < boardData.getBoardSize(); ++rowIndex)
{
// CellPanel derives from JPanel; has a call to setBorder() to create inner grid lines
cellPanes[columnIndex][rowIndex] = new CellPanel(columnIndex, rowIndex, boardData.getCell(columnIndex, rowIndex));
}
}
for (int box = 0; box < boardData.getBoardSize(); ++box)
{
// Inner grid initialization
innerBoxGridPanels[box] = new JPanel(innerBoxGridLayout);
innerBoxGridPanels[box].setBorder(innerBoxLineBorder);
// Adding cells to inner grid
for (int cell = 0; cell < boardData.getBoardSize(); ++cell)
{
innerBoxGridPanels[box].add(cellPanes
[( cell % boardData.getBoxWidth() )
+ ( ( box % boardData.getBoxHeight() ) * boardData.getBoxWidth() )]
[( cell / boardData.getBoxWidth() )
+ ( (box / boardData.getBoxHeight() ) * boardData.getBoxHeight() ) ]);
}
// Adding inner grids to outer grid
add(innerBoxGridPanels[box]);
}
}
Here's a screenshot of the rendered board at various sizes. In the upper-right image, you can see that if you adjust the window size just right you can get rid of most of the extra space (there's still some at the edges though).
Has anyone had this sort of problem with nested GridLayouts before and found a solution? Any help would be greatly appreciated, I've been pulling my hair out over this for over a day now.
Seems like a problem with the window size. Let´s say there are 2 panels horizontally inside an outer panel. The outer pane has a border of size 2 and the inner panels have a border of size 1.
If the window has a size of 18 for example, we have
2px for outer border
1px for inner border
5px for panel content
1px for inner border
1px for inner border
5px for panel content
1px for inner border
2px for outer border
So there is no space and both inner panels have same size
If for whatever reason the window size is changed to 19, there is a need to create a gap between the panels to mantain them of equal size.
If the size is 20, both panels get +1 to his size and there is no gap needed.
To solve this you can use a fixed size of the window (one you know has no gaps), or add a padding to the outer panel when the window is resized so the padding makes the rest of the frame to have a size that produces no gaps. (in your case, if my maths are fine, should be 20 + x/3)
If you want to discard the use of nested panels and change the cell border when has the focus, you need a LineBorder instance for each cell. In your code, all cells are using the same LineBorder instance.

Center JTable's cells to the middle of the JTable

How to center JTable cells to the middle of the JTable?
I am using table to paint boxes in the table, but when resized, the cells remain left-top aligned. When the table fits the cells nicely, it is not a problem. But when I resize the window (and the table with it), it is wrong:
My question is, is it possible to make the cells appear in the middle of the table? I suppose I could use glues on sides (this is BorderLayout), but I would rather take this approach.
Since your TableCellRenderer is just painting colored blocks, you could put the table in a FlowLayout and override getPreferredScrollableViewportSize().
private static final int WIDE = 10;
private static final int HIGH = 20;
private static final int SIZE = 50;
...
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(WIDE * SIZE, HIGH * SIZE);
}

JLabels to be resized in a GridLayout

I have a very simple Java program (see below). The GridLayout has 20 rows and 4 columns. As you know the elements are supposed to be added horizontally by (GridLayout) definition. However, I get the two elements (labels) placed one above the other, vertically.
I colored them and realised the labels take up the whole row, hence the vertical effect. But then I also used setSize(5,5) with each to make them smaller, however they still take up the whole row. Any advice as to why this happens and how to fix/set smaller size/etc?
public class Sam extends JFrame {
public JButton btn_arr;
public Container c;
public JLabel[] lbl = new JLabel[20];
public Sam()
{
c = getContentPane();
c.setLayout(new GridLayout(20,4));
lbl[1] = new JLabel("Column1");
c.add(lbl[1]);
lbl[2] = new JLabel("Column2");
c.add(lbl[2]);
show();
}
public static void main(String[] args)
{
Sam x = new Sam();
x.setVisible(true);
x.setSize(7500,4500);
}
}
You're only adding two components to the grid so they will fill it up. You need to add more components to the grid as placeholders so that it can place the original JLabels in their proper place, perhaps empty JLabels or JPanels.
As an aside, you should avoid setting the size of any Swing component. Your current size of 7500, 4500 is a bit on the large size.
As a second aside, perhaps you want to use a JTable instead here.
Edit: if you want a GridLayout with 4 columns and variable number of rows, use 0 for your GridLayout row constant:
c.setLayout(new GridLayout(0, 4));
e.g.,
import java.awt.*;
import javax.swing.*;
public class Sam extends JFrame {
public static final int COLUMN_COUNT = 4;
public JButton btn_arr;
public Container c;
public JLabel[] lbl = new JLabel[COLUMN_COUNT];
public Sam() {
c = getContentPane();
c.setLayout(new GridLayout(0, COLUMN_COUNT));
for (int i = 0; i < lbl.length; i++) {
lbl[i] = new JLabel("Column " + (i + 1));
c.add(lbl[i]);
}
}
public static void main(String[] args) {
Sam x = new Sam();
x.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
x.pack();
x.setLocationRelativeTo(null);
x.setVisible(true);
// x.setSize(7500,4500);
}
}
But still I wonder if a JTable wouldn't work better here.
One thing to keep in mind with the GridLayout is it that it is designed to cover the entire containing panel sizing the cells as equally as possible, and elements added to the cells will be expanded to fill the entire cell. So as the cell sizes change, the labels will also change in size. Effectively grid cells force an expansion/contraction in both X and Y direction of all contained elements.
One way to prevent that from happening if you must use the GridLayout is to not add the labels directly to the container that uses the GridLayout, but instead put each label inside a JPanel that uses a FlowLayout (the default) that you can set alignment of either Left, Middle or Right, then add that JPanel to the Grid container. The JPanel will be resized but it will not change the size of the Label.
Or use the GridBagLayout manager. More complex, but once you understand it, it makes life easier. But as Hovercraft mentioned, if what you are trying to do is create a grid with column headers, a JTable might be a better option.

Fit JTable to fill the JPanel

Hi here are my codes for my table settings:
String [] column = {"MacAddress","PcName","OperatingSystem","IpAddress","Port","Status"};
model = new DefaultTableModel(0,column.length);
model.setColumnIdentifiers(column);
mainTable = new JTable(model);
mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for(int i=0;i<=column.length-1;i++){
mainTable.getColumnModel().getColumn(i).setPreferredWidth(300);
}
pane = new JScrollPane(mainTable);
pnlTabel = new JPanel();
pnlTabel.setBorder(BorderFactory.createTitledBorder(""));
pnlTabel.setPreferredSize(new Dimension(dim.width*70/100, dim.height*60/100));
pnlTabel.add(pane);
addMainPanel(pnlTabel);
Here is my addMainPanel() function:
public void addMainPanel(Component pnl){
mainPanel.add(pnl);
mainPanel.revalidate();
}
And here is my code for my mainPanel:
mainPanel = new JPanel();
mainPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
add(mainPanel,"Center");
and I'm using border layout for my frame:
setLayout(new BorderLayout(0,0));
My problem is that, even i use this set of code to set my JTable to fit but it seems to fail all the this, this code:
mainTable.setAutoResizeMode(JTa![enter image description here][1]ble.AUTO_RESIZE_OFF);
for(int i=0;i<=column.length-1;i++){
mainTable.getColumnModel().getColumn(i).setPreferredWidth(300);
}
When is use that code, my jtable does not resize but only add on a horizontal scroll bar at the bottom.
No offense meant but .. your code and consequently your question is a mess ;-) Plus you don't explain what exactly you want to achieve.
Trying to detangle, taking the nested layouts/resizing characteristics (as seen in the snippets, might not be complete):
frame // BorderLayout
mainPanel // FlowLayout
pnlTabel // FlowLayout, hard-coded prefSize
pane // scrollPane
mainTable // auto-resize-off
Aside: intentionally kept untelling names to demonstrate how mixing naming schemes tend to contribute to overall confusion :-) Doesn't really matter whether you decide for pre or postFixing some type-related marker, but if you do be consistent.
In that hierarchy, there are two levels of FlowLayout which basically will layout their children at their respective prefs and adjusting their own pref accordingly, lest the pref were hard-coded on the level of the pnlTable: however the table's pref will be changed (by changing the column prefs) it cannot bubble further up because ... hard-coding the pref leads not calculating its size (neither by layoutManager and nor uiDelegate, both never get a chance to do it)
Another issue - the more interesting one :-) - is that the JScrollPane is somewhat special in
calculating its own prefSize from its view's pref/scrollablePrefViewportSize depending on whether or not the view implements Scrollable (JTable does so, though in a crappy way)
being a validationRoot: invalidating the view (or any children) doesn't bubble further up the hierarchy
Assuming that you want the table's scrollPane to grow if the prefWidts of the columns change, there are two thingies to tweak:
implement table's getPreferredScrollableWidth to return a value calculated based on the prefWidth of the columns
revalidate a container higher up in the hierarchy
Some code to play with:
final JTable table = new JTable(50, 10) {
// properties to base a reasonable prefScrollable size
int visibleColumns = 3;
int visibleRows = 10;
// hard-coded default in super
Dimension dummySuper = new Dimension(450, 400);
#Override
public Dimension getPreferredScrollableViewportSize() {
Dimension dim = super.getPreferredScrollableViewportSize();
if (!dummySuper.equals(dim)) return dim;
dim = new Dimension();
for (int column = 0; column < Math.min(visibleColumns, getColumnCount()); column++) {
dim.width += getColumnModel().getColumn(column).getPreferredWidth();
}
dim.height = visibleRows * getRowHeight();
return dim;
}
};
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for (int i = 0; i < table.getRowCount(); i++) {
table.setValueAt("row: " + i, i, 0);
}
JComponent tablePanel = new JPanel();
tablePanel.add(new JScrollPane(table));
Action sizeColumns = new AbstractAction("size columns") {
int prefWidth = 75;
#Override
public void actionPerformed(ActionEvent e) {
int newWidth = prefWidth + 15;
for (int i = 0; i < table.getColumnCount(); i++) {
if (table.getColumnModel().getColumn(i).getPreferredWidth() == prefWidth)
table.getColumnModel().getColumn(i).setPreferredWidth(newWidth);
}
prefWidth = newWidth;
// revalidate "higher up" than the table itself
frame.revalidate();
}
};
frame.add(new JButton(sizeColumns), BorderLayout.SOUTH);
If you want a JTable to fill the available space, you should put it inside a JPanel which has a BorderLayout layout manager. Also don't forget about the JScrollPane which ensures that if the table doesn't fit into the view (e.g. too many rows), scrollbars will appear:
JFrame frame = new JFrame();
// set up frame
JTable table = new JTable();
// Set up table, add data
// Frame has a content pane with BorderLayout by default
frame.getContentPane().add( new JScrollPane( table ), BorderLayout.CENTER );
If you have other content you wish to display besides the table, you can add those to the NORTH, SOUTH, EAST, WEST parts of the content panel (which can be wrapped into other panels if more components are to be placed there).

Categories

Resources