Programming 101 By Way of Java

Programming 101


By Way of Java


Instructor: T. Boutell


Transcript for 12/12/96


Stimulants Anonymous

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

You say, "welcome to the seventh meeting of the java class."
kristen meeps.
You say, "you may recall the calculator applet assignment. My own solution to that problem was remarkable for its spartan appearance."
You say, "this is because we haven't yet addressed the issue of how to 'lay out' an applet's components in a sensible manner, although some of you have already looked into that on your own."
You say, "I've written a version of the calculator applet which is considerably more attractive. Please open up the following URL:"
You say, "http://boutell.com/~boutell/class/PrettyCalculator.html"
You say, "take a peek at the applet, then move to the source code. Please page me when you're ready to continue."
jkcohen says, "Ooh. Pretty."
You say, "okay..."
You say, "now, take a look at the 'init' method in the PrettyCalculator class."
You say, "the first new thing I'm doing is setting an alternate font. I did that because the default font looked a bit pathetic in this applet."
You say, "I create and set the font in one line, like so:"
You say, "setFont(new Font("Helvetica", Font.PLAIN, 14));"
You say, "the constructor of the Font class takes a font family name, a font style, and a point size."
You say, "the font styles, such as Font.PLAIN, are constants defined in the font class, so you can find them in the API documentation. The families that actually work in practice seem to be Courier, Helvetica, Times, and perhaps Symbol. You can try calling getFontFamilies, if memory serves, to find more."
You say, "font support is a bit weak in most 1.0 implementations of java, so I generally stick to those three fonts for most purposes."
You say, "now, the next bit is more interesting. Recall that in the original version of the calculator, we would create various components, and then call add(component); to add them to the applet."
You say, "how exactly they were added wasn't explained at the time."
You say, "when you call the add method of any container, such as an applet, the container will consult with its current 'layout manager' to find out how the component should be positioned."
You say, "java was designed to accommodate many layout managers. By default, an applet has a FlowLayout, which simply lays out components from left to right. This is OK for simple examples but not all that useful for serious work."
You say, "fortunately, there are better layout managers provided. In practice, I feel that the GridBagLayout manager is the only layout manager of general usefulness."
You say, "GridBagLayout is neat because it works much like an HTML table."
You say, "this may not be immediately obvious, but when you work with it you'll discover it has a similar 'feel' to laying out HTML table cells, some of which span multiple rows or columns, and so on."
You say, "so, first we construct the layout manager:"
You say, "GridBagLayout gridbag = new GridBagLayout();"
You say, "next, we tell the applet to use this layout manager:"
You say, "setLayout(gridbag);"
You say, "now, I mentioned that GridBagLayout uses 'cells' much like table cells in HTML. These cells have quite a few possible settings to control how much space they get and where they appear..."
You say, "so a class has been provided to record that information. It's useful to create one of these 'GridBagConstraints' objects, and make small changes to the settings as you create one control after another."
You say, "here we construct it:"
You say, "GridBagConstraints c = new GridBagConstraints();"
You say, "next, we set a few characteristics that all of the cells in our layout are going to want. The first question we have to answer is, 'should these components expand to fill the available space in the applet, or should they take up the smallest amount of space they are comfortable with?'"
You say, "for the calculator, we want a large 'results' display, and large buttons never hurt. So it makes sense to take the first route and ask to fill all of the available space."
You say, "we do this by setting the 'fill' member, like so:"
You say, "c.fill = GridBagConstraints.BOTH;"
jkcohen says, "BOTH here means vertical and horizontal?"
You say, "this indicates that components should be resized to fill the entire cell they occupy, on both the horizontal and the vertical axis, yes. We could specify one or both or neither instead."
You say, "now, by setting the 'fill' member to BOTH, we've indicated that we want the components to completely fill their cells. But we still haven't said how large the cells should be."
You say, "we need to set a 'weight' which asks that we be given any extra space in the applet."
You say, "this can be set separately for every cell, but we don't need to do that here. All we need is a non-zero weight, so the layout manager will know we are interested in extra space. By default, the weight would be zero and our calculator would be of the minimum size the buttons fit into."
You say, "c.weightx = 1.0;"
You say, "c.weighty = 1.0;"
You say, "here we're saying that all cells are equally interested in extra space. I know that this is a bit confusing, and you may want to just remember to include these two lines if you want your applet to completely fill the available space."
You say, "HTML tables don't have an equivalent concept, since HTML table cells are always supposed to be no bigger than absolutely necessary."
You say, "any questions at this point before I go on?"
You say, "okay, I'll proceed, then."
You say, "now, just as in HTML tables, there are times when you want to give a particular cell more than one row or column's worth of space."
You say, "in Java you can do this two ways. You can set gridwidth and gridheight to any number of cells you like, just like colspan and rowspan attributes in HTML table cells..."
You say, "or you can use the convenient constant REMAINDER to mean 'give me all the remaining space on this axis.'"
You say, "there's nothing wrong with using a number of cells, except that you'll have to remember to change it if you add more cells elsewhere."
You say, "so, for the first cell, which contains the 'result' TextField, we want to span the entire applet horizontally."
You say, "so we set gridwidth = REMAINDER."
You say, "also, we want to occupy the upper left cell, so we set gridx and gridy appropriately..."
You say, "c.gridx = 0;"
You say, "c.gridy = 0;"
You say, "note that cells begin at 0,0 and count to the right and down."
You say, "next we construct the text field..."
You say, "result = new TextField();"
You say, "and now, we tell the layout manager to associate the constraints with this component, like so:"
You say, "gridbag.setConstraints(result, c);"
You say, "finally we add the component to the layout:"
You say, "add(result);"
You say, "now, the next cell is the '7' key on the second row, so we set the gridx and gridy variables to 0 and 1 respectively, and we set gridwidth to 1 instead of REMAINDER:"
You say, "c.gridwidth = 1;"
You say, "c.gridx = 0;"
You say, "c.gridy = 1;"
You say, "my keyboard buttons are of class KeyboardButton, which is a subclass of Button that I whipped up. I'll come back to those after we finish looking at the layout manager."
jkcohen says, "But the layout manager doesn't know how many components you're going to put on that line in advance."
You say, "jon: no, it doesn't. Fortunately, the layout doesn't actually 'happen' until layout() is called on the container. And by then, the whole story is known..."
jkcohen says, "You could merrily keep on adding buttons with c.gridx=100, couldn't you?"
You say, "jon: yes, you could. Cells you haven't defined constraints for don't make any demands, so those rows or columns would occupy zero pixels and it would work out pretty reasonably."
jkcohen nods.
You say, "layout(), by the way, can be called explicitly, but it'll be called on our behalf after init(), so we don't need to."
You say, "now, the constraints for most of the buttons are very similar, so I won't go through each of them..."
You say, "essentially I reset c.gridy between rows, and c.gridx for every cell."
You say, "notice that keeping one GridBagConstraints object around is handy, since we can just update whatever actually needs changing from one cell to the next."
You say, "the last button is a lot like the TextField at the very beginning, in that it spans the entire layout horizontally..."
You say, "c.gridwidth = GridBagConstraints.REMAINDER;"
You say, "and once we return from init(), the applet will lay out the individual components and create the 'real' windows or mac or Motif controls, consulting the layout manager to find out where and how large to create them."
You say, "I see that we've just covered the basics of using a layout manager in about 35 minutes. Whee. I should ask if there are any questions at this point. We have plenty of time, although there's other stuff in this applet I should review."
jkcohen says, "To tell you the truth, GridBag is the most comprehensible thing we've covered in this class."
Tom laughs.
You say, "jon: well, it's Just Another Object, as opposed to a brand new language feature. Perhaps that helps."
stevi says, "so you could conceivably write the gridbag in columns, instead of being contrained to rows, eh?"
You say, "stevi: yes, you could. I tend to do it 'like a table' because HTML tables are familiar, to me at least."
stevi nods.
You say, "let me make a few more observations about how GridBagLayout will work..."
You say, "much like an HTML table, GridBagLayout makes sure that table cells line up neatly in rows and columns, even if their components don't occupy an entire cell (if c.fill = NONE, for instance)."
stevi says, "so if you did a cell for 1,0 and set its fill to REMAINDER, and then created a 1,2, the first cell would take up 1,0 to 1,1?"
You say, "stevi: it should. I have not tried that scenario but that is the sensible outcome."
jsam says, "if you want to have, say, 8-pixel gaps between buttons, is there an easy way to specify that?"
You say, "jsam: yes. You can set the 'internal padding' in the ipadx and ipady members."
You say, "jsam: this padding is always supplied around the component within the cell if you ask for it."
You say, "jsam: I believe ipadx and ipady values of 4 would provide eight pixel gaps."
You say, "I'm going to talk about weightx and weighty for a moment, since they are confusing but useful..."
You say, "the layout manager needs to decide how to distribute extra space, when there is some available beyond the minimum acceptable size."
You say, "to decide how much extra horizontal space the cells in a *column* will want, the gridbag takes a look at the weightx values of the cells in that column."
You say, "gridbag takes the largest weightx value for each of the columns, totals these up, and gives each column the share it has asked for, relative to the others."
You say, "so if the largest weightx value in the first column is 1.0, and the largest weightx value in the second column is 2.0, and there are no other columns that have any nonzero weightx values, then the first column will get 1/3rd of the extra width, and the second column will get 2/3rds of the extra width."
jsam nods belatedly.
stevi ahhs. smart gridbag.
You say, "extra space on the Y axis is distributed the same way, by looking for the largest requests on each row."
jsam nods.
You say, "now, at this point, you've got plenty of information on how to use a layout manager with existing components."
You say, "but you might be curious how the layout manager 'knows' how much space a component will actually need, at a minimum."
You say, "well, component has a 'minimumSize()' method, which returns a 'Dimension' object with 'width' and 'height' members."
You say, "subclasses of Component, such as Button and TextField, replace this method with a version that takes into account the width of the label or the number of characters they have been created to accommodate."
You say, "this is particularly important for Button since buttons don't scroll horizontally and would look pretty awful if they did."
You say, "when you make your own component, you don't subclass Component directly; you subclass Canvas, which was provided just to help you make your own components..."
You say, "if your component is very simple, you might simply call resize() once in your constructor to indicate how much space you need, and leave it at that."
You say, "if you do, the default version of minimumSize() will just return your current size."
You say, "there is also a 'preferredSize()' method, which returns how much space you would ideally like to get."
You say, "the layout manager takes both into account in its decisions."
jsam nods.
You say, "any questions about this stuff before I move on?"
stevi says, "does layout manager take into account the size of the applet per the html page?"
You say, "stevi: the size of the applet in the web page is fixed when the applet starts, and can't change..."
You say, "stevi: the layout manager is aware of that size, yes, and it will distribute any 'extra space' according to the weights, as it does in my calculator."
stevi nods.
You say, "stevi: unfortunately applets can't 'negotiate' for space in a web page."
You say, "stevi: however, if you create your own free-standing Frame object as part of your applet, which isn't hard to do, and add your components to that, then you can resize it freely."
stevi nods sagely.
Tom will go on to the KeyboardButton class. Feel free to fire off questions about the layout manager stuff if they occur to you.
You say, "stevi: if you want to make a Frame of your own, and have it 'shrink to fit' what the layout requires, call 'pack' after laying out the components."
You say, "now, I'm going to take a look at the KeyboardButton class."
You say, "the purpose of this class is to provide virtual keyboard buttons in the calculator, or another applet, that act pretty much like keys on the real keyboard."
You say, "naturally, this class extends Button."
You say, "the workings of the class are simple. It accepts a String label when constructed..."
You say, "and when the 'action' method is invoked, it tells the calculator to accept that string as input. It returns 'true' from action so the container -- the applet in this case -- won't have its own action method called unnecessarily."
You say, "now, I could have written the constructor to take a PrettyCalculator object as an argument. Then I could have called insertString on that object from my 'action' method."
You say, "that would have worked just fine. The only weakness is this: it would mean that the KeyboardButton class is only useful when used with the PrettyCalculator class."
You say, "that would be a waste, since it's probably something I'll have use for again. I could copy and paste it, but I'd rather have a class I can make public and use without changes."
You say, "trouble is, I have to 'connect' it to an owner somehow, in order to pass the string to the owner when the button is selected."
You say, "it would be nice if there was a way to say 'this class requires a KeyboardOwner, which knows how to accept a string from me.'"
You say, "fortunately, Java lets us say exactly that."
You say, "we do this by defining a KeyboardOwner 'interface.'"
You say, "here's the interface code:"
You say, "interface KeyboardOwner"
You say, "{"
You say, "public void insertString(String s);"
You say, "}"
You say, "this interface gives us a way to refer to 'any class that understands how to 'insertString'."
You say, "now, take a look at the constructor of KeyboardButton:"
You say, "KeyboardButton(KeyboardOwner ownerArg, String label)"
You say, "notice that the first argument is a KeyboardOwner. Java lets us pass around references to interfaces, and it checks to make sure the object we pass really does implement that interface."
You say, "later, when we want to actually insert the string, we do this:"
You say, "owner.insertString(getLabel());"
You say, "to invoke that method of the interface."
You say, "now, an interface is neat and all, but we have to implement it somewhere to make it useful. We're doing that in the PrettyCalculator class, and we can do it in other applets that find KeyboardButton objects useful."
You say, "here's the declaration of PrettyCalculator:"
You say, "public class PrettyCalculator extends Applet implements KeyboardOwner"
You say, "'implements' is followed by one or more interface names, separated by commas. We can implement as many interfaces as we want. If we wanted threads, we'd implement Runnable as well as KeyboardOwner. Then we just have to provide the methods the interface calls for."
You say, "so, sure enough, here's our version of 'insertString', fulfilling the requirements:"
You say, "public void insertString(String s)"
You say, "... which grabs the current text of the result field, adds the new text, and does a trick to make sure the caret winds up at the end of the field:"
You say, "result.select(text.length(), text.length());"
You say, "most environments automatically put the caret at the end of the selected part of a text control, and we're taking advantage of this by making an empty selection at the very end of the text."
You say, "so, by declaring a KeyboardOwner interface, we get the freedom to use KeyboardButtons in more than one applet. And by implementing it and providing an insertString method in PrettyCalculator, we do what's necessary to take advantage of it in this particular applet."
You say, "any questions on anything we've covered so far tonight?"
jkcohen says, "I really don't know where to begin with questions on the idea of an 'interface.'"
jkcohen says, "What does an interface connect? Let's try it that way."
You say, "jon: an interface lets other classes know that you will definitely have certain methods."
You say, "jon: so it is a way of connecting two classes, if you like, without the one having to refer to the other by name."
You say, "jon: look at it this way: I could have implemented this applet without interfaces. I could have just called the insertString method of the calculator directly from the KeyboardButton. But I would have had to keep a reference to the PrettyCalculator object in the button. So I wouldn't be able to use KeyboardButton with any other class, without rewriting it."
You say, "jon: I didn't really want to say 'a KeyboardButton only works with a PrettyCalculator'. I wanted to say 'a KeyboardButton works with anything that supports the KeyboardOwner standard.' And 'to support the KeyboardOwner standard, you have to have an insertString method and proudly advertise your KeyboardOwner compliance in your declaration.'"
jkcohen says, "OK. And the insertString method is a method that we've made up, or is it basic to Java?"
You say, "jon: we made it up completely. We formalized it by putting it in the interface, so Java will know to sternly check whether it's really provided in PrettyCalculator or not."
You say, "jon: this way, we could put KeyboardButton and KeyboardOwner in files of their own, declared public, and use them in many applets. Each applet just has to implement the interface."
jkcohen says, "OK. Light is slowly starting to dawn."
You say, "jon: the Runnable interface works the same way. It allows a thread to start in any class that implements it, which makes threads much more flexible."
You say, "well, I'm going to stick around and answer questions, but I'm at the end of my new material for this week. Really hard gearhead questions are officially allowed."
stevi says, "how about bonehead questions?"
You say, "oh, sure, those too."
stevi says, "like, what's the caret about?"
You say, "oh, okay."
You say, "'caret' refers to the blinkything in the text field -- the insertion point. I wanted to keep it at the end because it looks more natural for it to move when you insert more numbers."
stevi says, "ahhhh. cool."
Tom notes that, to make the calculator 100% utterly natural, you would probably need to replace the 'result' field with your own version of Canvas in order to completely control what the keyboard does.
kristen oogs.
Tom blinks at kristen.
kristen says, "are we at some point going to be able to pick your brain about programs we're working on?"
You say, "kristen: now would be a fine time."
kristen says, "well okay."
kristen says, "i started to make a fractal program last week."
kristen says, "i got it to draw a fractal, using like EIGHT local variables that i passed as arguments."
kristen says, "but i can't get it to let me see it as it draws. it waits until it is done."
You say, "kristen: I take it you based this on the double-buffering program with a thread to do the painting in, right?"
kristen says, "yes."
kristen says, "i am sure it has to do with when i repaint() and all that, but the thing goes in circles and i started getting confused."
kristen says, "i had to do the drawLine thing in a recursive-loop method."
kristen says, "it's a simple looking program but i want to see the fractal as it draws."
You say, "kristen: hmmm. That's an interesting problem."
You say, "you could call repaint() at every pass through your method, but your method is recursive, so that would be a lot of repaint() calls."
kristen says, "that's what i thought, i mean, even if it takes forever, but i must have missed something because i couldn't force it to work."
You say, "kristen: and although java only actually calls update() 'when convenient' after a repaint() or even many repaint() calls, I still think that would be an awful lot of them."
kristen says, "well it only goes down 11 times, depending on how far i tell it."
kristen says, "i have it max out after 11 loops."
You say, "kristen: and this is taking a lot of time, but calls to repaint() nonetheless have no effect until it's all done?"
kristen says, "that's what it looks like."
You say, "wow, that slowed Netscape to a crawl. Peeking at the source..."
You say, "hmmm. Interesting."
kristen says, "hee."
kristen says, "Interesting Weird or Interesting Interesting?"
You say, "kristen: repaint 'schedules' actual painting when java considers it convenient. What you're doing is probably considered 'really inconvenient for a really long time.'"
You say, "kristen: there is a way around this."
kristen says, "oh. ah."
kristen says, "half the fun of watching fractals is watching them grow."
You say, "kristen: first, definitely take that every-time repaint call out, it's dogging my machine. Hee."
kristen says, "okay."
You say, "kristen: second, one approach would be to draw everything *both* to the real screen and the offscreen image, the latter just for refreshing purposes..."
You say, "kristen: to do that trick, you call getGraphics on the applet itself to get a graphics context that lets you draw right now on the real screen."
You say, "kristen: this way you don't have to call repaint() at all; you draw everything to the real screen and your private copy all the time, and paint() only gets invoked by the system when the window is obscured or what have you."
kristen says, "hm. okay."
kristen tries to fathom 'call getGraphics on the applet itself'.
You say, "kristen: ie, just do Graphics g = getGraphics();"
You say, "kristen: which is, of course, Graphics g = this.getGraphics();, which works because Applet is a descendant of Component."
kristen says, "so i should draw to the offscreen AND to the real screen every time i draw at all?"
You say, "kristen: yes. If you really want to watch the squiggle, and yet still not have to worry about starting over when the applet is obscured."
kristen nods.
kristen wants to watch the squiggle.
You say, "kristen: anything else would involve limited squiggling, and-or damaged displays when the window is obscured."
Tom decides to close his log for the week.
Next | Up to the Index

Follow us on Twitter | Contact Us

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