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);
}
});
Related
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.
I am making a storybook and, because I'm not very creative with names, I gave images page numbers like so.
ImageIcon pg1icon = new ImageIcon("images/1.png");
ImageIcon pg2icon = new ImageIcon("images/2.png");
ImageIcon pg3icon = new ImageIcon("images/3.png");
JLabel pg1Label = new JLabel(pg1icon);
JLabel pg2Label = new JLabel(pg2icon);
JLabel pg3Label = new JLabel(pg3icon);
Because I have 30 pages, this process is getting tedious. Is there a way to scale the page num similar to i++?
Using streams we can handle this requirement concisely as:
List<JLabel> labels = IntStream.rangeClosed(1, 30)
.mapToObj(i -> new JLabel(new ImageIcon("images/" + i + ".png")))
.collect(Collectors.toList());
Assuming you only need the ImageIcon for the JLabel, you could use one array to store your thirty JLabel(s). Like,
int pageCount = 30;
JLabel[] labels = new JLabel[pageCount];
for (int i = 0; i < pageCount; i++) { // <-- i++ as requested
ImageIcon icon = new ImageIcon(String.format("images/%d.png", 1+i));
labels[i] = new JLabel(icon);
}
And then use labels[0] - labels[29] instead of pg1Label - pg30Label.
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
I'm in the process of creating a frogger type game and have gotten pretty far in getting the program to do what I want it to do. However, I'm starting to think that to finish the game I will have to use way to much code and there must be a simpler way achieve the same results. I'm not looking for an answer, just need some more information.
Question 1: What can I use for the images that represent the moving Icons or cars? I'm currently using JButtons. The problem is that is difficult to get the buttons to move uniformly and I want to use 24 different moving Icons and from what I've learned so far I will have to add a new JButton for each icon.
Question 2: The way that I've gotten the Jbutton icons to move is to use a timer delay and then a counter to increment the x values. This works for the most part, but is there a better, perhaps simpler, way to move icons across the screen?
Any tips, tutorials etc are greatly appreciated.
Here is one of the classes that I've created to get movement of the icons:
public class EnemyJPanel extends JButton {
JButton enem = new JButton();
JButton enem12 = new JButton();
JButton enem13 = new JButton();
JButton enem1 = new JButton("1");
JButton enem2 = new JButton("2");
JButton enem3 = new JButton("3");
JButton enem4 = new JButton("4");
JButton score = new JButton("Score");
JButton enem5 = new JButton("5");
JButton enem6 = new JButton("6");
JButton enem7 = new JButton("7");
JButton enem8 = new JButton("8");
JButton yard = new JButton("50 Yard Line");
int i = 16;
int u = 576;
int d = 16;
int n = 576;
int k = 16;
int l = 16;
int dummyval = 16;
public EnemyJPanel(){
super();
setLayout(null);
enem1.setBounds(16,300,40,55);
enem2.setBounds(16,245,40,55);
enem3.setBounds(16,190,40,55);
enem4.setBounds(16,135,40,55);
score.setBounds(16,80,601,55);
yard.setBounds(16,355,601,55);
enem5.setBounds(16,410,40,55);
enem6.setBounds(16,465,40,55);
enem7.setBounds(16,520,40,55);
enem8.setBounds(16,575,40,55);
enem12.setBounds(16,300,40,55);
enem13.setBounds(16,300,40,55);
add(enem1);
add(enem2);
add(enem3);
add(enem4);
add(score);
}
public void addEnemy(){
enem1.setBounds(16,300,40,55);
enem2.setBounds(16,245,40,55);
enem3.setBounds(16,190,40,55);
enem4.setBounds(16,135,40,55);
score.setBounds(16,80,601,55);
add(enem1);
add(enem2);
add(enem3);
add(enem4);
add(score);
}
public void enemyMovement(){
i++;u--;d++;n--; // increments lateral movement from a timer in
dummyval++; // the dummy value is needed to keep the icons looping
dummyval = dummyval + 2;
enem1.setBounds(i,300,40,55);
i = i + 2;
if (dummyval > 176){
k++; k = k + 2;
enem12.setBounds(k,300,40,55);
}
if (k > 176){
l++;
l = l + 2;
enem13.setBounds(l,300,40,55);
}
enem2.setBounds(u,245,40,55);
enem3.setBounds(d,190,40,55);
enem4.setBounds(n,135,40,55);
enem5.setBounds(i,410,40,55);
enem6.setBounds(u,465,40,55);
enem7.setBounds(d,520,40,55);
enem8.setBounds(n,575,40,55);
if(i > 576){ // resets button
i = 16;
}
if(k > 576){
k = 16;
}
if(u < 16){
u = 576;
}
u = u - 2; // increase lateral speed
if(d == 576) {
d = 16;
}
if(n < 16){
n = 576;
}
n = n - 5; //increases lateral speed
}
}
The problem is created because you try to manage all the "stuff" separately. It looks like you may be missing some basic information on classes
First, I would create a custom class, something like
class ButtonObject extends JButton
{
public ButtonObject(String text, int x, int y, int width, int height)
{
super(text);
this.setBounds(x, y, width, height);
}
}
You also may want to take a look at arrays and create an array of your new ButtonObject.
The for loop will help you get through all the objects in your array.
ButtonObject[] enemies = new ButtonObject[10];
for (int i = 0; i < 10; i++)
{
String text = String.valueOf(i);
int y = 300 - (i * 55);
enemies[i] = new ButtonObject(text, 16, y, 40, 55);
}
There is probably a better way to do it than buttons but you may want to stick with them for now for simplicity.
I am creating a matching game using Netbeans, but not the GUI editor (it sucks). So, basically, I created a new class, called Card, that extends the JButton class. Upon construction, the button's size is set to 100px by 100px and an icon is set. When I add the button to a JPanel in a GridBagLayout, it is not the intended size.
Here is some of my code:
JFRAME CLASS:
package matchinggame;
... imports ...
public class MatchingGameWindow extends JFrame {
Card[] cards = new Card[16]; //16 game cards
public MatchingGameWindow() {
...
//Create new game panel (for the cards)
JPanel gamePanel = new JPanel(new GridBagLayout());
//gamePanel.setSize(500,500); removed as it is not needed.
...
this.add(gamePanel, BorderLayout.CENTER);
//Create 16 card objects
cards = createCards();
//Create new grid bag constraints
GridBagConstraints gbc = new GridBagConstraints();
//Add the cards to the game panel
int i=0;
for (int y = 0; y < 4; y++) {
gbc.gridy = y;
for (int x = 0; x < 4; x++) {
gbc.gridx = x;
gamePanel.add(cards[i], gbc);
i++;
}
}
}
public final Card[] createCards() {
Card[] newCards = new Card[16];
//New choices array
ArrayList<Integer> choices = new ArrayList();
int[] choiceValues = {0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7};
//Add the initial choice values to the arraylist
for (int i=0; i < choiceValues.length; i++) {
choices.add(choiceValues[i]);
}
//Create 16 cards
for (int i=0; i < 16; i++) {
//Initial value of -1 for error checking
int iconIndex = -1;
//Loop until card is created
while (iconIndex == -1) {
//Get a random number from 0 - 7
Random r = new Random();
int rInt = r.nextInt(8);
//If the random number is one of the choices
if (choices.contains(rInt)) {
//the icon # will be the random number
iconIndex = rInt;
//Get rid of that choice (it is used up)
choices.remove(new Integer(rInt));
//Create a new Card in the Card[]
newCards[i] = new Card(i,iconIndex);
//If all the choices are gone
} else if (choices.isEmpty()){
iconIndex = -1; //done creating this card (breaks out of loop)
}
}
}
//Return the created cards
return newCards;
}
}
CARD CLASS:
package matchinggame;
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class Card extends JButton {
final static ImageIcon defaultIcon = new ImageIcon("cardback.jpg");
ImageIcon secretIcon;
boolean isRevealed = false;
...
public Card(final int cardIndex, int secretIconIndex) {
//Size is 100px by 100px
setSize(100, 100);
//Default icon is card back image
setIcon(defaultIcon);
//Get the secret icon behind the back of the card
secretIcon = icons[secretIconIndex];
}
}
And using this code I get a result of this:
Any ideas as to what I am doing wrong here?
EDIT:
I overrided the getPreferredSize method like Hovercraft Full Of Eels said, and it worked!
I added this code in the Card class:
#Override
public Dimension getPreferredSize() {
return new Dimension(100,100);
}
and got my desired result:
Now I must be doing something wrong with the icons, as they are not showing up as they should.
You should not use setSize(...) in the class's constructor but rather override the class's getPreferredSize() method to return a Dimension(100, 100). And in fact you should have setSize(...) no-where in your program. Instead use decent layout managers, call pack() on the JFrame after adding all components and before setting it visible, and let the layout managers size the GUI appropriately.