JPanel: extra spaces between borders of nested GridLayout tables - java

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.

Related

scrollRectToVisible --> need effect on vertical line

I have JTable with two scrollbars (horizontal and vertical). When I use scrollRectToVisible, it return me Rectangle and this action cause that vertical and horizontal scrollbars automatically move to specified rows/columns. How can I move automatically vertical scrollbar to desired place and horizontal scrollbar should stay inactive? This method should show me selected row but my JTable is very wide and I would like to avoid automatically movement (horizontal) at the beginning (left side) of track -> simple say, I expect that horizontal position is unchanged.
public void goToSelected() {
int selectedRow = this.getSelectedRow();
if (selectedRow >= 0)
this.scrollRectToVisible(this.getCellRect(selectedRow, 0, true));
}
The horizontal position might change because you are specifying a column index (0) and the specified column might be invisible. You can simply combine the values of the currently visible area and the cell’s vertical range to get the desired effect:
Rectangle target = getCellRect(selectedRow, 0, true), vis = getVisibleRect();
target.x = vis.x;
target.width = vis.width;
scrollRectToVisible(target);

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.

Offsetting GUI grid of gifs in java?

so I'm trying to make a battleship game, and I implemented a 10x10 grid containing gifs of some pixelated water. Now, what I'm trying to do is offset the grid one cell down and to the right so I can place the numbers and letters along the side and top like in a real battleship game. The problem is that when I try to offset it, it removes either the entire right side column of cells and reduces the window accordingly, and visa versa with the bottom. Here's my code:
EDIT 3: I have replaced the gif so that it can be used by everyone, simply copy the code and run it.
public class ButtonGrid {
JFrame frame=new JFrame(); //creates frame
JLabel[][] grid; //names the grid of buttons
public ButtonGrid(int width, int length) throws MalformedURLException{
URL urlPic = new URL("http://i47.tinypic.com/14wswi9.gif");
ImageIcon urlPicture = new ImageIcon(urlPic);
frame.setLayout(new GridLayout(width,length)); //set layout
grid=new JLabel[width][length]; //allocate the size of grid
for(int y=0; y<length; y++){
for(int x=0; x<width; x++){
grid[x][y]= new JLabel(urlPicture); //creates new button
frame.add(grid[x][y]); //adds button to grid
}
}
grid[1][1].setForeground(Color.red);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(270, 400);
frame.pack(); //sets appropriate size for frame
frame.setVisible(true); //makes frame visible
}
public static void main(String[] args) throws MalformedURLException{
new ButtonGrid(11,11);//makes new ButtonGrid with 2 parameters
}
}
EDIT: It seems that I may not have made this really clear. On a real battleship game, there is a row of numbers and a column of letters. The rest of the grid is simply water with the ships inside of it. With the code I have provided, the entire grid is water, and I want to offset to place a row of numbers and a column of letters just like in a real battleship board. If I try to place anything on top of the current grid, it doesn't change anything. I tried to change the foreground color and even changed the picture of one of the cells to something different, and the grid still remained the same; a 11 x 11 grid of animated pixel water.
Second EDIT: I have rewritten my code to draw a grid on top and on the left of the window, but now I am getting a null pointer exception. I don't exactly know what is wrong with this:
EDIT FOUR: Removed un-needed code.
One way to get what you need is to use the same technique I used in the Chess GUI with the columns (A-H) and rows (8-1) as seen below. Have a look over the code and see if you can implement that in your own code.
Failing that, post an MCVE (Minimal Complete and Verifiable Example) of your own (as opposed to uncompilable code snippets). One way to get image(s) for an example is to hot-link to the images seen in this answer (again as the linked code does).
Not sure I really understand the question, but I would think you should be using a GridLayout if you want to display components in a Grid.
//frame.setLayout(new FlowLayout());
frame.setLayout( new GridLayout(0, 11) );
Or maybe you would do something like this
JPanel topPanel = new JPanel( new GridLayout(1, 11) );
// use a loop to create 11 labels for the letters and add the labels to the panel
JPanel leftPanel = new JPanel( new GridLayout(10, 1) )
// use a loop to create 10 labels for the letters and and the labels to the panel
Use your current loop to create your "gamePanel" with the 10 x 10 grid of labels.
Then you add the 3 panels to the frame using:
frame.add(topPanel, BorderLayout.NORTH);
frame.add(leftPanel, BorderLayout.WEST);
frame.add(gamePanel, BorderLayout.CENTER);

Creating Cells for GridLayout in 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...

Layout managers to set this specific layout in java

I have this layout that I need to program for an assignment and this is the first time I have used layout managers in the GUI so I'm having problems getting the layout to match. I need your help
I Have two tabs labeled Account creation and Account transfer (those are ok) what I need is to have the JLabel (Account ID) and the first text field in one line then the next JLabel (Amount) and text field in the line under the first. Under that needs to be the JButton Centered (Create an Account). Lastly the JTextArea (No account) needs to be in a column to the right in the empty space separate from the labels, text fields, and button.
Here is the code I have started with:
public CreatePanel(Vector accountList, TransferPanel transferPanel)
{
this.accountList = accountList;
this.transferPanel = transferPanel;
JLabel l1 = new JLabel("Account ID");
JTextField t1 = new JTextField();
JLabel l2 = new JLabel("Amount");
JTextField t2 = new JTextField();
JButton b1 = new JButton("Create an Account");
JTextArea a1 = new JTextArea("No Account");
JPanel panel1 = new JPanel();
panel1.setLayout(new GridLayout(2,3));
panel1.add(l1);
panel1.add(t1);
panel1.add(l2);
panel1.add(t2);
panel1.add(b1, BorderLayout.SOUTH);
b1.setVerticalAlignment(JLabel.CENTER);
JPanel panel2 = new JPanel();
panel2.add(a1);
a1.setSize(200, 300);
add(panel1);
add(panel2);
}
This is how I would approach it. Instead of adding the outer panel to a frame though, it would be added to a tab of the tabbed pane.
The above is an example of a nested or compound layout. The titled borders show the layouts used and the arguments (if any) used to construct them.
The size of the button is suggested by the content (the text). The sizes of the text fields and text area is suggested in the constructor (which itself has been included as the text value).
To get the TALL effect in the text fields, set an HUGE font but use less columns for the constructor.
See also
Another nested layout.
GridBagLayout is the most powerful Layout that you can use to easily implement grid-like displays. It's a layout with n rows and m columns where each cell is customizable independently of the others in several aspects. In this layout you have to attach a GridBagConstraints object to each panel.add(JComponent, Constraints) as constraints. In the tutorial it's clearly specified what is customizable. It might look a little harsh at the beginning but once you get the hang of it it's great. It's powerful and flexible and you don't have to worry about the uncustomizable restrictions you might encounter with other Layouts.
In your layout, the most inconvenient thing I see is having the "Account ID" label aligned by its center with the TextField AND with the empty space over both of them. It would be easier if the label was aligned with the bottom of the TextField. To solve this I'll assume that the Label and the TextField are inside a panel I constructed beforehand that aligns each other correctly (easily with BorderLayout or GridBagLayout... or anything really) and I'll just place the panels in the Layout.
Then I see this Layout as a GridBagLayout with 3 rows and 2 columns that looks like this:
This is an overview of how I'd put the constraints to specify each component in the layout.
Panel 1 (Account ID Label + TextField)
gridx = 0
gridy = 0
weighty = 0.5
weightx = 0.5
anchor = PAGE_END
fill = HORIZONTAL
Panel 2 (Amount Label + TextField)
gridx = 0
gridy = 1
weighty = 0.0
fill = HORIZONTAL
Button
gridx = 0
gridy = 2
anchor = PAGE_START
weighty = 0.5
TextArea
gridx = 1
gridy = 0
gridheight = 3
weightx = 0.5
fill = BOTH
There is a couple of details I overlooked but the core issues can be aproached with these constraints. The least obvious thing to learn about the GridBagLayout is how do weights work in complex cases, for example what happens when there are several different weightx values in the same column. Does it count the max? or the sum?...
For the sake of discussion, you could avoid having those panels using an extra initial row with an invisible component with weighty > 0 and then having 2 columns: one for the JLabels and the other one for the JTextFields, with the apropiate anchors; the button would have gridwith = 2... but that's totally unnecesary, go for the two auxiliar panels.

Categories

Resources