I'm currently trying to program BlackJack in JavaFX and I use ImageViews to display the cards. I have a player model which has a LinkedHashMap of Strings and cards from my Card class.
LinkedHashMap<String, Card> cards = new LinkedHashMap<>();
The String always looks like "ACE-CLUB" or "SIX-HEART", because my imported images are also named like that ("ACE-CLUB.png").
Now, I don't know how I should bind the ImageView playerCardOne to the first position of the LinkedHashMap. I thought about making an array which only stores the Strings of the LinkedHashMap but then I'd still don't know how I bind an ImageView. I thought it may look something like this, but it doesn't work:
playerCardOne.imageProperty().bind(new Image("/cards/" + vm.getPlayerCard(0) + ".png"));
The method getPlayerCard(0) returns the String which is stored at the index of the number in the brackets (0).
I hope someone can help? It's my first time using MVVM and making a more complex program using JavaFX. Thank you.
Answering your question, you need to make cards
observable first:
ObservableMap<String, Card> cards = FXCollections.observableMap(new LinkedHashMap<>());
Then you can create a Binding
that always returns the first item in your map like this:
ObjectBinding<Image> firstImageBinding = Bindings.createObjectBinding(() -> {
Iterator<Map.Entry<String, Card>> iterator = cards.entrySet().iterator();
if (iterator.hasNext()) {
return new Image("/cards/" + iterator.next().getKey() + ".png");
}
return null;
}, cards);
Finally, you can bind it to an Image
property:
playerCardOne.imageProperty().bind(firstImageBinding);
Although this might work as you expect, I believe you can make it better.
First, I don't know how you implemented Card
class, but anytime you need a fixed set of constants, you should use enums:
Suit class:
enum Suit {
CLUB, DIAMOND, HEART, SPADE
}
Rank class:
enum Rank {
ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING
}
Card class:
public class Card {
private final Suit suit;
private final Rank rank;
public Card(Suit suit, Rank rank) {
this.suit = suit;
this.rank = rank;
}
public Suit getSuit() {
return suit;
}
public Rank getRank() {
return rank;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Card card = (Card) o;
return suit == card.suit &&
rank == card.rank;
}
@Override
public int hashCode() {
return Objects.hash(suit, rank);
}
@Override
public String toString() {
return String.format("%s %s", suit, rank);
}
}
Now, you can get an Image
for a Card
without the need to use a Map<String, Card>
:
public Image getImageForCard(Card card) {
String url = String.format("/cards/%s-%s.png", card.getRank(), card.getSuit());
return new Image(url);
}
If you are always interested in the image of the first card in a List<Card>
, you can get it like this:
ObservableList<Card> cards = FXCollections.observableArrayList();
ImageView playerCardOne = new ImageView();
ObjectBinding<Image> firstImageBinding = Bindings.createObjectBinding(() ->
cards.isEmpty() ? null : getImageForCard(cards.get(0)), cards);
playerCardOne.imageProperty().bind(firstImageBinding);
cards.add(new Card(Suit.CLUB, Rank.ACE));
// playerCardOne's image is now /cards/ACE-CLUB.png
cards.add(0, new Card(Suit.HEART, Rank.SIX));
// playerCardOne's image is now /cards/SIX-HEART.png