I need to build a gridline for a crossword puzzle. I wanted to know if I could do it in the same part where I created the JPanel and it's properties instead of doing it in methods?
class CrosswordWindow extends JFrame {
public JPanel crossPanel;
public CrosswordWindow() {
super("Crossword");
setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
/*
bunch of buttons, labels, JLists etc.
*/
int size;
crossPanel = new JPanel();
crossPanel.setBounds(240, 40, 680, 360);
crossPanel.setBackground(Color.white);
crossPanel.setBorder(BorderFactory.createEtchedBorder(1, Color.lightGray, Color.lightGray));
add(crossPanel);
I'm having a tough time trying to draw the grid lines for the crossword.
First off, never do this: setLayout(null);. 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.
But more to your original point, my suggestion is not to draw gridlines. Instead consider using a JPanel that holds a GridLayout, you can give it a horizontal and vertical gap of 1, and set is background to black if you want to show gridlines, and then fill it with either JLabels or JTextFields that accept one single char.
There is a multitide of ways that this might be done, one of the simplest I can think of would be to use a component of some kind for each cell and a series of MatterBorders as the "grid lines"
I also agree with HovercraftFullOfEels, avoid using null layouts, pixel perfect layouts are an illusion within modern UI design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.MatteBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static final Border TOP_LEFT = new MatteBorder(1, 1, 1, 0, Color.DARK_GRAY);
protected static final Border TOP_RIGHT = new MatteBorder(1, 1, 1, 1, Color.DARK_GRAY);
protected static final Border BOTTOM_LEFT = new MatteBorder(0, 1, 1, 0, Color.DARK_GRAY);
protected static final Border BOTTOM_RIGHT = new MatteBorder(0, 1, 1, 1, Color.DARK_GRAY);
public TestPane() {
setLayout(new GridLayout(10, 10));
for (int row = 0; row < 10; row++) {
for (int col = 0; col < 10; col++) {
Border border = null;
int index = (row * 10) + col;
if (row == 0) {
if (col == 9) {
border = TOP_RIGHT;
} else {
border = TOP_LEFT;
}
} else if (row == 9) {
if (col == 9) {
border = BOTTOM_RIGHT;
} else {
border = BOTTOM_LEFT;
}
} else if (col == 9) {
border = BOTTOM_RIGHT;
} else {
border = BOTTOM_LEFT;
}
JLabel cell = new JLabel(" ");
cell.setBorder(border);
add(cell);
}
}
}
}
}
Take a look at How to Use Borders, Laying Out Components Within a Container and How to Use GridLayout for more details
Related
I am developing a simple application, and am currently working on the gui design using Swing. In my program I have a JPanel which I would like to have a background color black like so:
JPanel playerPanel = new JPanel();
playerPanel.setOpaque(true);
playerPanel.setBackground(Color.BLACK);
This code works fine. However, the problem is when I assign a Layout Manager to the panel:
JPanel playerPanel = new JPanel();
playerPanel.setOpaque(true);
playerPanel.setBackground(Color.BLACK);
playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.PAGE_AXIS));
For some reason, this makes the black color of the panel go away. This happens no matter where I place the .setLayout(...) command, before or after the .setBackground(...) and .setOpaque(true).
Why is this, and how do I work around this? How do I keep a black JPanel that uses a BoxLayout manager?
Verify that your panel's content is not obscuring the altered background. Resize the example below, which I've artificially enlarged, to see the effect.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Random;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/57785802/230513
*/
public class BoxTest {
public static final Random random = new Random();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new BoxTest().create();
}
});
}
void create() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.BLACK);
panel.add(Box.createVerticalGlue());
for (int i = 0; i < 4; i++) {
panel.add(new VariablePanel());
panel.add(Box.createVerticalGlue());
}
JFrame f = new JFrame("BoxTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
f.setSize(f.getWidth(), f.getHeight() + 64);
}
}
/**
* A VariablePanel has a label showing its current size,
* as well as a variable number of text items.
*/
class VariablePanel extends JPanel {
private static final String text =
"Sed ut perspiciatis unde omnis iste natus error sit.";
private final JLabel sizeLabel = new JLabel("Size:");
public VariablePanel() {
this.setLayout(new GridLayout(0, 1));
this.add(sizeLabel);
int count = BoxTest.random.nextInt(5) + 1;
for (int i = 0; i < count; i++) {
this.add(new JLabel(text));
}
this.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
int w = e.getComponent().getWidth();
int h = e.getComponent().getHeight();
sizeLabel.setText("Size: " + w + "\u00d7" + h);
}
});
}
}
Swing components (except JLabel) are opaque by default. This means:
you don't need playerPanel.setOpaque(true)
most components you add to the panel will be opaque and cover the background of your playerPanel.
Also, the BoxLayout respects the maximum size of any component you add to the panel. So if you add a component:
like a JButton which has a defined maximum size, you will see the button on top of the playerPanel and the background will surround the button.
like a JPanel, which does not have a defined maximum size, the panel will be resized to fill the entire area of the playerPanel and you won't see the background of the playerPanel.
If you want to see the background of the playerPanel show through a component added to the playerPanel, then you need to use setOpaque(false) on the component. For example:
JPanel child = new JPanel();
child.setOpaque( false );
playerPanel.add( child );
TL;DR
On different DPI settings, components of my application can overlap. To get rid of that I want to check the components width, which always equals 0 after updating the UI. Simplyfied code at the end of the question.
The base problem is that on different DPI settings different parts of my program is the one that reaches farthest to the right. On higher DPI its a dynamically generated label and on lower DPI a fixed width textfield.
I am working with a SpringLayout to deal with possible changing DPI settings. With the above described I have the problem that on specific settings components overlap.
To overcome that, I wanted to check whether the left edge of the one component is further left than the right edge of the other component +10 pixels for more space.
if (labelRight.getX() < (labellist.get(0).getX() + labellist.get(0).getWidth+10)){
//Use one SpringLayout setting for labelRight
} else //Use another SpringLayout setting for labelRight
I assume the problem is that I check the width of the components after they were generated. The user may choose from different settings which will change parts of the panels content. labellist contains the rightmost labels from that area.
I made up a small testprogram to show the problem width the width check:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.UIManager;
public class testsforSO extends JFrame {
private static final long serialVersionUID = -857761460788731909L;
ArrayList<JLabel> lbls = new ArrayList<>();
int lblCount = 6;
JPanel panel = new JPanel();
SpringLayout sl_panel = new SpringLayout();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
testsforSO frame = new testsforSO();
frame.pack();
frame.setVisible(true);
frame.setSize(500, 500);
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public testsforSO() {
getContentPane().add(panel);
updateUI();
JButton incr = new JButton("Increment here!");
incr.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
for (int i=0; i<=lbls.size()-1; i++){
panel.remove(lbls.get(i));
}
lblCount++;
lbls.clear();
updateUI();
panel.repaint();
panel.revalidate();
System.out.println("Width of slimest label: " + lbls.get(0).getWidth());
System.out.println("Width of widest label : " + lbls.get(lbls.size()-1).getWidth());
}
});
panel.add(incr);
sl_panel.putConstraint(SpringLayout.NORTH, incr, 10, SpringLayout.NORTH, panel);
sl_panel.putConstraint(SpringLayout.EAST, incr, -10, SpringLayout.EAST, panel);
panel.setLayout(sl_panel);
}
private void updateUI(){
for (int i = 0; i <= lblCount; i++) {
String abc = new String();
for (int j = 0; j <= i; j++) {
abc += "xx";
}
JLabel bfrLbl = new JLabel(abc);
lbls.add(bfrLbl);
panel.add(bfrLbl);
}
for (int i = 0; i <= lbls.size() - 1; i++) {
if (i > 0)
sl_panel.putConstraint(SpringLayout.NORTH, lbls.get(i), 8, SpringLayout.SOUTH, lbls.get(i - 1));
else
sl_panel.putConstraint(SpringLayout.NORTH, lbls.get(i), 8, SpringLayout.NORTH, panel);
}
}
}
When pressing the button, the UI gets updated with one more label at the bottom. The SpringLayout is fitted accordingly and the panel gets revalidated after. However the output of the width is 0. Why is it and what can I do against that?
After further investigation I found a way to correctly read the width of the component:
sl_panel.getConstraint(SpringLayout.WIDTH, lbls.get(0)).getValue();
Does the trick. Seems to be a very massive line compared to
lbls.get(0).getWidth();
though.
(Beginner)
Hi, sorry for the specific question, but I'm having errors constantly ambush me out of nowhere with a program that I would expect to be quite simple.
I was planning on creating a program that would allow the user to click on JPanels with in a GridLayout in order to change their colours. Imagine a poor man's pixel art program, like the old MS Paint.
The plan was to create a JFrame set to GridLayout, of an integer width and height, and fill the grids with JPanels with a 2d array and a for loop. I would then put a MouseListener into each individual JPanel to listen for a mouseClicked, which would change the background colour of the panel clicked.
package pixelpainter;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class PixelPainter extends JPanel {
int width = 20;
int height = 20;
int pixSize = 10;
Color bGColor = Color.WHITE;
Dimension pixDim = new Dimension(pixSize,pixSize);
private JPanel panelClicked = null;
JFrame frame= new JFrame();
/**
* #param args the command line arguments
*/
public PixelPainter()
{
initGUI();
}
public void initGUI() {
frame.setLayout(new GridLayout(height, width, 0, 0));
frame.setSize((height * pixSize), (width * pixSize));
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
int[][] pixGrid = new int [width][height];
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
JPanel pixel[][] = new JPanel[width][height];
frame.add(pixel[row][col]);
pixel[row][col].setBackground(bGColor);
pixel[row][col].setPreferredSize(pixDim);
pixel[row][col].setBorder(BorderFactory.createLineBorder(Color.BLACK));
pixel[row][col].addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent click)
{
JPanel selectedPixel = (JPanel) getComponentAt(click.getPoint());
if (selectedPixel == null || selectedPixel == PixelPainter.this)
{
return;
}
if (selectedPixel != null)
{
selectedPixel.setBackground(Color.BLACK);
}
}
#Override
public void mousePressed(MouseEvent press)
{
}
});
}
}
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
#Override
public void run(){
new PixelPainter().setVisible(true);
}
});
}
}
Ideally I would be using the 2d array JFrame when filling in the colours, but apparently they must be final or effectively final.
I rearranged your code to group like things together.
Here's the GUI I created.
I made the following changes to your code.
I had the main class implement Runnable. Since the EventQueue invokeLater method expects a Runnable, you might as well make the main class a Runnable.
I moved the JPanel creation into the createPixels method. Your methods should do one thing and do that one thing well.
The initGUI method now just creates the JFrame.
I moved the sizing integers into the new PixelPanel class. The class that extends a JPanel has to provide a preferred size. The JFrame pack method then creates a JFrame of the correct size.
In the paintComponent method of the PixelPanel class, all I do is paint. You shouldn't do anything else but paint in the paintComponent method.
I made the pixels bigger, so I could left click and right click on a pixel easier. The left click makes the pixel blue, and the right click erases the blue (makes the pixel white).
Because of the model / view / controller pattern, I pulled the mouse adapter code into its own class. Separating the concerns makes getting each part of the GUI working properly much easier.
And here's the code.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PixelPainter implements Runnable {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new PixelPainter());
}
#Override
public void run() {
initGUI();
}
public void initGUI() {
frame = new JFrame("Pixel Art");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createPixels());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createPixels() {
int width = 30;
int height = 20;
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(height, width, 0, 0));
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
PixelPanel pixelPanel = new PixelPanel();
pixelPanel.addMouseListener(new ColorListener(pixelPanel));
panel.add(pixelPanel);
}
}
return panel;
}
public class PixelPanel extends JPanel {
private static final long serialVersionUID = 8465814529701152253L;
private static final int PIXEL_SIZE = 20;
private Color backgroundColor;
public PixelPanel() {
this.backgroundColor = Color.WHITE;
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
this.setPreferredSize(new Dimension(PIXEL_SIZE, PIXEL_SIZE));
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackgroundColor());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class ColorListener extends MouseAdapter {
private PixelPanel panel;
public ColorListener(PixelPanel panel) {
this.panel = panel;
}
#Override
public void mousePressed(MouseEvent event) {
if (event.getButton() == MouseEvent.BUTTON1) {
panel.setBackgroundColor(Color.BLUE);
panel.repaint();
} else if (event.getButton() == MouseEvent.BUTTON3) {
panel.setBackgroundColor(Color.WHITE);
panel.repaint();
}
}
}
}
Your code is creating a new pixel Array inside the loop. The idea is to create the Array out side the loo and then create a new JPanel to add to the Array inside the loop.
Something like:
int[][] pixGrid = new int [width][height];
JPanel pixel[][] = new JPanel[width][height];
and
//JPanel pixel[][] = new JPanel[width][height];
pixel[row][col] = new JPanel();
Now inside the listener because you add the listener to every panel you can access the panel directly without worrying about the mouse point:
//JPanel selectedPixel = (JPanel) getComponentAt(click.getPoint());
JPanel selectedPixel = (JPanel)click.getSource();
In fact you can create a single MouseListener to add to each panel instead of creating a different listener for each panel because the above code is generic.
I try to do this:
but if I use GridLayout, the heigth is bery small
PanelPost.setLayout(new GridLayout(20, 1, 0, 12));
for(int y=0;y<15;y++){
JPanel p=new JPanel();
p.setBackground(Color.RED);
p.setLayout(null);
p.setSize(PanelPost.getWidth(),150);
PanelPost.add(p);
}
if I use FlowLayout, only displays some points
With BoxLayout
setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
the panels are in the center and the width and height are not respected
and if i use setLayout(null) the scroll does not work then
What is the best way to do this? :C
Your problem starts here -> p.setLayout(null);. GridLayout will use the preferredSize of the components to determine the cell sizes it wants to use. This value is determine by the panel's layout manager...which you've now discarded...
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(new ListOfStuffPane()));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ListOfStuffPane extends JPanel implements Scrollable {
public ListOfStuffPane() {
setLayout(new GridLayout(20, 1, 0, 12));
for (int y = 0; y < 15; y++) {
JPanel p = new JPanel(new FlowLayout(FlowLayout.LEFT));
p.setBackground(Color.RED);
p.add(new JLabel("Boo"));
add(p);
}
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(100, 200);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 128;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 128;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
}
See Laying Out Components Within a Container for more details
You can use BoxLayout to solve this issue. Let me explain. First of all, let's consider your scenario; you want a scrollable list, right ? with components having fixed height, but varying width (since you had used the container's getWidth() as the width). So, for creating some thing "Scrollable", you need a ScrollPane in Java. JScrollPane is the preferred scrollable container. Now set this JScrollPane as the basecontainer. Now create a new JPanel object with the BoxLayout set as having Y_AXIS orientation. Now set this newly created JPanel object as the viewport of the JScrollPane container. So, by here, we had created a scrolling container.
Next step is what you mentioned in your code sample, ie adding 'post' elements. For that just as you did, inside the for loop create each JPanel objects. But instead of using setSize() method, you should use setPreferredSize(new Dimension(w,h)) method. This forces your components to render according to the dimension specified. For inserting vertical gaps between each post objects, you can use java.awt.Box class's createVerticalStrut(int height) method. I am giving the code below. Feel free to ask if you have any doubt. :)
import java.awt.*;
import javax.swing.*;
public class LinkedInLayout extends JFrame{
public LinkedInLayout(){
setTitle("Linked In Layout Resolution");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBounds(0,0,500,520);
JPanel postPane = new JPanel(new FlowLayout(FlowLayout.LEADING));
JScrollPane container = new JScrollPane(postPane,ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
setContentPane(container);
postPane.setLayout(new BoxLayout(postPane,BoxLayout.Y_AXIS));
for(int y=0;y<15;y++){
postPane.add(Box.createVerticalStrut(35));
JPanel p=new JPanel();
p.setBackground(new Color(247,221,221));
p.setLayout(null);
p.setPreferredSize(new Dimension(postPane.getWidth(),75));
postPane.add(p);
}
setVisible(true);
}
public static void main(String[] args) {
new LinkedInLayout();
}
}
I'm making an app where a user would be able to add a button to the screen or remove it (I don't have those options implemented yet). So, for now I'm manually populating it with a for() loop and manually removing one of the buttons. My problem is that after the button has been removed (the removal action in the main()), there's just a blank spot. I want to be able to repaint the screen after I remove one of those buttons. In this example, index 2 (block #3) has been removed, leaving an empty space, where it was before... and I have no idea how to repaint it. I've tried validating or repainting from different places in the program with no success.
Here's the code (P.S. I'm sure my code is not the most efficient way to accomplish what I'm trying to and I'm using setLayout(null), which is not a preferred method, but for now I'm just trying to learn certain things and then expand on that to better myself and my code):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
class TestApp extends JFrame{
JFrame frame = new JFrame("Test Program");
ArrayList<JButton> grid = new ArrayList<JButton>();
private int w = 14;
private static int amount = 102;
private static int counter = 0;
//Default Constructor (sets up JFrame)
TestApp(){
frame.setLayout(null);
frame.setPreferredSize(new Dimension(1186, 880));
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setResizable(false);
paintGrid();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void newWindow()
{
JFrame select_win = new JFrame("Selected Frame");
JPanel select_panel = new JPanel();
select_panel.setPreferredSize(new Dimension(600, 800));
select_panel.setBackground(Color.ORANGE);
select_win.add(select_panel);
select_win.pack();
select_win.setResizable(false);
select_win.setVisible(true);
select_win.setLocationRelativeTo(null);
}
private void paintGrid()
{
for(int i = 0, y = 4; i < ((amount / w) + (amount % w)); i++, y += 104)
{
for(int j = 0, x = 4; j < w && (counter < amount); j++, x += 84)
{
addBlock(counter, x, y);
counter++;
}
}
}
//Adds a block
private void addBlock(int index, int x, int y){
int height = 100;
int width = 80;
grid.add(new JButton("counter: " + (counter + 1)));
(grid.get(index)).addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
newWindow();
}
});
}
});
(grid.get(index)).setBorder(new LineBorder(Color.BLACK));
(grid.get(index)).setBackground(Color.YELLOW);
(grid.get(index)).setVisible(true);
(grid.get(index)).setBounds(x, y, width, height);
frame.add(grid.get(index));
}
//Removes a block
private void removeBlock(int index){
frame.remove(grid.get(index));
grid.remove(index);
amount--;
counter--;
}
public static void main(String [] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestApp app = new TestApp();
//testing block removal
app.removeBlock(2);
}
});
}
}
The proper way would be: revalidate()
revalidate() method informs the layout manager that this component and all parents above it are marked as needing to be laid out. This means the Layout Manager will try to realign the components. Often used after removing components.
I would think that you will only know this if you are actually using Swing
As you said, is not good to use NullLayout. To fix your problem you only need to do two changes:
Change the layout to FlowLayout on the constructor, like this:
frame.setLayout(new FlowLayout());
Change the setBounds call to a setPreferredSize:
(grid.get(index)).setPreferredSize(new Dimension(width, height));
Now the FlowLayout will automatically align your items and you don't need to worry about it anymore.