grid layout with JPanels java - java

I am trying to create a program in Java that is sort of like a board game. I have gameTiles, currently with only one color as I try to get the layout right. I want the tiles to appear about halfway of the width of the window and extend to the bottom, maybe anywhere from 9x9 or 11x11 different tiles. I have tried to use the grid layout to get these to be close together, not necessarily touching but close enough to look like a board game. However, no matter what I try, the tiles are space so far apart, and change shape when I resize window. I have been using the GridLayout manager to try to achieve this. Here is my code.
import java.awt.GridLayout;
import javax.swing.JFrame;
public class GameWindow extends JFrame {
public GameWindow(String title){
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1200,400);
}
/**
* #param args
*/
public static void main(String[] args) {
GameWindow gameWindow = new GameWindow("Game");
gameWindow.setLayout(new GridLayout(0,2));
GameTile greenTile = new GameTile(0,true,0,10);
GameTile greenTile2 = new GameTile(0,true,0,10);
gameWindow.add(greenTile);
gameWindow.add(greenTile2);
gameWindow.setVisible(true);
}
}
This is the GameWindow.java file. The GameTile.java I have so far (which is still mainly not finished just for testing) is as follows:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
/**
* #author Zachary Parks
*
*/
public class GameTile extends JPanel {
private Color color;
private Color[] colors = {Color.BLUE, Color.YELLOW, Color.BLACK};
private int score, multiplier,initialX,initialY;
private boolean positiveEffect;
public GameTile(int colorTile, boolean effect, int initX, int initY){
color = colors[colorTile];
score = getScore(color);
multiplier = getMultiplier(color);
positiveEffect = effect;
initialX = initX;
initialY = initY;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Image image = null;
try {
image = ImageIO.read(new File("greentile.png"));
} catch (IOException e) {
e.printStackTrace();
}
g.drawImage(image,initialX,initialY,this);
}
private int getMultiplier(Color color2) {
return 0;
}
private int getScore(Color color2) {
return 0;
}
/**
* Method that returns the data from the tile in
* array of int. 0 index = added score, 1 index = tile effect score
* #return
*/
public int[] getData() {
int [] scoreData = null;
scoreData[0] = score*multiplier;
return null;
}
}
A lot of the code is still in progress, like the GameTile's properties, all I'm trying at this point is get the tiles next to each other.
This is what I am getting:

To add tiles like a grid. A tileSize variable is great to have. Let's say the image/tile is 32 pixels.
public static final tileSize = 32;
With this, we can now add tiles using a for loop:
for(int x = 0; x < SCREEN_WIDTH / GameTile.tileSize; x++) { // loop through as many tiles fit the x-axis.
for(int y = 0; y < SCREEN_HEIGHT / GameTile.tileSize; y++) { // do the same with y-axis
GameTile greenTile = new GameTile(0,true, x * GameTile.tileSize , y * GameTile.tileSize);
gameWindow.add(greenTile);
}
}
SCREEN_WIDTH and SCREEN_HEIGHT is the size of your JFrame.
Keep in mind this loops through the whole screen, you wanted the half but it's easily configurable.
Please format your code next time (tab in), much easier to read and help you.
I highly recommend moving image = ImageIO.read(new File("greentile.png")); into the constructor, right now you're loading the image every framerate for every gameTile which will cause performance drop.
Also do not have a JPanel for every GameTile. Instead keep it in your main drawing class and loop through all GameTiles using an ArrayList
public static ArrayList<GameTile> gameTiles = new ArrayList<GameTile>();
protected void paintComponent(Graphics g) {
for(int i = 0; i < gameTiles.size(); i++) {
gameTiles.get(i).draw(g);
}
So instead of adding a JPanel to the JFrame for every gameTile, we draw gameTiles at the specified coordinates. Good luck!
To answer your question in the comment field: the code will look similar to this
public class GameWindow extends JPanel {
public static ArrayList<GameTile> gameTiles = new ArrayList<GameTile>();
public static void main(String[] args) {
new GameWindow();
}
public GameWindow() {
JFrame frame = new JFrame();
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.setVisible(true);
// add all the tiles (I just use x and y as parameters here)
gameTiles.add(new GameTile(10, 10));
gameTiles.add(new GameTile(50, 10));
}
public void paintComponent(Graphics g) {
for(int i = 0; i < gameTiles.size(); i++) {
gameTiles.get(i).draw(g);
}
}
}
And inside your GameTile class, remove the extends JPanel. And rename the paintComponent as draw or something alike.

However, no matter what I try, the tiles are space so far apart, and change shape when I resize window
A GridLayout expands each cell to fill the space available to the component. If you only have two cells each cell will take up half the width of the frame.
So the solution is to wrap your tile panel into another panel that will respect the preferred size of the tiles. So don't set the layout of the frame, set the layout of a panel holding the tiles:
Something like:
JPanel tilePanel = new JPane( new GridLayout(0, 2, 5, 5) );
tilePanel.add( new GameTile(...) );
tilePanel.add( new GameTile(...) );
JPanel wrapper = new JPanel( new GridBagLayout() );
wrapper.add(tilePanel, new GridBagConstraints() );
frame.add(wrapper, BorderLayout.CENTER );
The above code will cause the tiles to be centered in the frame.
frame.add(wrapper, BorderLayout.LINE_END);
The above will cause the tiles to display on the right of the frame vertically centered.

Related

Java Swing DrawRect: creating new replaces old dimensions

I tried making two rectangles using the class below: DrawRect but when I create a new DrawRect object it replaces old one's width and height.
package MemDiagramVisualizer;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
public class DrawRect extends JPanel {
private static int RECT_X;
private static int RECT_Y;
private static int RECT_WIDTH;
private static int RECT_HEIGHT;
public DrawRect(int w, int h) {
RECT_X = 20;
RECT_Y = 20;
RECT_WIDTH = w;
RECT_HEIGHT = h;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(RECT_X, RECT_Y, RECT_WIDTH, RECT_HEIGHT);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(RECT_WIDTH + 2 * RECT_X, RECT_HEIGHT + 2 * RECT_Y);
}
}
JPanel visDisplay = new JPanel();
visDisplay.setLayout(new GridLayout(1,3));
DrawRect rec2 = new DrawRect(40,60);
visDisplay.add(rec2);
DrawRect rec = new DrawRect(100,600);
visDisplay.add(rec);
The code above when added to frame content pane creates two rectangles with 100,600 dimensions
You have a couple of issues in your program:
All these 4 variables:
private static int RECT_X;
private static int RECT_Y;
private static int RECT_WIDTH;
private static int RECT_HEIGHT;
They all are static, but you're trying to change them inside the program, this will apply for all the instances of your program and they all are gonna share the value. In this case I'd suggest you to remove that and you should be good. This is the reason why when you create another class with the same code, it worked.
RECT_X = 20; and RECT_Y = 20; inside the constructor, if these are constants, then initialize them at the top and don't set them in every instance of the class.
Not an error, but depending on your requirements, you might want to stop using multiple JPanels and instead use the Shape API as shown in this answer to create an Array of Shapes that you can draw in a single JPanel. Again, this all depends on your needs.
After removing the static keyword from those constants above, we have this:

I'm trying to draw square in java but it doesn't work

I'm trying to draw squares to make grid system to make snake game in java but when i run my code i can't see any thing.
I made node class that has the square info like position, and grid class that has the data of all the squares and GUI class and i tried to use drawRect method but i have no result.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.*;
import java.util.List;
public class SnakeGame {
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setSize(300,300);
frame.setLayout(null);
Grids grids = new Grids(300);
GUI gui = new GUI(grids.nodesList,grids.nodeSize);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(gui);
frame.setVisible(true);
}
public static class Node{
public float[] position = new float[2];
public Node(float[] Position){
//this is the x position
position[0] = Position[0];
//this is the y position
position[1] = Position[1];
}
}
public static class Grids{
public int nodesNum = 6;
public float screenSize;
public float nodeSize;
public float[] startNodePos = new float[2];
public List<Node> nodesList = new ArrayList<Node>();
public Grids(float ScreenSize){
screenSize = ScreenSize;
nodeSize = screenSize / nodesNum;
//set the start node position
//x
startNodePos[0] = nodeSize /2;
//y
startNodePos[1] = nodeSize /2;
//use for loop to create nodes
for (float X = startNodePos[0] ; X <= nodeSize * 5; X += nodeSize / 2) {
for (float Y = startNodePos[1]; Y <= nodeSize * 5; Y += nodeSize / 2) {
float[] Pos = {X, Y};
Node node = new Node(Pos);
nodesList.add(node);
}
}
}
}
public static class GUI extends JPanel{
public List<Node> nodes;
public float NodeSize;
public GUI(List<Node> Nodes,float nodeSize){
nodes = Nodes;
NodeSize = nodeSize;
}
#Override
public void paintComponents(Graphics g) {
super.paintComponents(g);
for(Node node : nodes){
g.setColor(Color.BLACK);
g.drawRect((int)node.position[0],(int)node.position[1],(int)NodeSize,(int)NodeSize);
}
}
}
}
There are two primary mistakes
First
frame.setLayout(null);
This means that you will become completely responsible for determine the location and size of all child components.
In this case, it's probably just easier to get rid of it and use the default BorderLayout
Second
You're overriding paintComponents, not paintComponent (not the s at the end)
public void paintComponents(Graphics g) {
//...
}
Since, there are no components to be painted, it's not getting called
Simple change it to paintComponent (and make sure you call it's super properly)
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Node node : nodes) {
g.setColor(Color.BLACK);
g.drawRect((int) node.position[0], (int) node.position[1], (int) NodeSize, (int) NodeSize);
}
}
Observations
While running through the code, I note some ... interesting things, which might cause you issues in the long run.
The size of the view area is not the same as the size of the window. A window also needs to display the window decorations (title bar, borders, etc), which are inset into the window. This reduces the available viewable area. It's better to have the content provide a sizing hint and pack the window around, which brings me to...
The model seems to be taking into consideration view properties. You should aim to have the model and view be as agnostic as possible, meaning that the model should not be dictating display state to the view. Instead, the size of the cell's, and by extensions, the size of the panel, should be determined by the view.

Resize JList file elements in JFileChooser

I have a JFileChooser. I am trying to add a zoom feature to the files JList.
I would like to change the scale factor of the file name and of the file icon, for each element of the list.
How could we achieve this ?
Should I make a custom renderer like here [JList custom renderer example] (http://www.codejava.net/java-se/swing/jlist-custom-renderer-example)
or change the list Model ?
Well, I found out some ugly lazy hacks to do it.
It might not be just what you want, but it's a good starting point (and fairly simple):
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.plaf.basic.BasicListUI;
public class TJFileChooserDemo {
//Obtains the (first) JList which is found inside the component/container:
public static JList getFirstJList(final Component component) {
if (component instanceof JList)
return (JList) component;
if (component instanceof Container)
for (int i=0; i<((Container)component).getComponentCount(); ++i) {
final JList list = getFirstJList(((Container)component).getComponent(i));
if (list != null)
return list;
}
return null;
//As you can see, it's a bit lazy hack, which has to run for every JFileChooser once at start-up.
}
private static final double SCALE_STEP_SIZE = 0.125; //Smaller values of this makes zooming slower. Greater values makes zooming faster.
private static double scaleFactor = 1;
public static class TJListCellRenderer extends DefaultListCellRenderer {
public TJListCellRenderer() {
//Ensure every pixel is painted starting from the top-left corner of the label:
super.setVerticalAlignment(JLabel.TOP);
super.setHorizontalAlignment(JLabel.LEFT);
//We need to do this, because the scaling in paintComponent() is also relative to the top-left corner.
}
#Override
public void paintComponent(final Graphics g) {
//setRenderingHints here? Probably for ANTIALIAS...
((Graphics2D)g).scale(scaleFactor, scaleFactor); //Let's scale everything that is painted afterwards:
super.paintComponent(g); //Let's paint the (scaled) JLabel!
}
#Override
public Dimension getPreferredSize() {
final Dimension superPrefDim = super.getPreferredSize(); //Handles automatically insets, icon size, text font, etc.
final double w = superPrefDim.width * scaleFactor, //And we just scale the preferred size.
h = superPrefDim.height * scaleFactor; //And we just scale the preferred size.
return new Dimension((int)w + 5, (int)h + 5); //Add 5 extra pixels to spare.
}
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
// System.out.println(value.getClass()); //Something ugly...
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
}
public static class TJListUI extends BasicListUI {
#Override
public void updateLayoutState() {
super.updateLayoutState(); //Just make the following method public.
/*Note: this is not really needed here:
The method could remain protected, but in the case you want this
code to be a bit more reusable, then you shall make it public.*/
}
}
public static void main(final String[] args) {
final JFileChooser jfc = new JFileChooser();
jfc.setDialogType(JFileChooser.OPEN_DIALOG);
final TJListUI ui = new TJListUI();
final JList list = getFirstJList(jfc);
list.setUI(ui);
list.setCellRenderer(new TJListCellRenderer());
final JButton buttonZoomIn = new JButton("Zoom in"),
buttonZoomOut = new JButton("Zoom out"),
buttonResetZoom = new JButton("Reset zoom");
buttonZoomIn.addActionListener(e -> {
scaleFactor = scaleFactor + SCALE_STEP_SIZE;
ui.updateLayoutState(); //Read the preferred sizes from the cell renderer.
list.revalidate(); //Update the JScrollPane.
list.repaint(); //Repaint the list.
});
buttonZoomOut.addActionListener(e -> {
scaleFactor = Math.max(scaleFactor - SCALE_STEP_SIZE, SCALE_STEP_SIZE); //Do not allow underflow.
ui.updateLayoutState(); //Read the preferred sizes from the cell renderer.
list.revalidate(); //Update the JScrollPane.
list.repaint(); //Repaint the list.
});
buttonResetZoom.addActionListener(e -> {
scaleFactor = 1;
ui.updateLayoutState(); //Read the preferred sizes from the cell renderer.
list.revalidate(); //Update the JScrollPane.
list.repaint(); //Repaint the list.
});
final JPanel buttons = new JPanel(); //FlowLayout.
buttons.add(buttonZoomIn);
buttons.add(buttonZoomOut);
buttons.add(buttonResetZoom);
final JPanel panel = new JPanel(new BorderLayout());
panel.add(buttons, BorderLayout.PAGE_START);
panel.add(jfc, BorderLayout.CENTER);
final JFrame frame = new JFrame("JFileChooser's JList cell sizes demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Alternatively you can check my answer here about individually resizable cells of a JList.
You can also probably add the JFileChooser's buttons for zooming in/out as an accessory. Read this simple example for how to do it.
Test this code, and I am waiting for comments...
In the end, I realized scaling the text wasn't needed.
To obtain the image files thumbnail, I used the code in making JFileChooser show image thumbnails - check BoffinbraiN answer.
Then for scaling :
1) add an ActionListener to the buttons of ThumbnailFileChooser.
public class ZoomListener implements ActionListener {
private boolean zoomIn = false;
private IconScaleManager iconScaleManager = null;
public ZoomListener(boolean zoom, IconScaleManager renderer) {
zoomIn = zoom;
iconScaleManager = renderer;
}
#Override
public void actionPerformed(ActionEvent e) {
iconScaleManager.scaleButton(zoomIn);
}
}
2) ActionListener::actionPerformed() calls a scale method of a ScaleManager.
#Override
public void actionPerformed(ActionEvent e) {
iconScaleManager.scaleButton(zoomIn);
}
3) The ScaleManager method changes and update the cells of the ThumbnailFileChooser's Jlist (the list is an attribute of the ScaleManager)
public class IconScaleManager {
static final int[] iconScales = new int[]{ 16, 32, 64, 128, 256, 512, 1024, 2048 };
private int scaleIndex = 4;
private JList fileList = null;
public IconScaleManager(JList list) {
fileList = list;
setFixedCellDimension();
}
public void scaleButton(boolean zoomIn) {
if (zoomIn && scaleIndex < iconScales.length - 1) {
scaleIndex++;
setFixedCellDimension();
} else if (!zoomIn && 0 < scaleIndex) {
scaleIndex--;
setFixedCellDimension();
}
}
private void setFixedCellDimension() {
fileList.setFixedCellWidth(iconScales[scaleIndex]);
fileList.setFixedCellHeight(iconScales[scaleIndex]);
}
}
Thank you #thanopi57 for your help. I didn't really use what you provided, but I appreciate your support.
Also, I will have to make sure that it works, because there might not be a JList for all JFileChooser

Making a Chess Board out of JButtons

I'm making a chessboard for a project and I have to use JButtons. I'm trying to just set the board up with a blank image I have for each tile, this is the code I have:
Driver
public class Driver
{
public static void main (String[] args)
{
new ChessBoard();
}
}
ChessSquare
import javax.swing.*;
import java.awt.*;
public class ChessSquare
{
public ImageIcon pieceImage;
/** The square's location */
private int xCoord;
private int yCoord;
/** Constructor for the squares */
public ChessSquare(ImageIcon thePieceImage, int theXCoord, int theYCoord)
{
pieceImage = thePieceImage;
xCoord = theXCoord;
yCoord = theYCoord;
}
public int getXCoord()
{
return xCoord;
}
public int getYCoord()
{
return yCoord;
}
}
ChessBoard
public class ChessBoard
{
public ChessBoard()
{
JFrame board = new JFrame();
board.setTitle("Chess Board!");
board.setSize(500,500);
board.setLayout(new GridLayout(8,8));
JPanel grid[][] = new JPanel[8][8];
ImageIcon empty = new ImageIcon("/pieces/EmptySquare.jpg");
for(int x = 0; x<8; x++)
{
for(int y = 0; y<8; y++)
{
ChessSquare s = new ChessSquare(empty, x, y);
JButton square = new JButton(s.pieceImage);
grid[x][y].add(square);
board.setContentPane(grid[x][y]);
}
}
board.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
board.setVisible(true);
}
}
My code compiles fine but when I run it I get this error:
Exception in thread "main" java.lang.NullPointerException at
ChessBoard.(ChessBoard.java:23) at Driver.main(Driver.java:8)
I don't know what to do to fix this error. Thanks for any help :)
One of the likely causes is ImageIcon empty = new ImageIcon("/pieces/EmptySquare.jpg");...
The path of the image suggest that you are using embedded resources, but ImageIcon(String) treats the value as if it were a file, you can't do this with embedded resources, they aren't files.
Instead, you need to use something more like ImageIcon empty = new ImageIcon(getClass().getResource("/pieces/EmptySquare.jpg"));.
Personally, I'd recommend that you should be using ImageIO.read(getClass().getResource("/pieces/EmptySquare.jpg")) as this will throw an exception if the resource can not be loaded from some reason, rather then failing silently.
grid[x][y].add(square); also won't work, as you've not assigned anything to grid[x][y]
grid[x][y] = new JPanel();
grid[x][y].add(square);
Might work better, but I don't know why you're doing this, when doing something like...
JButton grid[][] = new JButton[8][8];
//...
grid[x][y] = square;
Would seem to be more logical for what you are trying to achieve...
Updated...
Instead of board.setContentPane(grid[x][y]); you should be using board.add(grid[x][y]);, other wise you will replace the content pane with the button...since there can only be a single content pane, you'll only get one button...
In grid[x][y].add(square); you are actually calling JPanel.add(), because each element in the array is a JPanel.
So the error is because grid[x][y] is still null you will get a NullPointerException if you call a method on it, like add() in this case.
You want to assign the value since you are using an array
grid[x][y] = square;

Coin flip program

I tried making a program that flips a coin(shows image of heads first and later shows image of tails) and I encountered problems trying to have the image of the coin viewed when I ran the problem; only a blank screen would show. I don't know whether this is from an improper saving method of the jpg images or from an error in the code. I also came across an error before again coding the program where I had the heads image show and tails image not show.
CoinTest.java runs coin runner and Coin.java is the class for the program.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CoinTest extends JPanel
implements ActionListener
{
private Coin coin;
public CoinTest ()
{
Image heads = (new ImageIcon("quarter-coin-head.jpg")).getImage();
Image tails = (new ImageIcon("Indiana-quarter.jpg")).getImage();
coin = new Coin(heads, tails);
Timer clock = new Timer(2000, this);
clock.start();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
int x = getWidth() / 2;
int y = getHeight() / 2;
coin.draw(g, x, y);
}
public void actionPerformed(ActionEvent e)
{
coin.flip();
repaint();
}
public static void main(String[] args)
{
JFrame w = new JFrame("Flipping coin");
w.setSize(300, 300);
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CoinTest panel = new CoinTest();
panel.setBackground(Color.WHITE);
Container c = w.getContentPane();
c.add(panel);
w.setVisible(true);
}
}
Now the actual Coin class.
import java.awt.Image;
import java.awt.Graphics;
public class Coin
{
private Image heads;
private Image tails;
private int side = 1;
public Coin(Image h, Image t)
{
heads = h;
tails = t;
}
//flips the coin
public void flip()
{
if (side == 1)
side = 0;
else
side = 1;
}
//draws the appropriate side of the coin - centered in the JFrame
public void draw(Graphics g, int x, int y)
{
if (side == 1)
g.drawImage(heads, heads.getWidth(null)/3, heads.getHeight(null)/3, null);
else
g.drawImage(heads, tails.getWidth(null)/3, tails.getHeight(null)/3, null);
}
}
Firstly, ensure that both images are in the correct location to load.
Secondly, you have a typo here:
if (side == 1)
g.drawImage(heads, heads.getWidth(null)/3, heads.getHeight(null)/3, null);
else
g.drawImage(heads, tails.getWidth(null)/3, tails.getHeight(null)/3, null);
^^^^
should be tails...
The width and height of the applet are coded in the tag. The code that draws the applet uses the two methods to get these values at run time. So now, different tags can ask for the same applet to paint different sized rectangles. The source code does not need to be recompiled for different sizes.

Categories