Programming 101 By Way of Java

Programming 101


By Way of Java


Instructor: T. Boutell


Transcript for 1/30/97



Stimulants Anonymous

Welcome to the Java programmers' roasteria. A wide variety of stimulating bevera ges are consumed among the whirring virtual machines.

You say, "hello, folks."
You say, "this is the last installment of the java class, at least in its current form."
You say, "it's been a really good thing."
You say, "I think that, at least once over lightly at 100mph, we've touched on most of what current web browsers can do with their java implementations."
You say, "there are some interesting areas we haven't looked at. For instance, it's not hard for a java applet to tell the browser to go to a different web page, and so forth."
You say, "and the java.net.URL classes provide a somewhat less daunting way to interact with CGI programs, without using sockets."
Malus says, "Tom: will the class page still remain for a while? I've got some catching up I'm still doing."
You say, "Malus: oh, sure, the class pages will be up indefinitely. I might even rearrange the classes and announce a session in the middle of the fictional time line some thursday or other."
Malus says, "Thanks."
You say, "now, last class I started talking about the game map applet. Tonight, I'll look some more at how the program interacts with the scrollbars."
You say, "Let's take a look at the keyDown method."
You say, "please open the following source code URL:"
You say, "http://boutell.com/~boutell/class/map/MapApplet.java"
You say, "now, our primary interest in the keyboard in this applet is to allow the user to scroll using the arrow keys instead of the scrollbars."
You say, "since scrollbars are iffy in Java 1.0, this isn't a bad idea."
You say, "let's take a look at this part of the code:"
if (key == Event.UP) {
	yoffset -= tileSize;
You say, "what we're doing here is responding to the up arrow key."
You say, "does anyone remember what yoffset refers to?"
You say, "okay, keep in mind that this applet displays just a portion of the map at any given time."
You say, "in order to display the right part of the map, we need to know where our 'window' into the map is located in the larger virtual world of the complete map."
You say, "'xoffset' and 'yoffset' contain the x and y coordinates of the upper left corner of our 'window'."
You say, "when the user presses the up arrow key, we want to move up by one row of tiles. So we subtract from yoffset, moving the window upward."
You say, "next, we check to make sure we're within bounds:"
if (yoffset < 0) {
	yoffset = 0;
}
You say, "then, we tell the vertical scrollbar to update its apperance:"
vertical.setValue(yoffset);
You say, "calling setValue updates the scrollbar, but it doesn't result in an event or any other message telling the applet to scroll itself, because it presumably called in the first place. I assume that was the design decision."
You say, "so we request a repaint ourselves:"
mapCanvas.repaint();
You say, "the code for the other arrow keys is, of course, almost identical."
You say, "any questions on this method?"
You say, "if not, I'll move on to handleEvent, where we listen for arrogant demands from our scrollbar objects."
You say, "okay, let's take a look at handleEvent..."
You say, "as I've explained before, handleEvent is called first whenever an event happens. The 'standard' version of handleEvent takes care of calling the more convenient methods like mouseDown() and keyDown() and so on."
You say, "I believe it was probably the intention of the java AWT designers to provide more methods like mouseDown(), etc., to handle more events, such as, say, scrollAbsolute(). But they ran out of time, and the AWT had to be shipped."
You say, "so there are a few things, such as WINDOW_DESTROY and SCROLL_ABSOLUTE events, that can only be caught in handleEvent. They barely made it in, to put it simply."
kristen heh.
You say, "now, the first thing we do is figure out which scrollbar, if either, the event came from:"
if (event.target == horizontal) {
You say, "and then we use a 'switch' statement to look at several possible events."
You say, "I don't believe I've discussed 'switch' before. Understand that I could have used if/else if/else if... here instead."
You say, "the 'switch' statement is an efficient way to execute a particular piece of code depending on the value of an integer."
You say, "it has its roots in the C programming language."
You say, "switch takes an expression resulting in an integer as an argument, followed by a block of statements enclosed in { }."
You say, "the 'case' statement is used to announce where the code for each value of the integer begins."
You say, "the argument to case must be a constant. It can't be a variable; the compiler has to be able to determine its final value at compile time."
You say, "again, that's because switch has its roots in C and other languages concerned with performance above all else."
You say, "but it's useful if you just want to see which of a small number of possible values a particular variable possesses at the moment."
You say, "now, this is an important point:"
You say, "the thread does *not* break out of the switch { } block when it reaches the next 'case' statement."
You say, "if you want it to do that, and you usually do, you have to explicitly use the 'break' statement to jump out."
You say, "so most switch statements look like this:"
switch(x) {
	case 1:
	do_something();
	break;
	case 2:
	do_something_else();
	break;
	default:
	complain();
	break;
}
You say, "the code after 'default:' is executed if the value of x doesn't match any of the cases."
You say, "now, occasionally, you really do want several cases to do the same thing."
You say, "that's true here, because our response to all scrolling events is the same thing: we ask the scrollbar what its current position is, set new xoffset or yoffset values, and repaint the map."
You say, "most of the types of SCROLL events are fairly self-explanatory. SCROLL_LINE_UP means that the scrollbar has been scrolled up one line, which is done by clicking on the up arrow gadget in the scrollbar."
You say, "SCROLL_PAGE_UP means that the scrollbar has been scrolled up one page, which is usually done by clicking in the body of the scrollbar, above the 'thumb.'"
You say, "you probably know this intuitively from using scrollbars, but you may not have thought about it before."
You say, "SCROLL_ABSOLUTE is reported many times as you drag the thumb from one place to another."
You say, "now, it would be a very good thing if java helped us distinguish between scrolling events while you drag, and a more important event when you let go of the thumb in its final resting place."
You say, "for some tasks, especially sluggish ones like scrolling this map, it might be better to repaint only once."
You say, "alas, java 1.0 does not do that. 1.1 might; I understand that scrollbars are much improved."
You say, "... the final line of handleEvent is always important:"
return super.handleEvent(event);
You say, "it is very important that we not interfere with the normal handling of events we don't handle ourselves."
You say, "for instance, action events will never make it to the action() method if we don't make this call at the end of handleEvent."
You say, "any questions before I move on? Comments? Airborne fruit?"
kristen smiles.
jsam smiles.
stevi similies.
You say, "okay, I'll take a look at paintMap(). Feel free to break in."
You say, "paintMap()'s job is to actually draw the map in the mapCanvas component. It is called by the paint() method of mapCanvas. I put as little code as possible in the MapCanvas class itself; it's a simple slave class. MapApplet has all the needed information, because it owns the scrollbars, so it is the more logical place to paint the window."
stevi says, "why have the slave class here?"
You say, "stevi: are you asking why there is a MapCanvas class at all?"
stevi says, "yes."
You say, "ah. Good question."
You say, "well, this applet had to be laid out using GridBagLayout, placing the scrollbars in the proper place. Something had to occupy the rest of the space."
You say, "also, by making that thing an actual component, I avoid having to worry about how big the 'drawing area' is, minus the space for the scrollbars. The MapCanvas object occupies that space, courtesy of the layout manager, and when it is painted into, the output is neatly clipped to fit."
You say, "stevi: does that make sense?"
stevi says, "yeah. so conceivably, this applet could be done with just the MapApplet class, but creating MapCanvas is more elegant?"
You say, "stevi: yes. I believe it is possible to do it without MapCanvas. But I'd have to reinvent some wheels, perhaps. And I'm not sure cells with no components in them work well in layout managers."
stevi nods.
You say, "now, in the paintMap method, our task is to paint the tiles that are in the visible 'window' into the map."
You say, "one way to do it would be to paint all the tiles. The location of a tile would be x * tileSize, y * tileSize, where x and y are the coordinates in the grid of tiles and tileSize is the breadth of a tile in pixels."
You say, "that would be sufficient if the window never moved. Of course, it does, so we have to subtract xOffset and yOffset. So the location becomes: (x * tileSize - xoffset, y * tileSize - yoffset)"
You say, "that works. However, we can make it more efficient by only actually painting tiles that at least partially are visible at this time."
You say, "so we first figure out what row and column in the grid are in the upper left corner:"
You say, "yoffset / tileSize gives us the row, xoffset / tileSize gives us the column."
You say, "then we find out how many visible rows and columns there are, by dividing the amount of space in the map canvas by the size of a tile in pixels: bounds().height / tileSize and bounds().width / tileSize. That's almost right; we add one to allow for a tile that is only partly visible on the bottom or right edge."
You say, "now we have a simple test to see if a tile is visible. On the y axis, it looks like this: if ((y < (cornerRow - 1)) || (y > cornerRow + visibleRows)) then it is not visible."
You say, "y is the row in the grid of the tile we are considering."
You say, "we apply a similar test on the x axis, and then we draw the tile."
You say, "here's the call to draw the tile:"
g.drawImage(tiles[map[x][y]], x * tileSize - xoffset, y * tileSize - yoffset);
You say, "as I mentioned last week, the map array just contains tile numbers; there's a number for water, another for mountains, and so on."
You say, "the tiles array contains an Image object for each of those numbers."
You say, "so, if I simplify that a bit, it looks like this:"
int imageNumber = map[x][y];
Image image = tiles[imageNumber];
You say, "is everyone happy with this method?"
kristen wonders what you mean by 'happy'
You say, "heh. Let me rephrase that: does everyone understand this method?"
stevi says, "in theory, any way."
You say, "good enough."
kristen nods. heh.
kristen says, "oh i thought you were asking if we thought some other method would be better. heh."
You say, "well, the rest of this applet is concerned with generating the landscape."
You say, "this is a demonstration of my poor understanding of geophysics and general laziness, for the most part."
You say, "I'm not going to go through it line by line, as it doesn't touch on any new concepts."
You say, "there's one exception: I pass arrays from one method to another."
You say, "please scroll down to the fillNewCell method."
You say, "this method's job is to fill one cell in the grid by choosing a terrain for it."
You say, "my solution to the problem of choosing terrain for the map was based on the idea that the world is a bunch of volcanic islands; essentially I start with a few tall mountains and erode them."
kristen cool.
You say, "then I use the height of a cell's neighbors to help decide how tall the formerly empty cell should be; and I do this many times, until the map is mostly full, or until I have made an alarming number of passes over the map."
You say, "I choose terrain in a somewhat random way, but I use the altitude of a cell to push the result in a particular direction. So at a high altitude, mountains are more likely than swamp."
You say, "now, all this means that to fill a cell, I need to know the altitude of its neighbors."
You say, "I could have made 'altitude', the array containing the altitude of each cell on this pass, into a data member by declaring it outside of any method. That would not be unreasonable at all."
You say, "I decided instead, though, to declare it and use 'new' to create it in makeMap, and to pass it to fillNewCell as needed."
You say, "I did that for two reasons: because makeMap and fillNewCell are the only methods that are concerned with the altitude array, and because I wanted to show how arrays are passed, of course."
You say, "notice the declaration of fillNewCell:"
boolean fillNewCell(
	int[][] altitude,
	int[][] newAltitude,
	int y, 
	int x)
You say, "what I'm saying here is that fillNewCell expects to be passed two arrays, each of which has two dimensions. fillNewCell also expects two integer arguments."
You say, "notice that I don't have to say how large the arrays are."
You say, "now, when you pass an integer to a method, like this:"
foo(x);
You say, "the 'foo' method gets a copy of the value of x. If the 'foo' method changes that copy, it doesn't affect the calling method."
You say, "as I've mentioned before, objects -- things created with 'new' -- are different. When you pass one of those, you are passing a reference to it, not a complete copy of it. So the method you pass it to can change it, and should be careful not to do that if it isn't desired."
You say, "it's important to realize that arrays are objects in java. Like other objects, they are created with 'new'. And like other objects, they are passed by reference. If you pass an array to another method, and that method changes some of the elements in the array, it's changing the real thing, not a copy that goes away when the method returns."
You say, "in fillNewCell, this turns out to be handy. We change the newAltitude array, which contains the altitude of each cell for the next pass."
You say, "now, at this point, I'd like to ask if there any questions about this particular applet."
Tom twiddles his thumbs, scratches himself, and centers the chalk on the chalkboard tray.
kristen hee.
jsam smiles.
You say, "okay, then, I'd like to ask if there are any questions about anything we've covered in the class, from day one."
Tom stretches elaborately and walks around the room.
jsam has some questions about images.
jsam says, "but I suspect I should seek the answers by searching the documents I already have."
kristen hm.
Tom further expands the universe of discourse. "Are there any questions about any aspect of java? I may not know the answers, but feel free to pose them."
jsam says, "I don't really know where to look for decent instructions on how to use, say, MemoryImageSource."
jsam says, "can you modify the pixels in an already-made image, directly?"
kristen also suspects her questions will be answered when she looks over the transcripts and makes new thingies.
You say, "sam: access to pixels is deliberately difficult in java. If difficult is the right word."
jsam says, "Indeed."
jsam says, "Which suggests that it's not quite the right question."
You say, "sam: there are no setPixel() and getPixel() routines, for Image objects or for Graphics objects. That's because java is not oriented toward bit twiddling."
You say, "also, the authors wanted to push us in the direction of doing faster, more general things, like drawing entire images or lines or fills with a single call..."
You say, "however, they recognized that some tasks truly require access to pixels."
You say, "essentially, the tasks that require bit twiddling are referred to as image processing. Stuff like, say, the fancy filters in PhotoShop, can't be implemented with a bunch of calls to drawLine(), for instance."
jsam says, "jkc had a neat idea;"
jsam says, "he suggested an applet which performed star-wars like text scrolling. at the moment my exploration is motivated by that."
You say, "well, you can get hold of the pixel data for an image after all, using the PixelGrabber class."
You say, "PixelGrabber fetches an array of integers which represent the pixels."
You say, "if you don't change the color model, which is a topic unto itself, then each 32-bit integer represents one pixel."
You say, "the pixels are organized so that pixel(x, y) is at array[y * width + x]."
You say, "now, that array of pixels is a copy -- it's not directly associated with the Image object, let alone with the screen."
You say, "the bits in each integer, by the way, look like this:"
You say, "AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB"
You say, "where 'A' refers to the alpha channel, and R, G and B are red, green, and blue, of course."
You say, "so array[x] & 0xFF will give you the blue component, for instance."
jsam takes his hands off tom's throat.
Tom laughs.
You say, "the alpha channel is mostly used by transparent GIFs at this time. The only values that reliably work there are 00 for complete transparency -- when the image is drawn with drawImage, that pixel doesn't change at all -- and FF for complete opacity."
You say, "I should mention that twiddling all those bits isn't necessarily efficient, but you will fare much better if you just set entire pixels at a time to values you figure out in advance. For instance, you can set the integers in the array to values like Color.red; that's a simple integer assignment and it's pretty fast."
jsam muses.
You say, "now, once you've made all your nifty changes to this array, or created the array from scratch for a completely synthetic image, you need to create a new Image from it, and the MemoryImageSource class will do that for you."
You say, "then you've got an Image you can draw."
You say, "unfortunately, MemoryImageSource is not all that fast."
You say, "complaints about its performance are fairly common."
jsam nods.
You say, "MemoryImageSource is in the java.awt.image package. There are other useful things there, which let you take a subset of an image or scale a subset of an image as a new Image object."
jsam says, "Mmmm. If you are creating a sequence of images, then it looks like you're going to be creating a sequence of large objects to be garbage-collected as you go."
You say, "jsam: that's true. Creating gobs of objects freely and trusting the garbage collector seems to be a fact of life in java."
Tom says, "and that brings us to the end of the class."
jsam says, "so, which is harder: running an online class or an online wedding?"
You say, "the class, definitely. The online wedding was a beautiful crisis, solved by a weekend of absurd hacking. The class is a matter of consistent performance, which is always tougher."
You say, "well, I think I may call another session or two in the future, if you folks are inclined, in order to fill in some nifty topics that we passed by; applets I would have written if I had 20/20 foresight."
You say, "thanks, folks."
You say, "and thanks for attending. It's been a blast."
jsam grins.
kristen nods to tom, and applauds.
Tom closes his log, amid fanfare.

Up to the Index

Follow us on Twitter | Contact Us

Copyright 1994-2012 Boutell.Com, Inc. All Rights Reserved.