Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 20 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
20
Dung lượng
296,79 KB
Nội dung
Registering PlayAreaListeners The registering of PlayAreaListeners takes place in the PlayArea class. You won’t see the entire source listing for PlayArea.java until a bit later, but I feel it is most relevant to discuss how it registers listeners here, and how it fires events in the next section, “Firing PlayAreaEvents.” Registering the listeners is not that com- plicated. Basically, PlayArea uses a Vector to maintain a dynamic list of PlayAre- aListener s: protected Vector listeners; The PlayArea class also provides two methods for updating this Vector object, addPlayAreaListener(PlayAreaListener) and removePlayAreaListener(Pla- yAreaListener) . addPlayAreaListener(PlayAreaListener) adds the passed Pla- yAreaListener , any class that implements the PlayAreaListener interface, to listeners and removePlayAreaListener(PlayAreaListener) removes the speci- fied PlayAreaListener. Here is what they look like: public void addPlayAreaListener(PlayAreaListener pal) { listeners.addElement(pal); } public void removePlayAreaListener(PlayAreaListener pal) { listeners.removeElement(pal); } You can see that they work just by calling the Vector class’s methods. That’s all it takes. Now you have a list of nosy classes that you need to notify when you fire PlayAreaEvents. Firing PlayAreaEvents You fire PlayAreaEvents from the PlayArea class by creating an instance of the PlayAreaEvent class and then calling the registered PlayAreaListeners’ block- Landed(BlockEvent) methods. The PlayArea class has a private method for doing this: private void fireBlockLanded(PlayAreaEvent pae) { for (int l = 0; l < listeners.size(); l++) { ((PlayAreaListener)listeners.elementAt(l)).blockLanded(pae); } } fireBlockLanded(PlayAreaEvent) loops on the elements in the listeners Vector and calls their blockLanded(PlayAreaEvent) methods, passing in the given pae reference. As you can see, the PlayAreaEvent object must be created prior to call- ing fireBlockLanded(PlayAreaEvent). It does this by counting the number of rows completed and checking whether the block is out of area and passing those values along with this as the source of the event (meaning this PlayArea object 398 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 398 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. was the source of the event) and passing these values to the PlayAreaEvent con- structor method. I’ll talk more about the details of creating and firing Pla- yAreaEvent s, such as firing them in their own threads, in the next section, “Creating the PlayArea Class.” Creating the PlayArea Class The PlayArea class is the most significant part of this project. It extends the BlockGrid class, which provides the graphical representation and already defines the methods for adding, removing, flipping, and painting blocks. The PlayArea class adds the capability to accept user input (as keyboard commands). It also implements the Runnable interface and animates the block. It animates falling blocks as time goes by and also repaints the blocks as users interact with them, flipping them around and such. It also handles the clearing of rows as they are completed and fires events, informing PlayAreaListeners, of them. Inner Classes The PlayArea class defines inner classes. Now is a good a time as any to start talk- ing about inner classes because I’m close to the end of the book. An inner class is a class that is defined within another class, that is, within the curly braces of some other class, which is called the outer class. Inner classes are also sometimes called nested classes. Consider the following simple example: public class Outer { private int x; Inner inner; public Outer() { inner = new Inner(); inner.innerMethod(); } public class Inner { private int y; public Inner() { x = 1; y = 2; } public void innerMethod() { System.out.println("x = " + x + ", y = " + y); } } public static void main(String args[]) { new Outer(); } } 399 C h a p t e r 11 C u s t o m E v e n t H a n d l i n g a n d F i l e I / O JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 399 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. This code would appear in a file named Outer.java because it is a definition of the class named Outer. Inside of the curly braces of the Outer class definition is where I defined the inner class, Inner. The Inner class definition looks like any other class definition. The main differences are the fact that it is defined within another class, Outer, and has access to the Outer class’s members and methods, including private ones. The y variable is declared to be private and yet, the Inner class has access to it. See the inner variable declared as a member of Outer? That’s an Inner object. In the previous example, the Inner object is created by using the code new Inner(). The Inner class is directly accessible, as if it were a member itself, from the Outer class, but it is also accessible from other classes because it is declared to be public. To access the Inner class from a static method or from another class that has access to it, you do it as follows: Outer.Inner inner = new Outer().new Inner(); The inner class is referenced by Outer.Inner and a new instance of it is created by invoking the Inner() constructor as if it were an instance method. I wouldn’t blame you for scratching your head looking at this code. I did a shortcut. I could have done it this way too: Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); I created an instance of Outer, called outer, and then on a second line, I created an instance of Inner through outer. Before, I just did it all on one line. You saw me reference the Inner class using the syntax Outer.Inner, but that’s not the actual class name. If you take the time to write out this program and compile it, you will see two new class files generated by the compiler: Outer.class and Outer$Inner.class. The dollar sign ($) separates outer classes from inner classes, so the actual name of the Inner class is Outer$Inner. Inner classes can also be created from within methods of an enclosing class and you don’t even have to give them a name. You’ve done this before when you cre- ated listeners for your AWT components. Does this look familiar? addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { dispose(); System.exit(0); } }); The previous code was taken from GUIFrame.java. It creates an anonymous inner class that implements the WindowListener interface (by subclassing Win- dowAdapter , which implements WindowListener). Anonymous inner classes are defined when you construct the object, right after the new keyword. Anonymous HINT 400 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 400 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. inner classes have no name, but Java does create a .class file for them. It uses the enclosing class’s name, and then a dollar sign followed by a number. An example of this is GUIFrame$1.class. That is in fact the name of the class file that Java creates for the anonymous WindowAdapter. What you might not know is that you can even create an anonymous inner class for any other class. Here is an example: Canvas canvas = new Canvas() { //define an anonymous subclass of Canvas, such as overriding paint() public void paint(Graphics g) { g.fillRect(0, 0, 10, 10); } }; In the preceding example, an anonymous inner class, which is a subclass of Can- vas , is created. The paint(Graphics) method is overridden and it fills a rectan- gle. This eliminates the need to define a completely separate class, just to subclass Canvas to do something as simple as filling a rectangle. Accepting User Input for Block Movements The PlayArea class accepts user input in the form of KeyEvents. Before you get into the KeyListener implementation, take a look at the PlayArea members and methods that are available to facilitate block movements. First off, there are the static integer constants that represent possible block movements. Their names are self-explanatory; they are BLOCK_DOWN, BLOCK_UP, BLOCK_LEFT, BLOCK_RIGHT, BLOCK_CLOCKWISE, and BLOCK_COUNTERCLOCKWISE. The motions that are the oppo- site of each other are stored with opposite values (negatives of each other). For example, BLOCK_LEFT is –2 and BLOCK_RIGHT is 2. So, BLOCK_LEFT is the same as – BLOCK_RIGHT. This just makes it easier to reverse a movement. You reverse a movement when attempting one movement causes the block to move out of bounds. Performing 0 minus the number constant that represents whatever the original move was, reverses that movement, which caused the block to go out of bounds. For example, (0 – BLOCK_DOWN == BLOCK_UP), (0 – BLOCK_LEFT == BLOCK_RIGHT) , and so on. The moveBlock(int) method accepts these constants as its arguments and uses the aid of a private helper method, performMove(int), to actually move the block. Here’s how they work: /* Returns true if successful. If block movement causes block * to be out of bounds, movement is not performed, returning false; */ protected synchronized boolean moveBlock(int movement) { boolean moved = true; if (block == null) return false; removeBlock(); performMove(movement); 401 C h a p t e r 11 C u s t o m E v e n t H a n d l i n g a n d F i l e I / O JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 401 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. if (blockOutOfBounds()) { performMove(-movement); if (movement == BLOCK_DOWN) { addBlock(); blockOut = blockOutOfArea(); block = null; return false; } moved = false; } addBlock(); return moved; } private void performMove(int movement) { switch (movement) { case BLOCK_DOWN: blockPos.translate(0, 1); break; case BLOCK_UP: blockPos.translate(0, -1); break; case BLOCK_LEFT: blockPos.translate(-1, 0); break; case BLOCK_RIGHT: blockPos.translate(1, 0); break; case BLOCK_CLOCKWISE: block.rotateClockwise(); break; case BLOCK_COUNTERCLOCKWISE: block.rotateCounterClockwise(); break; } } The moveBlock(int) method is synchronized so that you can be sure that there is at most only one thread in this method moving the block around at any given time. It returns a boolean value that indicates whether the originally intended movement was performed successfully. The way it determines this is it removes the block’s square’s colors from the matrix, and then calls the performMove(int) method to actually move the block by either translating its position point for up, down, left, and right movements, or by calling the block’s rotate methods for rotation movements. Note that nothing is repainted at this point, nor are there any colors added to the matrix; only the squares of the block are rearranged. Next, it checks if the move- ment causes the block to go out of bounds. If it does, it recalls the perform- Move(int) method, only passing in the negative of the original movement this time to reverse the movement. Don’t forget that the BlockGrid class considers out of bounds to be when a block’s square moves into an area where there is already a square. When the blocks are either to the left or to the right of the moving block, it simply doesn’t allow the block to be moved in the occupied direction, but when a block falls down out of bounds that means it landed on something, either another block’s square or the bottom of the play area. When a block lands in this way there’s no more need for the reference to the Block object, so you add the block by calling addBlock(), and then set the Block to null. Remember that this doesn’t remove the colors from the matrix, but 402 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 402 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. instead, it just allows you to use another Block object reference in the block member. On the other hand, if the movement is a success, there’s no need to reverse it or set the block to null. Instead, it just adds the colors to the matrix and returns true. To accept user input, the PlayArea class uses a KeyAdapter, which incidentally is defined as an anonymous inner class. The KeyAdapter listens for key presses. Specifically, it listens for the key presses that correspond to the keyboard com- mands listed in Table 11.1. For example, it listens for KeyEvent.VK_LEFT and attempts to move the block left. If it is successfully moves the block ( move- Block(int) returns true), it will repaint the PlayArea so that you can see the block move: if (ke.getKeyCode() == KeyEvent.VK_LEFT) { if (moveBlock(BLOCK_LEFT)) repaint(); } It does this for all the keyboard commands it’s interested in. The only one that works a bit differently is handling the down arrow ( KeyEvent.VK_DOWN). There is already a thread that moves the block down, which I’ll get to next; it pauses once every second. Pressing down is supposed to make the block drop faster, so it causes the sleep interval, which is stored in currentDropPeriod, to be faster. fastDropPeriod is assigned the value 50 in the PlayArea constructor method. The normal drop period, stored in dropPeriod, is 1000. Another thing I checked for was automatic key repeating (holding a key might automatically cause it to quickly repeat). That’s fine for the other movements, especially right and left, but not okay for pressing down because there is an inter- rupt that tells the block not to wait any more and just fall. Automatic repeating would cause the block to fall as fast as possible, which is too fast! I just set up a boolean variable pressingDown that gets set to true the first time it detects a KeyEvent.VK_DOWN, and doesn’t get set back to false until the down arrow key is released. Here is the code for handling the down arrow key press: else if (!pressingDown && ke.getKeyCode() == KeyEvent.VK_DOWN) { pressingDown = true; currentDropPeriod = fastDropPeriod; blockDrop.interrupt(); //causes immediate effect } The code that handles the release of the down arrow sets currentDropPeriod back to dropPeriod and sets pressingDown to false. As you know, Canvases normally display graphics and don’t accept user input. PlayArea extends BlockGrid, which extends Canvas, so PlayArea is a Canvas. In order to let the PlayArea gain user focus, I overrode the isFocusTraversable() method to return true. It indicates whether the Canvas should normally gain 403 C h a p t e r 11 C u s t o m E v e n t H a n d l i n g a n d F i l e I / O JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 403 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. focus using Tab or Shift+Tab focus traversal. If it returns false, it can still get focus by calling requestFocus(), but any component that you want the user to be able to focus on should return true here to make it easier. Making Blocks Fall The PlayArea class implements the Runnable interface. It overrides the run() method to animate the block falling down. It uses a Thread object, called block- Drop , to run the PlayArea block dropping animation. Here is the code for the run() method: public void run() { stopRequest = false; try { Thread.sleep (dropPeriod); } catch (InterruptedException ie) {} while (block != null && !stopRequest) { if (moveBlock(BLOCK_DOWN)) repaint(); if (block != null) { try { Thread.sleep(currentDropPeriod); } catch (InterruptedException ie) {} } } if (!stopRequest) handleBlockLanded(); } stopRequest is a boolean variable that is true if the thread is told to stop running because you shouldn’t call the deprecated stop() method. The first Thread.sleep(long) call exists so that no matter what, the block will pause at the top of the PlayArea at least the amount of time specified by dropPeriod, which is the normal pause between each successive down movement. The while loop con- tinues while block is not null (or it doesn’t have anything to move) and while there is no request to stop running. It attempts to move the block down and repaints if it does, and then checks if block is null again (because moveBlock(int) sets block to null when the block lands), and then it sleeps and repeats. After the while loop, the run() method checks whether it stopped because of a stop request. If it didn’t it, assumes that it stopped because the block landed on something and calls handleBlockLanded() as a result. The PlayArea class implements the Runnable interface. Its run() method moves the block down. However, the block must be provided by a different class by calling the introduceBlock(Block) method. Doing this triggers a Thread, blockDrop to start running the PlayArea, which moves one block as far down as it can go, and then stops. No outside class needs to run the PlayArea in a differ- ent thread because PlayArea takes care of it itself. In the real world, you TRICK 404 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 404 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. probably don’t want to do it this way. If you needed to hide the thread from the outside world, you are better off creating a protected or private inner class that extends Thread so that classes that don’t need to start the thread can’t run the thread by creating a new Thread object using an instance of PlayArea. The handleBlockLanded() method checks if any rows were completed and calls rowClear(int[]) if there were any. Here’s how this works. It declares a boolean array, rows[], which is the same size as the number of rows. true indicates rows, that, by their index, are completed and need to be cleared. Initially, they are all explicitly set to true and a variable, nComplete, which counts the number of rows that are completed, is set to the number of rows. Then it loops on the columns and rows, checking whether there is one square in every column of the rows (these rows are complete). Any time it finds a cell that doesn’t contain a square, it sets that row’s rows[] array to false and decrements nComplete. It won’t check that row again. The loop will also stop as soon as it knows for sure there aren’t any completed rows ( nComplete is zero): for (int c=0; c < getCols() && nComplete >= 0; c++) { for (int r=0; r < getRows(); r++) { if (matrix[c][r] == null && rows[r]) { rows[r] = false; nComplete ; } } } After this loop, the rows[] array is true at the index of every row that is complete and needs to be cleared. It rearranges this information so that an array, rowsTo- Clear[] , which is the size of the number of rows that are complete is created and the elements of the array are set to the row indices of the matrix array that actu- ally need to be cleared. Using the subscript Rindex++ sets the current value of the index to Rindex before incrementing it, so it is initially zero: int Rindex = 0; int[] rowsToClear = new int[nComplete]; for (int r=0; r < getRows(); r++) { if (rows[r]) rowsToClear[Rindex++] = r; } Next it calls the rowClear(int[]) method, passing in rowsToClear[] as the para- meter. rowClear(int[]) animates all the rows that need to be cleared with flash- ing colors. The flashing colors are set up in the flashColors[] array. It does this by looping on this array and on all the cells in the rows that need to be cleared, and then setting the color of the squares to the different colors and repainting after each color is set. The last color of the flashColors[] array is null, so that makes the squares disappear. 405 C h a p t e r 11 C u s t o m E v e n t H a n d l i n g a n d F i l e I / O JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 405 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Now there is the matter of making the blocks piled up on top of the rows fall down to fill up the newly vacated space. This isn’t as straight-forward as you might think. You have to consider the possibility that when multiple rows are cleared simultaneously, they might not necessarily be successive rows (for exam- ple, row 10 and row 12 need to be cleared, but not row 11). You also have to handle the fact that as you’re moving rows down, new rows are being formed at the top because there aren’t any rows above the play area to fall down. Here is the complicated loop that handles this: for (int c=0; c < getCols(); c++) { nRows = 1; for (int r = rows[rows.length - 1]; r >= 0; r ) { while (nRows < rows.length && r - nRows == rows[rows.length - nRows - 1]) { nRows++; } if (r >= nRows) matrix[c][r] = matrix[c][r - nRows]; else matrix[c][r] = null; } } repaint(); } It loops on all the columns of the PlayArea matrix and sets the nRows variable to 1. nRows counts the number of rows that have been cleared. It loops on the rows, starting at the bottom-most row that was cleared, indicated by the last element of the rows array, rows[rows.length – 1] and works its way up to the top of the PlayArea. nRows starts at 1 for each column because it already accounts for the bottom-most row that was cleared. For any cleared row, you have to move the squares in the row above the cleared row down into the cleared row. Hopefully that makes sense, but it’s not that straight-forward. If two rows were cleared, the squares in the row that is two rows up have to move down into this cleared row. That’s why it has to count the number of cleared rows as it works its way up to the top. It counts them in the while loop. Although the number of cleared rows I counted so far is still less than the total number of cleared rows ( nRows < rows.length) and the row above this row (r - nRows) was cleared to ( rows[rows.length - nRows - 1]) increment nRows (nRows++). Remem- ber that the rows[] array elements store the matrix row indices that were cleared, so if the row above this one needs to be cleared, it will be stored at the next index of the rows[] array, which is why this check works. As it moves up in rows, it moves the squares of rows that are nRows above this row down into this row. For example if you clear rows 10 and 8, the squares in row 9 move down into row 10. But because row 8 was cleared, row 7 moves down into row 9, and then row 6 moves into row 8, row 5 into row 7, and so on. It also has to be careful near the 406 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 406 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. top. What row moves down into row 0? It’s the top row, so there are no rows above it to move down. These rows just have all their colors set to null. That’s what the last if statement does. If this row index is greater than or equal to the number of rows cleared, there must be some actual rows still left to move down, but if this is row index 1, for example, and four lines were cleared, there aren’t any more rows to fall down, so set the colors to null. This PlayArea’s run() method is invoked from within the introduceBlock (Block) method: public void introduceBlock(Block b) { if (block == null) { blockPos = new Point(); block = b; blockPos.x = (getCols() - b.getSize()) / 2; blockPos.y = 1 - block.getSize(); addBlock(); repaint(); blockDrop = new Thread(this); blockDrop.start(); } } If this PlayArea doesn’t already have a block (block == null), set block to the passed in Block parameter. Then center its position at the top (actually above the top), add the colors by calling addBlock(), show the colors by calling repaint(), and create a new thread using this Runnable PlayArea, and then start a-droppin’ the block. The EventThread Inner Class The handleBlockLanded() method also takes care of setting up and dispatching the EventThreads. EventThread is an inner class that extends Thread and fires PlayAreaEvents. The reason why you fire them off in their own threads is because there is no reason to tie up the current thread ( blockDrop) with calling the lis- teners’ PlayAreaEvent handling methods. Who knows what they do and how long they will tie you up? Well, you do because you’re writing the code for it even- tually. It is definitely good practice to fire events off in their own threads so that the current thread is not taken over by any of the listeners to perform what could be enormous tasks. Nope, you’ll just fire off a thread and then go about your business. Here is a listing of the EventThread inner class: private class EventThread extends Thread { final static int BLOCK_LANDED = 0; int event; PlayAreaEvent pae; 407 C h a p t e r 11 C u s t o m E v e n t H a n d l i n g a n d F i l e I / O JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 407 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... blockDrop.start(); } } JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 412 412 Java Programming for the Absolute Beginner public void run() { switch (event) { case BLOCK_LANDED: fireBlockLanded(pae); break; } } } /* method that animates row clearing */ protected void rowClear (int[] rows) { Color[] flashColors = {Color.red, Color.green, Color.blue, Color.white, Color.yellow, Color.magenta, null}; int nRows = 1; for (int... Java program that copies a file by reading the files contents and writing it to a new file The source code for FileCopy .java is: /* * FileCopy * Demonstrates File I/O by copying a file * arg[0] - source filename TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 417 417 import java. io.*;...JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 408 Java Programming for the Absolute Beginner 408 protected EventThread (PlayAreaEvent pae, int e) { this.pae = pae; event = e; } public void run() { switch (event) { case BLOCK_LANDED: fireBlockLanded(pae);... if (rows[r]) rowsToClear[Rindex++] = r; } rowClear(rowsToClear); } (new EventThread(pae, EventThread.BLOCK_LANDED)).start(); } Chapter 11 block = null; return false; JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 414 Java Programming for the Absolute Beginner 414 public void addPlayAreaListener(PlayAreaListener pal) { listeners.addElement(pal); } public void removePlayAreaListener(PlayAreaListener pal) {... cleared as a result, and it also tells you whether the block landed out of area It gets this information by listening to PlayAreaEvents with the blockLanded(PlayAreaEvent) method it must implement Here is the listing for PlayAreaTest .java: /* * PlayAreaTest * Tests the PlayArea class */ import java. awt.*; import java. awt.event.*; public class PlayAreaTest extends GUIFrame implements PlayAreaListener {... actionPerformed(ActionEvent e) { playarea.introduceBlock(block); playarea.requestFocus(); status.setText("Block Falling"); } }); add(b, BorderLayout.SOUTH); Chapter 11 Point[] p = { new Point(1, 0), new Point(0, 1), new Point(1, 1) }; block = new Block(3, p, Color.white); playarea.addPlayAreaListener(this); add(playarea, BorderLayout.CENTER); JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 416 Java Programming for. .. such as the blockDrop thread, and it needs to be careful when it sets the block to null because there can be active threads moving the block around Here is the code: JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 410 Java Programming for the Absolute Beginner 410 public PlayArea(int cols, int rows, int cellsize) { super(cols, rows, cellsize); pressingDown = blockOut = false; currentDropPeriod = dropPeriod... * to be out of bounds, movement is not performed, returning false; */ protected synchronized boolean moveBlock(int movement) { boolean moved = true; if (block == null) return false; removeBlock(); performMove(movement); if (blockOutOfBounds()) { performMove(-movement); if (movement == BLOCK_DOWN) { addBlock(); blockOut = blockOutOfArea(); TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase... you get 100 points for each line that you clear, 300 if you clear two lines at once, 600 points for clearing three lines at once, and 1000 points for clearing four lines with a single block You also get a 10,000 point bonus each time you clear four lines multiple times in a row (not necessarily with two successive blocks, but two successive times lines are cleared) TEAM LinG - Live, Informative, Non-cost... rows = new boolean[getRows()]; for (int r=0; r < getRows(); r++) { rows[r] = true; } for (int c=0; c < getCols() && nComplete >= 0; c++) { for (int r=0; r < getRows(); r++) { if (matrix[c][r] == null && rows[r]) { rows[r] = false; nComplete ; } } } pae = new PlayAreaEvent(this, nComplete, blockOut); if (nComplete > 0) { int Rindex = 0; int[] rowsToClear = new int[nComplete]; for (int r=0; r < getRows(); . KeyAdapter listens for key presses. Specifically, it listens for the key presses that correspond to the keyboard com- mands listed in Table 11.1. For example, it listens for KeyEvent.VK_LEFT. simple as filling a rectangle. Accepting User Input for Block Movements The PlayArea class accepts user input in the form of KeyEvents. Before you get into the KeyListener implementation, take. false; removeBlock(); performMove(movement); 401 C h a p t e r 11 C u s t o m E v e n t H a n d l i n g a n d F i l e I / O JavaProgAbsBeg-11.qxd 2/25/03 8:57 AM Page 401 TEAM LinG - Live, Informative, Non-cost