Creating bold cell border lines JTable - java

I'm trying to create a Sudoku game and I am having trouble creating the bold lines to break it into 3 x 3 blocks. My recent attempt was to impose an image on top of the table using JLabel. The problem is the label completely covers the JTable. I goofed around with setting the opacity of the label and the table with no luck. Here are some images to show what I'm aiming for:
The current look:
The goal:
If you could advise me as to a method I can use to create these lines, it would be greatly appreciated. No direct answers needed, just a point in the right direction.

Check out Table Row Rendering.
It shows how to provide a custom Border for each cell in a row.
So you would need to modify the code to provide multiple different Borders depending on the cell being rendered.

For any board game I'd tend to use buttons in a grid layout (or a group of grid layouts) like in this mine sweeper game or this chess board.
For the borders in this GUI though, I'd use a 3 x 3 group of nine grid layouts, each of which has a LineBorder. By default the border would go around all four sides of the panel it is displayed in, but where they meet the border would be double width, thereby coming close to recreating the second image.
E.G.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.*;
public class Soduko {
private JComponent ui = null;
Soduko() {
initUI();
}
public void initUI() {
if (ui!=null) return;
ui = new JPanel(new GridLayout(3,3));
ui.setBorder(new EmptyBorder(4,4,4,4));
ArrayList<Integer> values = new ArrayList<>();
for (int ii=0; ii<34; ii++) {
values.add(0);
}
Random r = new Random();
for (int ii=34; ii<81; ii++) {
values.add(r.nextInt(9)+1);
}
Collections.shuffle(values);
int count=0;
for (int ii=0; ii<9; ii++) {
JPanel p = new JPanel(new GridLayout(3, 3));
p.setBorder(new LineBorder(Color.BLACK, 2));
ui.add(p);
for (int jj=0; jj<9; jj++) {
int v = values.get(count++).intValue();
String s = v>0 ? "" + v : "";
p.add(new JButton(s));
}
}
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
Soduko o = new Soduko();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}

Related

GridLayout is showing odd behavior

I am trying to create a grid comprised of 100 squares. My code below is extremely buggy and I am not sure why.
import javax.swing.*;
import javax.swing.border.Border;
import java.awt.*;
public class snake extends JFrame
{
public static void main(String[] args)
{
Border whiteLine = BorderFactory.createLineBorder(Color.white);
//-----------FRAME
JFrame frame = new JFrame();
frame.setSize(1000,1000);
frame.setTitle("Snake");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.black);
frame.setVisible(true);
frame.setLayout(new GridLayout(10,10));
//-----------FRAME
//-----------PANELS
Dimension panelDimension = new Dimension(20,20);
int counter = 0;
JPanel[][] p = new JPanel[10][10];
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
p[i][j] = new JPanel();
//p[i][j].setPreferredSize(panelDimension);
p[i][j].setBackground(Color.red);
//p[i][j].setLocation(490,490);
p[i][j].setBorder(whiteLine);
p[i][j].setVisible(true);
frame.getContentPane().add(p[i][j]);
counter+=1;
}
}
System.out.println("counter: " + counter);
}
}
When I run the code like this it shows a grid comprised of 2 columns the first column has 7 rows and the second column has 6. Sometimes it even shows other incorrect numbers of columns and rows. I am not sure why it doesn't create a grid of 10 rows 10 columns.
You've got several problems including:
Calling setVisible(true) on the JFrame before adding components, before calling pack() on the top-level window. This can lead to wonky positioned components within our GUI's or even GUI's that remain empty
Not calling pack() on the JFrame after adding components and before setting it visible
Setting the size of the JFrame. Let the layout managers, containers and components do this for you (which is what calling pack() is for)
Setting it to a bad size, a "perfect square", one that ignores the menu bar that the OS adds,
...
For example:
package foo01;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class SnakePanel extends JPanel {
private static final int CELL_WIDTH = 80;
private static final Dimension CELL_DIMENSION = new Dimension(CELL_WIDTH, CELL_WIDTH);
private static final int COLUMNS = 10;
private static final int GAP = 2;
private static final Color BG_COLOR = Color.WHITE;
private static final Color CELL_COLOR = Color.RED;
public SnakePanel() {
setBackground(BG_COLOR);
// add a white line around the grid
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
// create a grid with gaps that show the background (white) color
setLayout(new GridLayout(COLUMNS, COLUMNS, GAP, GAP));
for (int row = 0; row < COLUMNS; row++) {
for (int col = 0; col < COLUMNS; col++) {
JPanel cell = new JPanel(); // create a new cell
cell.setPreferredSize(CELL_DIMENSION); // cheating here. Better to override getPreferredSize()
cell.setBackground(CELL_COLOR);
add(cell);
// give the cell JPanel some simple behavior:
cell.addMouseListener(new MyMouse(col, row));
}
}
}
private class MyMouse extends MouseAdapter {
private int col;
private int row;
public MyMouse(int col, int row) {
this.col = col;
this.row = row;
}
#Override
public void mousePressed(MouseEvent e) {
System.out.printf("Mouse pressed row and column: [%d, %d]%n", row, col);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
// create the main JPanel
SnakePanel snakePanel = new SnakePanel();
// create the JFrame
JFrame frame = new JFrame("Snake");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// add the main JPanel to the JFrame
frame.add(snakePanel);
// pack the JFrame -- tells the layout managers to do their things
frame.pack();
// if we want to center the GUI:
frame.setLocationRelativeTo(null);
// only *now* do we display the GUI
frame.setVisible(true);
});
}
}
Some notes on the code:
Any code within the Runnable passed into the SwingUtilities.invokeLater(...) method is called on the Swing event thread, which is a wise thing to do when creating a Swing GUI
SwingUtilities.invokeLater(() -> {
// ....
});
First, create the main JPanel that is held by the JFrame:
SnakePanel snakePanel = new SnakePanel();
Then create the JFrame, add that JPanel and call pack(). The pack call tells the layout managers to do there thing, to lay out components within containers, to size things based on their preferred sizes and their layouts:
JFrame frame = new JFrame("Snake");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(snakePanel);
frame.pack();
if we want to center the GUI:
frame.setLocationRelativeTo(null);
only now do we display the GUI
frame.setVisible(true);

Reordering labels

I'm creating labels dynamically from an array in a FlowLayout JPanel, storing them in a JLabel array for future reference. They are displayed from left to right as intended.
I want to move one of the labels to the beginning (leftmost) of the panel.
I don't mind if the whole array shifts or just two labels swap places:
apple orange pear cherry melon
|
cherry apple orange pear melon
or
cherry orange pear apple melon
I've swapped array entries, then revalidate() and repaint(), but nothing happens.
Is there an easy way to move swing components around without removing all and then re-adding them to the panel or copying all the properties from one label to the other (I have others defined, not just the text)?
Here is a stripped down version of my code:
import javax.swing.*;
public class Test extends JPanel {
public Test () {
String entries[] = { "apple", "orange", "pear", "cherry", "melon" };
JLabel[] lbls = new JLabel[entries.length];
for (int i = 0; i < entries.length; ++i) {
lbls[i] = new JLabel();
lbls[i].setText(entries[i]);
add(lbls[i]);
}
// swap array entries
JLabel tmplbl = new JLabel();
tmplbl = lbls[3];
lbls[3] = lbls[0];
lbls[0] = tmplbl;
revalidate();
repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setContentPane(new Test());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
}
});
}
}
I've swapped array entries
Swapping entries in an array does nothing. The Array has nothing to do with the panel.
So you need to adjust the components on the panel.
I want to move one of the labels to the beginning (leftmost) of the panel.
Well that is a different requirement than "swapping". It is also easier.
You can add a component to a panel and specify its position in the panel, so adding a component to the beginning is easy because its position will always be zero.
So to move the 3rd component to the beginning the code would be something like:
Component component = panel.getComponent(2);
panel.add(component, 0);
panel.revalidate();
panel.repaint();
If you really want a swap, then the code would be similar. You would get the component at both locations and then add the one component back to the lower location first and the add the other component back to the higher location.
There are a couple of things to fix before fixing your error:
Here are 2 errors in this line: public class Test extends JPanel {
Class name, do you know how many people call their classes Test? A LOT! Make it more descriptive, like SwapLabelsTest.
extends JPanel, you're not changing the behavior of the JPanel so there's no need to extend it in this case, just create a new instance of JPanel.
Don't put everything in the constructor, it's better to have an initialize() method or something like that (createAndShowGUI() in the code below) to handle GUI construction. It may seem like the easiest way, but separating that part will come handy later on when the project becomes bigger.
Move your variables to a bigger scope, for easier handling, unless those variables are local to the method, this will improve performance and readability.
Include a component that detects events, such as a JButton so that your swapping execution will happen when that event is triggered (a button click).
Your swapping logic seems a little bit odd, you have created new JLabels there and are trying to swap them, but it's better to have a MVC kind of pattern here, so that you swap the values in the array and then just update the UI after with those changes.
You may be asking, but how do I do that? Well like this:
String tmpString = entries[3];
entries[3] = entries[1];
entries[1] = tmpString;
The above code swaps the values in the entries array, all we have to do now is update each label with lbl[i].setText(entries[i]) inside of a for-loop.
So, you end up with something like this in the end:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
private JFrame frame;
private JPanel panel;
private String entries[] = { "apple", "orange", "pear", "cherry", "melon" };
private JLabel[] lbls = new JLabel[entries.length];
JButton button;
private void createAndShowGUI() {
panel = new JPanel();
for (int i = 0; i < entries.length; ++i) {
lbls[i] = new JLabel();
lbls[i].setText(entries[i]);
panel.add(lbls[i]);
}
button = new JButton("Swap 1 and 3");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String tmpString = entries[3];
entries[3] = entries[1];
entries[1] = tmpString;
reloadLabels();
}
});
frame = new JFrame("Test");
frame.add(panel);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
}
private void reloadLabels() {
for (int i = 0; i < entries.length; ++i) {
lbls[i].setText(entries[i]);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test().createAndShowGUI();
}
});
}
}
Everytime you click the button, the items 1 & 3 (indexes) will be swapped and the UI will be updated (as .setText triggers an UI update).

placing 64 buttons randomly in a girdlaout java

The goal is to create a create a game like rubrics cube where the user has to rearrange the buttons according to the matched color. This is what I did to place the buttons randomly, but it doesn't work when the buttons are presented. The random order is taken as the ordered order if that makes sense. Any ideas on how to fix this?
while(list1.size()!=501){
int x=rand.nextInt(8);
list1.add(x);
}
while(list2.size()!=501){
int x=rand.nextInt(8);
list2.add(x);
}
for(int b=0;b<500;b++){
int l= list1.get(b);
//System.out.println(l);
int j= list2.get(b);
panel.add(buttons[l][j]);
//System.out.println(buttons[l][j].getBackground());
}
Consider:
Giving the buttons a value of some sort that represents their true order. There are several ways to do this, including putting them in an array of specified order, or extending JButton and giving your class an int value field, or using a HashMap
Place these buttons into an ArrayList<JButton>
Shuffling the ArrayList via Collections.shuffle(...)
Then adding the buttons to your GUI
Alternatively, you could use non-shuffled JButtons and instead shuffle AbstractActions which you then set into the buttons.
The details of any solution will depend on the details of your current program, something that we don't yet know enough about. If you need more detailed help, do consider creating and posting a valid MCVE in your question.
For example, compile and run this, and then read the comments:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.*;
public class RandomButtons extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ROWS = 8;
private JButton[][] buttons = new JButton[ROWS][ROWS];
private List<JButton> buttonList = new ArrayList<>();
private JPanel buttonsPanel = new JPanel(new GridLayout(ROWS, ROWS));
public RandomButtons() {
for (int i = 0; i < buttons.length; i++) {
for (int j = 0; j < buttons[i].length; j++) {
// create new JBUtton
final JButton button = new JButton("Button");
// put into array
buttons[i][j] = button;
// put into ArrayList
buttonList.add(button);
// unique value 0 to 63 for each button
// order it has been created
final int value = i * ROWS + j;
// create one of 64 different color hues using value above
float hue = ((float) value) / (ROWS * ROWS);
float sat = 0.7f; // reduce sat so we can see text
float brightness = 1.0f;
Color color = Color.getHSBColor(hue, sat, brightness);
button.setBackground(color); // set button's color
button.addActionListener(e -> {
// display the button's order
System.out.println("Value: " + value);
});
}
}
randomizeButtons();
JButton randomizeButton = new JButton("Randomize");
randomizeButton.addActionListener(e -> { randomizeButtons(); });
JButton orderButton = new JButton("Put in Order");
orderButton.addActionListener(e -> { orderButtons(); });
JPanel bottomPanel = new JPanel(new GridLayout(1, 2));
bottomPanel.add(randomizeButton);
bottomPanel.add(orderButton);
setLayout(new BorderLayout());
add(buttonsPanel, BorderLayout.CENTER);
add(bottomPanel, BorderLayout.PAGE_END);
}
public void randomizeButtons() {
buttonsPanel.removeAll(); // remove all buttons
Collections.shuffle(buttonList); // shuffle the ArrayList
// re-add the buttons **using the ArrayList**
for (JButton jButton : buttonList) {
buttonsPanel.add(jButton);
}
// tell JPanel to layout its newly added components
buttonsPanel.revalidate();
// and then paint them
buttonsPanel.repaint();
}
public void orderButtons() {
buttonsPanel.removeAll(); // remove all buttons
// re-add the buttons **using the 2D array**
for (JButton[] buttonRow : buttons) {
for (JButton jButton : buttonRow) {
buttonsPanel.add(jButton);
}
}
buttonsPanel.revalidate();
buttonsPanel.repaint();
}
private static void createAndShowGui() {
RandomButtons mainPanel = new RandomButtons();
JFrame frame = new JFrame("RandomButtons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}

Jpanel first item centers despite position of 0,0

I am trying to programmatically create a background grid for a project I am working on. The problem is that the first item I add to the jpanel is always painted at center of the jpanel. I am expressly trying to put it at the top right. I have even tried just placing a dummy jlabel down first and going from there but then the second and third element just overlap each other. This bug has me thoroughly dumbfounded. Any help would be great.
private void evalWindow(){
window.removeAll();
window.setBorder(BorderFactory.createLineBorder(Color.black));
JLabel p = new JLabel(new ImageIcon(tiles[0][0].getImage()));
p.setLocation(0,0);
System.out.println("1: " + p.getLocation());
p.setSize(64,64);
window.add(p);
System.out.println("2: " + p.getLocation());
for(int i = 0; i < x; i++){
for (int j = 0; j < y; j++){
JLabel piclabel = new JLabel(new ImageIcon(tiles[i][j].getImage()));
piclabel.setSize(64,64);
piclabel.setLocation(64*j, 64*i);
System.out.println("1: " + piclabel.getLocation());
window.add(piclabel);
}
}
}
sample image:
As mentioned elsewhere, a GridLayout would be the easiest way to layout the grid positions. It can be as simple as:
public void initUI(Image img) {
ui = new JPanel(new GridLayout(0,8));
ImageIcon icon = new ImageIcon(img);
for (int ii=0; ii<32; ii++) {
ui.add(new JLabel(icon));
}
}
This is the effect:
Here is the MCVE that produces the above GUI. In future, please post code in the form of an MVCE.
import java.awt.*;
import java.awt.image.*;
import java.io.IOException;
import javax.swing.*;
import javax.imageio.*;
import java.net.URL;
public class ImageGrid {
private JComponent ui = null;
String imageURL = "https://i.stack.imgur.com/DlSgb.png";
ImageGrid() {
try {
BufferedImage img = ImageIO.read(new URL(imageURL));
BufferedImage subImg = img.getSubimage(2, 2, 64, 64);
initUI(subImg);
} catch (IOException ex) {
ex.printStackTrace();
}
}
final public void initUI(Image img) {
ui = new JPanel(new GridLayout(0,8));
ImageIcon icon = new ImageIcon(img);
for (int ii=0; ii<32; ii++) {
ui.add(new JLabel(icon));
}
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
ImageGrid o = new ImageGrid();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
You do not tell us the type of the variable "window", but I'll assume it's a JFrame or something else that will take a layout manager. Components on Swing windows are not normally placed with absolute positions like 0,0; they are added to containers which are set to use an extension of LayoutManager, and the LayoutManager class manages where the components added to it will go.
FlowLayout adds things in order; GridLayout puts things in equal-size cells on a grid; BorderLayout allows placement in one of 5 areas, etc. The LayoutManager scheme saves you from having to mess with positioning while the user changes the size of the outer window, allows some elements to grow and shrink with available overall size, etc.
If you must place things with absolute positions, there is a NullLayoutManager, but in over 20 years of Java programming and a number of different Swing applications, I've never seen one.
Read about LayoutManagers. I'm sure whatever one is default for your window variable is placing your component in the center and ignoring the absolute placement.

Adding a Button array to a JPanel

I have another problem with my project. I have created this GameBoard made out of JButtons (it works as it's supposed to now) but I would need to add it to a JPanel. I need to display other things (in other panels) in the window. But when I tried to add the Button Array to a panel, and add that panel to the window, crazy things started happening (the buttons were very small, the grid was completely broken etc.). How could I add this Button Array to a JPanel and put that in the center of the window?
Here is my code:
package city;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GameBoard extends JFrame implements ActionListener {
// The overall box count in chess board
public static final int squareCount = 64;
// The Array of buttons (the GameBoard itself)
public Button[][] button = new Button[8][8];
// Colors for all the buttons
public Color defaultColor = Color.WHITE;
public Color darkRedColor = Color.RED;
public Color darkBlueColor = Color.BLUE;
public Color lightBlueColor = Color.CYAN;
public Color lightRedColor = Color.PINK;
public GameBoard(String title) {
// Creates the buttons in the array
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
button[i][j] = new Button();
add(button[i][j]);
button[i][j].setBackground(defaultColor);
}
}
// Build the window
this.setTitle(title); // Setting the title of board
this.setLayout(new GridLayout(8, 18)); // GridLayout will arrange elements in Grid Manager 8 X 8
this.setSize(650, 650); // Size of the chess board
this.setVisible(true); // Sets the board visible
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // If you close the window, the program will terminate
this.setResizable(false); //The window is not resizable anymore ;)
// Sets some text on the buttons
button[0][3].setText("Red's");
button[0][4].setText("Gate");
button[7][3].setText("Blue's");
button[7][4].setText("Gate");
// Colors the buttons
newGame();
}
// Colors the buttons
public void newGame() {
button[0][0].setBackground(lightRedColor);
button[0][1].setBackground(lightRedColor);
button[0][2].setBackground(lightRedColor);
button[0][3].setBackground(darkRedColor);
button[0][4].setBackground(lightRedColor);
button[0][5].setBackground(lightRedColor);
button[0][6].setBackground(lightRedColor);
button[0][7].setBackground(lightRedColor);
button[1][3].setBackground(darkRedColor);
button[7][0].setBackground(lightBlueColor);
button[7][1].setBackground(lightBlueColor);
button[7][2].setBackground(lightBlueColor);
button[7][3].setBackground(lightBlueColor);
button[7][4].setBackground(darkBlueColor);
button[7][5].setBackground(lightBlueColor);
button[7][6].setBackground(lightBlueColor);
button[7][7].setBackground(lightBlueColor);
button[6][4].setBackground(darkBlueColor);
}
// The ActionListener is not yet used
public void actionPerformed(ActionEvent ae) {
String action = ae.getActionCommand();
}
public static void main(String[] args) {
String title = "City - A Two-Player Strategic Game";
GameBoard gameBoard = new GameBoard(title); // Creating the instance of gameBoard
}
}
And here is a screenshot of the bad GUI that generated:
Bad GUI
Thanks for any help, and good luck everyone with a similar problem!
Is this what you are looking for? I am not very sure what´s the problem.
This adds the buttons to a new JPanel and adds this panel to the frame.
JPanel panel = new JPanel(new GridLayout(8, 8));
panel.setSize(650,650);
getContentPane().add(panel, BorderLayout.CENTER);
// Creates the buttons in the array
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
button[i][j] = new JButton();
panel.add(button[i][j]);
button[i][j].setBackground(defaultColor);
}
}
// Build the window
this.setTitle(title); // Setting the title of board
getContentPane().setLayout(new BorderLayout());
this.setSize(650, 650); // Size of the chess board
this.setVisible(true); // Sets the board visible
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // If you close the window, the program will terminate
this.setResizable(false); //The window is not resizable anymore ;)

Categories

Resources