JavaFX Example - FlashCard Game

I’ve gotten my FlashCard game to a point that is good enough and when I get the chance I’ll post a link to a zip of the contents so you can download it and check it out. Before I do that, I thought I’d take this chance to go over some of things I’ve learned, using my FlashCard application as an example.

My Flashcard game is a simple application that displays flashcards, word side up. Clicking on the Flip Me! button flips the card to show an image of the word. Clicking on the Next button cycles through the deck of cards. Since it is primarily a graphical application, I thought JavaFX would be a good choice for it. This post will go through the files that make up the application, pointing out some of the interesting characteristics of a JavaFX application.

The Flashcard objects themselves are made up of a Word and a CardImage. A Word object is made up of an array of Letters.

Let’s take a look at the Letter class:

class Letter extends CompositeNode {
    attribute myLetter: String; //String the represents a Letter in the Card
    attribute position: Number; //Position the letter is in in the current word
    attribute size: Number; //Size of the entire word
    
}

The declaration for the class extends CompositeNode, I am not sure exactly why except to say that to accomplish anything more than the most simplistic things you need to.

I stored the actual letter as a String because:

a. I wasn’t sure how to use characters in JavaFX and didn’t feel the need to look it up.

b. I wanted the option of tying two characters together to make one sound (called bends (”cl” or “st”) or diagraphs (”sh” or “ch”)).

The letter is class is repsonsible for displaying the letters and thus needs to know where to position them, therefore the size and postion numbers are provided so it can determine where in the canvas to put the letters.

The operation componseNode is used to display the letter:

operation Letter.composeNode() {
    return Text {
        var cardFont = Font {faceName: 'Arial', style: BOLD, size: 150}
        verticalAlignment: BASELINE
        content: myLetter
        font: cardFont
        fill: rgba(0x00, 0x00, 0x00, 0xff)
        x: (250 - (size*45) + ((position) * 90))
        y: 200
    };
 }

Pretty basic stuff, no other operations are defined. The main thing here is that I needed to define the size and position because apparently, these JavaFX objects are not aware of their size. There are operations that return width, but it is always zero. My first attempt was to have the word determine it’s own size and then place it on the canvas accordingly. Since that never worked, I resorted to this type of brute force method.

Word is slightly more complex:

class Word extends CompositeNode {
    public attribute letters: Letter[];
    public attribute word: String;
    private attribute show:Boolean;
    operation Word(aString:String);
    operation flip();
    operation showWord();
 } 

It contains an operation to flip the card, one to show the Word (to reset the state), and a constructor. The interesting part here is the “constructor”. As I mentioned in my previous post, there is no real constructor per say, instead you define a method with the same name as the class and this is used as a constructor. The constructor for Word is here:

operation Word.Word(aString:String) {
    //for each character in aString
    var aSize = aString.length();
    var i = 0;
    word = aString;
    while (i < aSize) {
        var nLetter = Letter {myLetter:aString.substring(i,i+1) position:i size:aSize};
        insert nLetter into letters;
        i++;
    }
 }

It takes an actual String and decomposes it into letters. This is one reason why justification #2 above isn’t so true without putting some serious logic into this operation. I do want to retain a way to simply create the cards, and this provides it. If and when I add sound, however, I will have to find a way to designate short and long vowels, hard and soft consonants, and letter combinations. If I leave off the letter combinations, I think I could get away with using upper and lower case letters to designate the different types.

The way to call a “constructor” in JavaFX is as follows:

    word = new Word(aWord);

Just the way you would do it in Java. It is helpful to note here, that if the attributes you want to set are public, you could create a word as follows:

word = Word {
    word: aWord
} 

Of course, this way, the method to populate the letters array does not get called, the word attribute is simply assigned the value that was passed. The constructor allows the FlashApp to create a new FlashCard using one line and not two or three.

Arrays are interesting in JavaFX script. You can declare an array as follows:

public attribute letters: Letter[];

To populate it, you need to use an insert statement:

 insert new FlashCard("dog", "{__DIR__}/img/dog.gif") into flashcards;

There doesn’t seem to be any type of size or length operation available on the array, so though you don’t need to specify the size when you create it, it seems you need to define the size if you are going to need it somewhere. In this case, the FlashApp needs to know when to flip to the beginning of the deck so I had to define a size variable to keep track of the length of the array.

The last piece of code I wanted to show, is the code to create the actual FlashApp frame:

    var f = Frame {
        background: grey
        title: "Flash Card JavaFX"
        width: 600
        height: 400
        content: Box {
            orientation: VERTICAL:Orientation
            content: [
                Canvas { content: bind flashcards[myIndex] },
                Box {
                    orientation: HORIZONTAL:Orientation
                    content: [
                        Button {
                            cursor: HAND
                            text: "Next"
                            action: operation() {
                                 flashcards[myIndex].showWord();
                                 if (myIndex < (size-1)) {
                                     myIndex++;
                                 } else {
                                     myIndex = 0;
                                 }
                            }
                        },
                        Button {
                            cursor: HAND
                            text: "Flip Me!"
                            action: operation() {
                                 flashcards[myIndex].flip();
                            }
                        }
                    ]
                }

            ],
        }
        onClose: operation() { System.exit(0); }
        visible: true
    }; 

I used a Box layout inside a Box layout to display the card and the buttons. The first line, binds the component for the card to the currentCard variable:

Canvas { content: bind flashcards[myIndex] },

The use of the word bind allows JavaFX to update the display when the index in the array is changed. Without the bind keyword, the canvas does not update when the index is changed. with it, the update occurs automatically.

The buttons are simple to create, along with their actions. Take this example:

Button {
	cursor: HAND
	text: "Flip Me!"
	action: operation() {
		flashcards[myIndex].flip();
	}
}

The cursor attribute tells JavaFX to change the cursor shape when it hovers over the button. The text is obviously the test displayed, and the action contains the commands to execute.

All in all JavaFX seems to be on it’s way to being a decent language to write GUI applications. Currently, I don’t see how anyone but Java developers are going to be able to program in it and I suppose that isn’t a goal of the language. Also, since it is obviously built upon Swing, it help to know Swing and some of the annoyances still seem to be there. There appear to be a few bugs still existing, such as the width attribute always returning zero, but this could be due to a misunderstanding which in itself is due to a lack of documentation.

With some updated documentation, I think we may be looking at this to build our next version of Rev or Evo. It should remove a lot of the complexities of the Swing coding and hopefully simplify the frontend while making it visually more appealing.

I’d like to see a JavaFX “constructor” added to our Rev product to simplify the creation of JavaFX programs that access a peristence layer. It isn’t exactly on the product timeline, but if I have another vacation that I ended up staying home for, perhaps I’ll take a look at it.

Next post will include some TODO’s in the project, a link to download the code, and if I get to it, a link to run the application through Java Webstart.

2 comments

  1. Adding Sound to the FlashCard JavaFX Game | Java Hair
  2. Create and Deploy a JavaFX WebStart Application | Java Hair

Leave a reply

You must be logged in to post a comment.