Java Swing: Swing Worker, invokeAndWait and Overlapping JLabels - java

Hey guys I've come with 2 problems, both being Java Swing related. I am developing a card game in Java. I use arreays lists of type to hold the values of each card and I have a main Play() method that calls updates to the GUI using invokeLater and a Singleton approach to my GUI class.
The first question is fairly simple. I create the cards on the GUI using JLabels like so; attaching relevent listeners and adding them to the appropriate panel, in this case, the 'Human Hand Panel':
for (int i = 0; i < (HumanHand.size()); i++)
{
Card card = HumanHand.get(i);
BufferedImage cardImage = null;
try {
cardImage = ImageIO.read(new File("card/" + card + ".jpg"));
} catch (IOException e) {
e.printStackTrace();
}
JLabel picLabel = new JLabel(new ImageIcon( cardImage ));
picLabel.addMouseListener((MouseListener) me);
HumanHandDisplay.add(picLabel);
}
//HumanHandDisplay.validate();
HumanHandDisplay.updateUI();
The problem I'm having is that when the human hand is more than 7 cards or so, it creates a larger panel area and starts a new row of cards beneath. What I would like to do is, when the hand reaches a certain size, the cards start to overlap eachother (like you would hold cards in your hand). I've messed about with .validate() but gotten nowhere. Any help you can give here would be most welcome.
My second question is about using a Swing Worker to return the Human player's card selection. I have read a little bit about Swing workers but I'm unsure as to the best way to implement one in my own game. At present, using the console I have a scanner than takes the input of an int as the choice (the place of the specific card in the ArrayList). I would like this int to be selected by clicking on the card in the players hand. At the moment I use:
String name = "" + i;
picLabel.setName(name);
to set the names of the Card JLabels to the int in the for loop creating them (as shown^^), and I use:
public void mouseClicked(MouseEvent e) {
String name = ((JLabel)e.getSource()).getName();
System.out.println("Working " + name);
selection = Integer.parseInt(name);
}
To return that int when one of the cards is clicked. Here is what I've been using to call the GUI methods from Play() aswell:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
GUI.getInstance().UpdateHand();
}
});
My question is, how can I get the Play method to call a method in the GUI class, which sets up the hand with the appropriate listeners (lets call it GUIPlayerSelection() for now) then wait for the player to click a card, which then returns the int to my Play() method in my main class that represents the card selected. I'm unsure as how to use invoke and wait, as in, if I use invoke and wait, will it just wait for the cards to be set up, or will it wait for the mouseClicked() method to finish aswell? Or will I have to do something else to make sure it waits for the mouse to be clicked after the hand set-up? And how then will I return that int? I've been told to use a swing worker but if someone could explain how I can implement that in this case that would be awesome.
Thank-you in advance.

For your first question, are you having an issue getting the frame to repaint or with the layout of the cards?
If the latter, then you may want to have a look at Java Layout Managers, it describes how the content pane inside your JFrame organizes the components added to it.
If none of those will work for you (and I don't think they will with that you describe), you can use the setBounds method to manually align your JLabels.

Related

Replacement of a JPanel element without adding to the end of the panel?

I posted a question earlier about this but the solution never worked and now it's under different circumstances. I'm making a "penny pitch" program, that, when the "confirm" button is pressed, a randomized number will dictate which spot on the board(the board is fill with image icons) the "penny" will fall, and in the process it removes the image icon that use to occupant the chosen space.
I set up a GridBagLayout to constrain each icon down, and my button has no problem removing the chosen spot, but it can not find a way for it to add a new icon in it's place. It just gets adds onto the end of the JPanel.
Heres my coding for the button:
private class AddListener implements ActionListener {
public void actionPerformed(ActionEvent a){
if (a.getSource()== confirm) {
if (numberToss >0){
thrown = pitch.nextInt(25) + 1;
System.out.println(thrown);
//kol is an array to check for repeated numbers in randomization
if (kol.contains(thrown)==false){
input.remove(spot.get(thrown));
//spot is a map to set icons down with a association with number
spot.put(thrown, bSet);
input.add((spot.put(thrown, bSet)));
repaint();
kol.add(thrown);
}
else {
JOptionPane.showMessageDialog(null, "Your toss landed onto an occupied spot; you receive no points");
}
numberToss--;
}
else{
JOptionPane.showMessageDialog(null, "Out of tosses.");
}
}
}
Anyone happen to know how to replace the new icon (bSet) with the former? Thanks in advance!

Swing based Java game

this is a problem that I've been really struggling with
I need to create a game for some University coursework. The game is basically a 4x4 grid. When i click a button, the player should move randomly within 1 square of it's location.
There's enemy spaceships that are generated randomly over the grid, and if the player lands on the spaceship, it destroys it. if there's two enemy ships on the same sqaure and the player lands on that square, the player is destroyed.
I've created a GIU using gridlayout and the button that I need to move the player, but I'm having problems trying to figure out how to actually use the grid. I've created an element class to create objects for the grid (ie, a list of enemy ships), but I don't know how to use this class with my gridlayout. I know I also have to redraw the grid after each move to reflect the changes, but I don't know if I would need to create a custom redraw method for this.
Any push in the correct direction so I can get my program actually doing something would be really helpful.
Some time ago I created a card game that also operated based on a GridLayout. Below is some of the code that was used to drive major events:
CardPanel extends JPanel {
public CardPanel() {
setLayout(gridLayout);
placeCards(N);
chatBoxText = new JTextArea(5, 30);
}
public void placeCards(int numCards) {
for (int i = 0; i < numCards; i++) {
Card card = deck.distributeCard();
String cardImageName = card.getImageName();
JLabel jLabel = makeImage(cardImageName);
cardSet.add(new GraphicCard(card, jLabel));
add(jLabel);
jLabel.addMouseListener(this);
}
if (GameLogic.noSetsOnBoard(cardSet)) {
gridLayout.setRows(gridLayout.getRows() + 1);
placeCards(3);
}
}
public void updateCard(Integer location) {
Card card = deck.distributeCard();
String cardImageName = card.getImageName();
JLabel jLabel = makeImage(cardImageName);
cardSet.set(location, new GraphicCard(card, jLabel));
jLabel.addMouseListener(this);
}
}
The CardPanel here is an object that represents the GridLayout (you see the constructor) as well as some of those helper methods.
Further on, on particular actions, I have the following code:
cardPanel.removeAll();
// Some other logic here
for (GraphicCard card: cardSet) {
cardPanel.add(card.getJLabel());
}
cardPanel.revalidate();
cardPanel.repaint();
In short, yes, it is custom, but it is not difficult. There are better ways to do it than the above such that you do not need to empty the entire panel in order to refresh it, the logic I have omitted above merited this particular solution.
The approach I used kept track of a CardPanel object which was an abstraction that contained not only the information that each box represented (in my case a card with some values), but also the graphic element to be rendered.
This was some time ago, so I don't recall the exact details but hopefully this is helpful.

JComboBox is refering to old Frame while removeAllItems()

I tried to figure this out myself but I can't. I'm stuck at a strange problem.
I have a Java Program with multiple classes and forms (I use Intellij and the build in GUI-Creator). When I switch from one Screen to another I just call frame.setVisible(false); at the leafing window and frame.setVisible(true); at the window I want to show next.
On a Button Click I make this:
In Class 1:
if (e.getSource() == umschaltenButton) {
this.mainW.goToMainWindow();
logger.log(Level.INFO, "Switched Back to MainMenu");
frame.setVisible(false);
}
And here is the weird part.
In Class 2:
public void goToMainWindow() {
frame = tvElectronics.drawMainWindow(); // I get a new Frame with new Images and so on
frame.addMouseListener(al);
frame.add(BotomPanel); // in here is the JComboBox
frame.setSize(LENGTH, HEIGHT);
comboBox1.removeAllItems(); // Here it tryes to refere to the old frame before i made frame = tvElectronics.drawMainWindow();
Vector<String[]> content = tvElectronics.getContent();
for (int i = 0; i < tvElectronics.getAnz(); ++i) {
comboBox1.addItem((i + 1) + ". " + content.get(i)[3]);
}
comboBox1.setSelectedIndex(chanel);
frame.setVisible(true);
}
And so it tries to update the old frame from class2 which no longer exists because of the new one I just created. And so I have 2 frames open: one as I want it and one strange old frame form class2.
My problem is that I want bind my JComboBox to a new Frame and update it but it is still connected to the old one and that causes weird problems like jumping back in the function. I mean it is at the last line of goToMainWindow() and then it starts again at the first line.
First off you should avoid swapping JFrames as your program does since this is a very annoying GUI design. Please read The Use of Multiple JFrames, Good/Bad Practice? for more on this.
Next, it's impossible for us to tell what GUI view your JComboBox is associated with.
But having said that, it really shouldn't matter. Instead of doing what you're doing, I would give the display class that holds a JCombBox a public method that you call on the containing display class that clears the contained JComboBox's model or that places items in the model. This way, there will be no ambiguity as to which JComboBox you're referring to, and this way you avoid directly exposing a view's inner components.
As an aside, I try to gear my display or view classes towards creating JPanels, not JFrames as this will give my code much greater flexibility.
For example
// my display class
class Display1 {
private DefaultComboBoxModel<String> myModel = new DefaultComboBoxModel<>();
private JComboBox<String> myCombo = new JComboBox<>(myModel);
public void removeAllComboElements() {
myModel.removeAllElements();
}
public void addElement(String ele) {
myModel.addElement(ele);
}
}
Same for your Display2 class. Then you can call the correct method on the JComboBox that is held by the correct view/display.
This way, when you swap displays, perhaps by using a CardLayout, you can clear the JComboBox in the display that is being shown by calling its own method to clear its own combobox's model.

Connect Four Java Game Project, need help on basic concepts

I am working on a lab, it's a connect four game. I'm having trouble specifically with basic concepts like how classes communicate with each other, how to use private instance variables, how to use an ArrayList, how to compare JLabels or set them as something comparable...
To give a brief breakdown I have four classes GUI, Game, Player, Name
I can create the GUI by using two four loops, the game is a grid with 7 columns of 6 pieces. The pieces are images,
JLabel Piece = new JLabel("images/blank.png");
for example to denote an empty spot.
The GUI is based on a JFrame, single content pane and four panels, one for a header which indicates who is playing and who won, another for the 7 buttons accompanying the 7 rows, the grid itself of the possible places to be played and then a button panel which gives you the option to replay.
I'm lacking in a lot of concepts. For instance, the replay button shouldn't appear until the game has ended.
I don't understand how to use an ArrayList. I tried to use
ArrayList<ArrayList<JLabel>> myList = new ArrayList<ArrayList<JLabel>>();
So when I create the GUI by running two for loops like so
For ( c = 0 ; c<8 ; c++) {
ArrayList<JLabel> column = new ArrayList<JLabel>();
For ( r = 0 ; r<7 ; r++) {
ArrayList<JLabel> row = new ArrayList<JLabel>();
JLabel empty = new JLabel("images/blank.png");
row.add(empty);
}
column.add(row);
}
Even this small step I've already got confused.
I know the two for loops above are not correct specifically the ArrayList.
I don't know how to create the arraylist and then use them.
using something like
column.get().get();
myList.get().get();
to get a specific piece.
I don't know how to pass that to an argument so that for example if I push on button 7 for column 7, and no pieces have been played yet, I can start from the lowest area column 7 row 6 and update that to a played piece, red or yellow for the appropriate player.
This is vague and I doubt I'll get anywhere but I am desperate for help. There isn't much time available from the TA's / Teacher and I believe I am lacking significantly to be able to finish this project.
I understand how it works/what I have to do in words but in terms of applying Java code...
I'd appreciate any help.
OK first off you should use an array of Enums. ArrayLists are intended for lots of items and that can have rapidly changing numbers. Arrays are intended to house data in a grid of some sorts. Since you are using a static board, use arrays! They are also much less memory-intensive. Example:
//Note you should use [column][row] as that is common practice.
States[][] grid = new States[7][6];
//And initialize it:
for(int i = 0; i < grid.length; i++)
for(int o = 0; o < grid[i].length; o++)
grid[i][o] = EMPTY_JLABEL;
Then declare an enum (this is a new class) (NOTE: replace FULL_PLAYER_ONE_JLABEL and FULL_PLAYER_TWO_JLABEL with the JLabels that have the image for each.):
public enum States {
FULL_PLAYER_ONE(FULL_PLAYER_ONE_JLABEL), FULL_PLAYER_TWO(FULL_PLAYER_TWO_JLABEL), EMPTY(EMPTY_JLABEL);
//The image of the appropriate state.
private JLabel label;
//Enum constructors must be private
private States(JLabel label) {
this.label = label;
}
public JLabel getLabel() {
return label;
}
}
In your GUI, have a JButton that is only added to the frame when the game is over. Also add a button to indicate when each column has been clicked by the player.
JButton button = new JButton();
//Initialize JButton and add to frame...
//Anytime before the frame is set to visible:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
/* Perform tests for for what should happen.
For example test whose turn it is then call a method to add a piece to that column. Then call a checker to see if someone has won. If so, display the replay button, otherwise do nothing (allow other player to move).
*/
}
}

How to tell which item fired a mouse listener

HI all,
I'm trying to write a simple star rating component. I'm fairly new to the Java language and I'm not sure if what i want to accomplish can even be done in Java. Is it possible for me to add a JLabel inside an array of JLabel, and each JLabel in the array will have a mouse event listener. Now is it possible to set it up so that when the mouse event fires on say Label[3] that i can get the index value of it?
Here is how I built my Panel
public Rating(Integer max,int size) {
JLabel position = new JLabel[max];
this.setLayout(new FlowLayout());
for(int i=0; i != max;i++){
position[i]=new JLabel(icons.getIcon("star-empty", size));
position[i].setOpaque(true);
position[i].addMouseListener(this);
add(position[i]);
}
}
#Override
public void mouseEntered(MouseEvent e) {
JLabel a= (JLabel) e.getComponent();
//****Have some code in here to tell me where in the position array the event came from????***
int index = ?????
}
Thoughts/Idea/Suggestions please.
Note I thought of using buttons, but it looks messy and would love to find a way with ImageIcons.
THanks.
Instead of using the same listener for each label like you did:
position[i].addMouseListener(this);
...you can create a special listener class that takes the index number, and allows you to find it later:
position[i].addMouseListener(new RatingMouseListener(i));
Each label will have a separate instance of the listener with a different index value. The code for the inner class would look like something like this:
private class RatingMouseListener extends MouseAdapter {
private final int index;
public RatingMouseListener(int index) {
this.index = index;
}
#Override
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse entered for rating " + index);
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println("Mouse exited for rating " + index);
}
}
Then, you just override any method in MouseAdapter.
Also, like other people said, you might want to use JButtons instead of JLabels because they have better support for action events. You can still give them icons.
You could name each JLabel according to its index using its setName method, then use the MouseEvent's getComponent method to get the originating JLabel back, use getName on it and there's your index. That would be one way, but would involve storing the index information in two places (implicitly in its placement in the array, and explicitly as the label's name), so it's pretty much begging for inconsistency to arise.
You could also search through the array for the JLabel reference you get from getComponent, but that's not so great either, especially for large arrays.
The way I usually do it is:
int i;
for (i = 0; i <max; i++)
if (position[i] == e.getcomponent())
break;
now position[i] is the label you are looking for.
Just know that JButtons can look any way you'd like. They can have ImageIcons and don't even have to look like buttons.
Why is the index important? You know how to get the component, so just loop through the array to get the index.
Note I thought of using buttons, but it looks messy and would love to find a way with ImageIcons.
How does using a button solve the problem of determining the index? However, I also agree using a button is better than a label and then you would use an ActionListener instead of a MouseListener. You can make the button look like a label by using:
button.setBorderPainted( false );
Now if you use an ActionListener you can use the setActionCommand(...) method to store the index value of the button. Then in the event you use the getActionCommand(...) method.

Categories

Resources