Now that the messages are all prepped and ready to go, we can finally draw them! In preparation for this long-awaited moment, add the following fields to Marquee
:
private int firstMessage = 0; private int mousePos = -1; private String mouseLink;
The firstMessage
field holds the index of the first (that is, leftmost) visible message; it is initialized to 0. (For example, after the first messageindex 0has scrolled by, firstMessage
will be set to 1.) The mouseLink
field holds the URL of the link that's currently under the mouse cursor. We'll use this when we implement the hyperlinks.
Next, make the following changes to the paint()
method:
public void paint(Graphics g) { // Initialize the drawing buffer Image drawBuffer = createImage(size.width, size.height); Graphics gDraw = drawBuffer.getGraphics(); // Clear the background gDraw.setColor(backColor); gDraw.fillRect(0, 0, size.width, size.height); if(errorMsg != null) { displayError(gDraw); g.drawImage(drawBuffer, 0, 0, null); return; } // Draw the messages for(int i = firstMessage; i < numMessages; i++) { // Don't draw messages past the right edge if(Messages[i].x >= size.width) break; // Don't draw messages past the left edge if(Messages[i].x + Messages[i].width < 0) { firstMessage = i + 1; continue; } int y; if(Messages[i].link == null) { gDraw.setColor(textColor); gDraw.setFont(textFont); y = getCenteredY(textFontMetrics); } else { if(mousePos != -1) { // If the mouse is on the link... if(mousePos >= Messages[i].x && mousePos < Messages[i].x + Messages[i].width) { mouseLink = Messages[i].link; gDraw.setColor(activeColor); } else gDraw.setColor(linkColor); } else gDraw.setColor(linkColor); gDraw.setFont(linkFont); y = getCenteredY(linkFontMetrics); } gDraw.drawString(Messages[i].text, Messages[i].x, y); } g.drawImage(drawBuffer, 0, 0, null); }
paint
in DetailAs usual, we use a for
loop to iterate through each message, starting from firstMessage
. The first thing we do inside is check to see if the current message, Messages[i]
, is past the right edge:
for(int i = firstMessage; i < numMessages; i++) { // Don't draw messages past the right edge if(Messages[i].x >= size.width) break;
If the x
field is greater than the applet width, we immediately end the loop by using the keyword break
. This handy keyword can be used anytime you want to transfer control out of a loop. In this case, we have no more messages to draw, so we resume execution with the statement after the loop:
g.drawImage(drawBuffer, 0, 0, null);
Returning to the loop, we next check if the message has scrolled out of sight to the left:
// Don't draw messages past the left edge if(Messages[i].x + Messages[i].width < 0) { firstMessage = i + 1; continue; }
If so, we simply update our firstMessage
field so that we won't draw message i
anymore. The next statement, continue
, is the counterpart of break
. Instead of exiting the loop completely, it merely skips the rest of the code in the loop for this iteration; the loop will still continue. Here, we still have the rest of the messages to draw, so we simply skip the drawing part for this message, which is next....
We declare an integer variable called y
that will hold the vertical, centered position of the message.
Then, we see if the current message is a link. If it isn't, we set the appropriate color and font for a plain text message, and calculate y
:
if(Messages[i].link == null) { gDraw.setColor(textColor); gDraw.setFont(textFont); y = getCenteredY(textFontMetrics); }
Otherwise, if the message is a link, we need to check if the mouse cursor is inside the applet and is hovering over the message. We then set the appropriate text color and also mouseLink
:
if(mousePos != -1) { // If the mouse is on the link... if(mousePos >= Messages[i].x && mousePos < Messages[i].x + Messages[i].width) { mouseLink = Messages[i].link; gDraw.setColor(activeColor); } else gDraw.setColor(linkColor); } else gDraw.setColor(linkColor);
Whether the mouse is over the message or not, we also set the font to linkFont
and calculate y
:
gDraw.setFont(linkFont); y = getCenteredY(linkFontMetrics);
The last thing we do inside the loop is to draw the message itself:
gDraw.drawString(Messages[i].text, Messages[i].x, y);
If you compile and run the applet now, you won't see anything. Why not? The messages are still past the right edge of the applet! We need to scroll the messages, so add this method at the end of the Marquee
class:
// Move messages private void moveMessages() { for(int i = 0; i < numMessages; i++) Messages[i].x -= scrollAmount; }
This simple method iterates through each message and changes their horizontal positions by scrollAmount
using the subtraction assignment operator, -=
. Also change the run()
method to call moveMessages()
:
public void run() { while(true) { try { repaint(); thread.sleep(scrollDelay); moveMessages(); } catch(InterruptedException e) { } } }
Now the messages should parade across the screenonce. To make it loop over and over, we need to reset the message positions after they have all scrolled past. Add these lines to the end of the paint()
method:
g.drawImage(drawBuffer, 0, 0, null); // Cycle after all the messages have scrolled past if(firstMessage >= numMessages) { firstMessage = 0; initMessagePositions(); } }
We simply see if firstMessage
is no longer a valid message index. If it isn't, we set firstMessage
back to 0 and call initMessagePositions()
to reset the positions.
In the next step, we'll complete Marquee by implementing the messages' hyperlinks.