JButtons incorrect size - java

I want to draw a 13x13 tiles board using JFrame.
Here's the code:
public static void drawBoard() {
final int TILE_SIZE = 60;
final int TILES = 13;
JFrame jFrame = new JFrame("Board");
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(TILE_SIZE * TILES + 17, TILE_SIZE * TILES);
jFrame.setResizable(false);
JButton button = null;
ImageIcon icon = null;
for (int y = 0; y < TILES; y++) {
for (int x = 0; x < TILES; x++) {
button = new JButton(alphabet[x] + "" + alphabet[y]);
button.setName(alphabet[x] + "" + alphabet[y]);
button.setBounds(TILE_SIZE * y, TILE_SIZE * x, TILE_SIZE, TILE_SIZE);
button.addActionListener(new Clicked());
button.setBackground(Color.WHITE);
jFrame.getContentPane().add(button);
}
}
jFrame.setVisible(true);
}//end drawBoard
Now, when I run the code, it shows me a grid of buttons, but the one at the bottom right corner is the same size of the frame.

but the one at the bottom right corner is the same size of the frame.
Swing was designed to be used with layout managers. The layout manager will determine the size/location of a component based on the rules of the layout manager.
In the case of a JFrame, the default layout manager is the BorderLayout. When you add a component to the BorderLayout without using a "constraint" the component is added to the CENTER. However only one component can be added to the CENTER. So only the last component added is give a size/location by the layout manager. In this case the rule is to make the button the size of the space available in the frame.
If you want to have a grid, then you should be using a GridLayout. Then the layout manager will make each button the same size.
Read the section from the Swing tutorial on Layout Managers for more information. There are working examples for both the BorderLayout and the GridLayout.

You should really look into a layout that handles the component size instead of setting it on the component itself (See: MIG Layout), but this should get you were you want to be.
public static void drawBoard(){
final int TILE_SIZE = 60;
final int TILES = 13;
JFrame jFrame = new JFrame("Board");
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(TILE_SIZE * TILES + 17, TILE_SIZE * TILES);
jFrame.getContentPane().setLayout(new GridLayout(TILES, TILES));
jFrame.setResizable(false);
JButton button = null;
Dimension dim = new Dimension(TILE_SIZE, TILE_SIZE);
for (int y = 0; y < TILES; y++) {
for (int x = 0; x < TILES; x++) {
button = new JButton(alphabet[x] + "" + alphabet[y]);
button.setSize(dim);
button.addActionListener(new Clicked());
button.setBackground(Color.WHITE);
jFrame.getContentPane().add(button);
}
}
jFrame.setVisible(true);
}

Related

How to combine images in a JFrame?

I have been working on a project that is displaying a grid 16 x 16 of images, based on user interaction this grid follows the user on a dynamically larger base (an example would be a base that is 50 x 50) than the 16 x 16 display.
However, I am using JLabel components to display these images, and every time the user interacts I have to move each of the 256 images and erase the ones that are no longer in the 16 x 16 display grid. This results in a lag that is close to a second per key press and is close to nonfunctional.
What I am looking to try to do is to chain these images together in the total width of the ground and simply move the focus to the portion that is within the 16 x 16 grid, making the process no longer have to use nested for loops for the display.
Is it possible that I could dynamically store and create these chained images for display using a label? If are there other ways to display .png files in Java that could be stored and used in a similar manner?
An example of my current methodology of having to draw every image upon every user interaction:
User user = game.user;
int floorWidth = game.floorWidth;
int floorHeight = game.floorHeight;
int pX = user.getTile().getX();
int pY = user.getTile().getY();
int minX = Math.max(pX - GameConstants.USER_DRAW_DISTANCE, 0);
int maxX = Math.min(floorWidth, pX + GameConstants.USER_DRAW_DISTANCE);
int minY = Math.max(pY - GameConstants.USER_DRAW_DISTANCE, 0);
int maxY = Math.min(floorHeight, pY + GameConstants.USER_DRAW_DISTANCE);
for (int i = minY; i < maxY; i++)
{
for (int x = minX; x < maxX; x++)
{
Tile tile = floor.getTile(x, i);
if (tile.getHasSeen())
{
JLabel cLabel = tile.imageLabel;
cLabel.setLocation(340 + x * 32, 140 + i * 32);
cLabel.setSize(64, 64);
cLabel.setVisible(true);
panel.add(cLabel, 1);
}
}
}
In principle your idea should work. So you're probably doing something else wrong.
I've made an example, where it displays a 16x16 square of JLabels out of 256x256 JLabels. When you move the mouse over the panel, it changes the layout to show a new set of 16x16 JLabels. The change is pretty snappy, definitely not a 1 second delay.
import javax.swing.*;
import java.awt.EventQueue;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.event.*;
import java.util.*;
public class GridViewer{
int x0, y0;
int N = 256;
int display = 16;
int length = 32;
List<JLabel> showing = new ArrayList<>();
List<JLabel> available = new ArrayList<>();
JPanel panel = new JPanel(){
Dimension sz = new Dimension(length*display, length*display);
#Override
public Dimension getPreferredSize(){
return sz;
}
};
public void showGui(){
JFrame frame = new JFrame();
panel.setLayout(null);
panel.addMouseMotionListener( new MouseAdapter(){
Random r = new Random();
#Override
public void mouseMoved(MouseEvent evt){
int x = evt.getX();
int y = evt.getY();
//map to position on the image to the position on the grid.
x0 = x/2;
x0 = Math.min(x0, N-display);
y0 = y/2;
y0 = Math.min(y0, N-display);
updateLayout();
}
});
for(int i = 0; i<N*N; i++){
available.add(createItem(i));
}
updateLayout();
frame.setContentPane(panel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
/**
* Creates a solid color jlabel, could be used to load an image
* as an icon.
**/
JLabel createItem(int i){
JLabel l = new JLabel("");
int r = (i/256);
int g = (0)&255;
int b = (i%256);
int c = (r << 16 ) + ( g << 8 ) + b;
l.setBackground(new Color(c));
l.setOpaque(true);
l.setSize(length, length);
return l;
}
public void updateLayout(){
for(JLabel l: showing){
panel.remove(l);
}
for(int i = 0; i<display; i++){
for(int j = 0; j<display; j++){
JLabel l = available.get((i + x0) + (j+y0)*N);
panel.add(l);
l.setLocation( i*length, j*length);
showing.add(l);
}
}
}
public static void main(String[] args){
EventQueue.invokeLater( () -> new GridViewer().showGui() );
}
}
Some variations.
Use a GridLayout
Using a layout manager has a lot of advantages. Especially when it comes to using different displays, fonts and platforms? When adding and removing elements, it could make partially showing elements tough.
Use a large JPanel with a ScrollPane
We could create a single JPanel and add all 256x256 components to it, then use a scroll pane to set the view. This would have an advantage of completely separating the layout and the view. Somebody wants a larger window, you don't have to change the layout, the view gets bigger and you just see more of the layout. For 256x256 components, it should perform well but if you have too many components you might want to reconsider it.
Use a JPanel and override paintComponent
This would involve loading your 'png' files as awt Images (probably BufferedImages) and drawing them with the graphics object. You would need to handle all of the layout and rendering. It gives you quite a bit of power over how you want to render your components.

Scrolling through a JPanel within a JScrollingPanel

I'm trying to make a level editor for my platformer game, I want my levels to be 100 by 100 squares.
So far the editor works, but I can't scroll through the JPanel. I've been playing around and I've made a small test class to fiddle with which I'll post. If you run it, all it does it show the grid. However if I swap out two variables (I'll comment where) it can show an image and scroll according to the size of that image.
I want that scrolling ability only for the JPanel, so that I can scroll through my 100 x 100 square level.
import java.awt.BorderLayout;
public class ScrollPaneJ extends JFrame {
// setting the panels
private JPanel contentPane;
private JScrollPane scrollPane;
// dimensions/ variables of the grid
int size = 16;
int startX = 112;
int startY = 48;
int width = 30;
int height = 30;
// this is the grid
String[][] grid = new String[width][height];
// this is from the full editor class
String currentImage = new String("platform");
ImageIcon currentBackIcon = new ImageIcon("Resources/backdirttile.jpg");
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
// adding the scrollpane
ScrollPaneJ frame = new ScrollPaneJ();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public ScrollPaneJ() {
setTitle("Scrolling Pane Application");
setSize(new Dimension(300, 200));
setBackground(Color.gray);
setDefaultCloseOperation(EXIT_ON_CLOSE);
// defining the top and bottom panels, bottom is what I think I'm
// drawing on, top is where the scrollpanel goes, I copied this code
// from the internet and I'm not too sure how it works
JPanel topPanel = new JPanel();
JPanel bottomPanel = new JPanel(new GridLayout());
bottomPanel.setLayout(new BorderLayout());
getContentPane().add(bottomPanel);
topPanel.setLayout(new BorderLayout());
getContentPane().add(topPanel);
// this is the label I was talking about
Icon image = new ImageIcon("src/MenuDesign.jpg");
JLabel label = new JLabel(image);
// Create a tabbed pane
// if you set it to say label instead of bottomPanel, you can scroll
// through the size of the label
scrollPane = new JScrollPane(bottomPanel);
scrollPane.setBounds(40, 40, 100, 100);
// set it label here as well.
scrollPane.getViewport().add(bottomPanel);
// I was hoping this would force the scrollbar in but it does nothing
scrollPane
.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane
.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setBounds(50, 30, 300, 50);
JPanel contentPane = new JPanel(null);
contentPane.setPreferredSize(new Dimension(500, 400));
contentPane.add(scrollPane);
topPanel.add(scrollPane, BorderLayout.CENTER);
init();
}
public void init() {
// this sets the grid to empty
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
grid[x][y] = "";
}
}
}
#Override
public void paint(Graphics g) {
// this paints the grid
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
g2d.drawRect(x * size + startX, y * size + startY, size, size);
if (grid[x][y].equals("")) {
g2d.drawImage(currentBackIcon.getImage(),
x * size + startX, y * size + startY, null);
}
g2d.setColor(Color.black);
g2d.drawRect((x * size) + 1 + startX, (y * size) + 1 + startY,
size, size);
}
}
}
public void drawTile() {
// this isn't enabled which is why you can't paint the grid, however it
// would change the tile of the square you're mouse is on, to the
// current tile, it works and isn't really important for what i need
// help with
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
int mouseX = (int) b.getX();
int mouseY = (int) b.getY();
int gMX = ((mouseX - 48) / 16) - 4;
int gMY = ((mouseY - 48) / 16) - 3;
grid[gMX][gMY] = currentImage;
repaint();
}
}
scrollPane.getViewport().add(bottomPanel); should be more like scrollPane.getViewportView(bottomPanel);
You shouldn't be painting directly to the frame, as child components can be painted without the notification to the parents, meaning that what ever you've painted could be partially wiped out. Instead, this kind of painting should be done within a custom component which acts as the JScrollPane's, JViewport's view.
A JScrollPane needs two things, first, the size that the component would like to be (the preferredSize) and the size of the viewport view. If the component doesn't implement the Scrollable interface, then the component's preferredSize is used to determine that as well. This is why a JLabel will work.
A JScrollPane has a JViewport as it's primary child component. The JViewport should only have a single component, typically assigned either via JScrollPane#setViewportView or JViewport#setView methods
See How to Use Scroll Panes for more details
Create a custom component that extends JPanel and override it's getPreferredSize method to return the size of the component you want. Override it's paintComponent method and perform you custom painting their.
Overlaying custom painting ontop of other components is more difficult
You can also add JScrollPane in your panel like this
JPanel p = new JPanel();
add(new JScrollPane(p));

calling jpanel added in a jframe

I have a JPanel 7width 9 height board. I can also place my pieces on top of the board. My problem now is how I will call the pieces:
pseudo code:
if(whero1 is on row 0 column 5 then....
code is below:
public class Board extends JPanel{
private static final String imageFolderPath = "src/resources/images/";
Dimension dimension = new Dimension(500, 500);
JPanel board;
JLabel piece;
MovePiece mp = new MovePiece(this);
public Board(){
//set size of panel;
this.setPreferredSize(dimension);
this.addMouseListener(mp);
this.addMouseMotionListener(mp);
//create the Board
board = new JPanel();
board.setLayout(new GridLayout(9,7));
board.setPreferredSize(dimension);
board.setBounds(0, 0, dimension.width, dimension.height);
this.add(board);
for (int i = 0; i < 63; i++) {
JPanel square = new JPanel(new BorderLayout());
square.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
board.add(square);
square.setBackground(new Color(185, 156, 107));
}
JLabel whero1 = new JLabel( new ImageIcon(imageFolderPath+"/pieces/bhero.png") );
JPanel panel = (JPanel)board.getComponent(60);
panel.add(whero1);
//I'm trying this, but i.m going nowhere
int x =whero1.getParent().getX();
int y = whero1.getParent().getY();
System.out.println(x);
System.out.println(y);
/*if(x==0&&y==0){
whero1.setIcon(new ImageIcon(imageFolderPath+"/pieces/bdg.png"));
}*/
}
}
The easiest solution would be to maintain some kind of virtual model of the board. In this way you could simply update the state of the game and request that the UI update itself to reflect the state of the model.
Much simpler then trying to interrogate n-depth contains and convert to/from coordinate systems
nb: This...
int x =whero1.getParent().getX();
int y = whero1.getParent().getY();
Is going to return the pixel x/y position of the whereo1s parent's in relation to it's parent container, not convinced that this would really help at all

Java - Filling an array of JLabels with ImageIcons via loop(NullPointerException)

the basic aim is to have a JPanel filled with 9 white squares in a 3x3 pattern; The squares are 150x150 blank white .jpg files. It must be this way since later on, the program will have to change the blank squares to one of a selection of simple images, and must be able to change any square at any time.
The problem, simply, is I'm getting a NullPointerException. I have to assume it's something to do with initialising the array as null but NetBeans(yes, NetBeans...) seems to get angry with me if I don't do that. Same if I try to declare the size of the array. (That would be... "ArrayType[arraysize] arrayName;", yes?"
Egh, I'm just guessing wildly.
Edit - NullPointerException fixed, but now the blank(white) images are simply not appearing in the frame. Code below edited to reflect its new state, more potentially relevant lines added.
Here be all relevant code:
JFrame controller = new JFrame("SmartHome Interface");
controller.setVisible(true);
controller.setSize(480,500);
controller.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//[...]
JPanel labelPanel = new JPanel();
//[...]
labelPanel.setBackground(Color.GREEN);
//[...]
ImageIcon blank = new ImageIcon("../Images/blank.jpg");
//[...]
controller.add(labelPanel);
//[...]
JLabel[] labels = new JLabel[9];
for (int i = 0; i <= 8; i++)
{
int xLowBound;
int xUpBound;
int yLowBound;
int yUpBound;
//Maths for positioning the labels correctly. Should be 150px in size with 10px gaps each.
xLowBound = (i % 3) * 160;
xUpBound = xLowBound + 150;
yLowBound = (i / 3) * 160;
yUpBound = yLowBound + 150;
labels[i] = new JLabel();
labels[i].setIcon(blank);
labels[i].setBounds(xLowBound, yLowBound, xUpBound, yUpBound);
labelPanel.add(labels[i]);
}
Also.....is the filepath for the ImageIcon correct?
The code itself being located in "src/smarthome" and the images in "src/Images"
And apologies if I broke any forum conventions/codes of conduct/etc. Newby here, tried to be careful not to but I may have forgotten something.
Your problem reduces to this:
JLabel[] labels = null;
for (int i = 0; i <= 8; i++) {
labels[i].setIcon(blank);
}
This code fragment will fail because labels == null. Therefore labels[i] == null.
Use this instead:
JLabel[] labels = new JLabel[9];
for (int i = 0; i <= 8; i++) {
labels[i] = new JLabel();
labels[i].setIcon(blank);
}
Your filepath for imageIcons is incorrect. You should use:
ImageIcon img = new ImageIcon(getClass().getResource("../Images/blank.jpg"));
if your code is in static method use this:
ImageIcon img = new ImageIcon(YourClass.class.getResource("../Images/blank.jpg"));
There is a good answer about loading image icons(thanks to nIcE cOw).
You should call setVisible() and setSize() after adding all components to the frame.
Add components to frame's content pane(frame.getContentPane()).
You always should place your GUI code in separate thread.
So, your code will be:
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
JFrame controller = new JFrame("SmartHome Interface");
controller.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel labelPanel = new JPanel();
labelPanel.setBackground(Color.GREEN);
// !!!
ImageIcon blank = new ImageIcon(YourClass.class
.getResource("../Images/blank.jpg"));
// !!!
controller.getContentPane().add(labelPanel);
JLabel[] labels = new JLabel[9];
for (int i = 0; i <= 8; i++)
{
int xLowBound;
int xUpBound;
int yLowBound;
int yUpBound;
xLowBound = (i % 3) * 160;
xUpBound = xLowBound + 150;
yLowBound = (i / 3) * 160;
yUpBound = yLowBound + 150;
labels[i] = new JLabel();
labels[i].setIcon(blank);
labels[i].setBounds(xLowBound, yLowBound, xUpBound,
yUpBound);
labelPanel.add(labels[i]);
}
// !!!
controller.setVisible(true);
controller.setSize(480, 500);
}
});

Java - Swing GUI renders incorrectly in Windows 7

I'm building a Tic Tac Toe game in Java with a Swing GUI, and it renders correctly in Ubuntu 10.4 and Windows XP. This is how it looks like in Ubuntu:
When I copied the bin-folder with all the class files and tried to run the program in Windows 7 it looked like this instead:
I just can't understand what's wrong. As I said, it works perfectly in Ubuntu 10.4 and Windows XP.
I would be very happy if someone could help me out! I'll post the code related to the GUI, just in case it is needed to solve the problem.
Here is the code I use to initialize the GUI:
//Initializing GUI.
frame = new JFrame(); //Creating the window.
frame.setTitle("Tic Tac Toe"); //Setting the title of the window.
frame.addMouseListener(this);
frame.getContentPane().add(BorderLayout.CENTER, grid.getPanel()); //Adding the grid panel.
info = new JLabel(" Initializing game..."); //Creating info text.
frame.getContentPane().add(BorderLayout.SOUTH, info); //Adding info text.
//Setting GUI properties.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
The panel with the grid itself is created in my GameGrid class, which have a method "JPanel getPanel()". Here is the initialization of that panel (the code belongs in the constructor of GameGrid):
GridBox temp;
layout = new GridLayout(getHeight(), getWidth());
panel = new JPanel(layout);
panel.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Click in a box to place a marker:"),
BorderFactory.createEmptyBorder(5,5,5,5)));
//Creating a GridBox for each cell, and adding them to the panel in the right order..
for(int i = 0; i < getHeight(); i++) {
for(int j = 0; j < getWidth(); j++) {
temp = new GridBox(j, i);
temp.addMouseListener(listener);
panel.add(temp);
}
}
GridBox is a subclass of JPanel, which I modified to automatically show the contents of the grid at the coordinates specified.
class GridBox extends JPanel {
private static final long serialVersionUID = 1L;
int fontsize, x, y, value, signHeight, signWidth;
char print;
FontMetrics fm;
LineMetrics lm;
public GridBox(int a, int b) {
x = a; //TODO - input control
y = b;
}
public Move getMove() {
Move m = new Move(x, y);
return m;
}
public void paintComponent(Graphics g) {
Border blackline = BorderFactory.createLineBorder(Color.black);
setBorder(blackline);
Dimension size = getSize();
Rectangle2D rect;
fontsize = (int)(size.getHeight()*0.75);
value = getGridValue(x, y);
if(value == EMPTY)
print = ' ';
else if(value == 0)
print = 'X';
else if(value == 1)
print = 'O';
else
print = (char)value;
Font font = new Font("Times New Roman", Font.PLAIN, fontsize);
g.setFont(font);
fm = g.getFontMetrics();
rect = fm.getStringBounds(Character.toString(print), g);
signHeight = (int)rect.getHeight();
signWidth = (int)rect.getWidth();
g.setColor(Color.black);
g.drawString(Character.toString(print), (size.width/2)-(signWidth/2), (size.height/2)-(signHeight/2)+fm.getAscent());
}
}
Thanks in advance!
There's an obvious problem in the you change the border whilst repainting the component. That's going to cause all sorts of problems.
Also, I don't see where you paint the background of the panel. You should have
super.paintComponent(g);
at the top of the method.

Categories