If you have completed the tutorial so far, you're done. You need not go any further--unless you would like to try a more interesting and challenging variation of the TempImageView class you created earlier. In this "extra credit" part of the tutorial, you will create a class for objects that know how to draw themselves and know how to respond to user events. These classes are custom subclasses of NSView.
Custom subclasses of NSView are ususally constructed differently than subclasses of other Application Kit classes because the custom NSView subclass is responsible for drawing itself and, optionally, for responding to user actions. Of course, you can do custom drawing in a subclass that doesn't inherit directly from NSView, but usually instances of these classes draw themselves adequately. This section describes in general terms what you must do to create a custom NSView subclass.
The differences are slight between the Interface Builder procedures for defining a direct subclass of NSView and for defining a subclass of any Application Kit class that inherits, directly or indirectly, from NSView. The following is a summary of the required procedure in Interface Builder:
Do this by selecting the object and selecting the class in the Classes display of the inspector.
If you are unsure how to complete any of these steps, refer to the Defining the Custom View Subclass and Connecting the View Object sections of this tutorial.
You can implement your custom NSView to do one or two general things: to draw itself and to respond to user actions. The basic procedures for these and related tasks are given below.
The information provided in this section barely scratches the surface of the concepts related to NSView, including drawing, the imaging model, event handling, the view hierarchy, and so on. This section intends only to give you an idea of what is involved in creating a custom view. For a much more complete picture, see the description of the NSView class in the Objective-C API reference.
All objects that inherit from NSView must override the
drawRect
method to render themselves on the screen. The invocation of NSView's
display
method, or one of the
display
variants, leads to the invocation of
drawRect
. Before
drawRect
is invoked, NSView "locks focus," setting the Window Server up with information about the view, including the window device it draws in, the coordinate system and clipping path it uses, and other PostScript graphics state information.
In the
drawRect
method, you must write the code that transmits drawing instructions to the Window Server. The
drawRect
method has one argument: the NSRect object defining the area in which the drawing is to occur (usually the bounds of the NSView itself or a subrectangle of it). The range of options the Java Yellow Box APIs provide is currently more limited than on the Objective-C side, which has the whole suite of PostScript client-side functions and operators. For drawing in Java, you can use the following classes:
With each cycle of the event loop, the Window Server ensures that each NSView in a window that requires redrawing is given an opportunity to redisplay itself. Besides implementing
drawRect
to draw your custom NSView, your application must indicate that an NSView requires redrawing when data affecting the view changes.
This indication is called "invalidation." Invalidation marks an entire view or a portion of a view as "invalid," and thus requiring a redisplay. NSView defines two methods for marking a view's image as invalid:
setNeedsDisplay
, which invalidates the view's entire bounds rectangle, and
setNeedsDisplayInRect
, which invalidates a portion of the view.
You can also force an immediate redisplay of a view with the
display
and
displayRect
methods, which are the counterparts to the methods mentioned above. However, you should use these and related
display
... methods sparingly, and only when necessary. Constant forced displays can markedly affect application performance.
If an NSView expresses a willingness to respond to user events, it is made the potential recipient of any event detected by the window system. The view then just must implement the appropriate NSResponder method (or methods) that correspond to the event the view is interested in. (NSView inherits from NSResponder.)
What this means in practical terms is that an NSView must at a bare minimum do two things:
acceptsFirstResponder
method to return
true
.
mouseDown
,
mouseDragged
, or
keyUp
. The argument of each of these methods is an NSEvent, which provides information related to the event.See the NSResponder and NSEvent class descriptions in the API reference for further information.
The TemperatureView class is similar to the TempImageView class implemented in the second part of the tutorial. Instead of displaying a different image when the temperature changes to a certain range, it draws a circle of a different color. To illustrate basic event handling, the TemperatureView class changes the thickness of the view's border each time the user clicks the view.
/* TemperatureView */ import com.apple.yellow.application.*; import com.apple.yellow.foundation.*; public class TemperatureView extends NSView { protected NSBezierPath sun; protected int temperature; protected int thickness; static public final int SpringSun=0; static public final int SummerSun=1; static public final int WinterSun=2; public TemperatureView(NSRect frame) { super(frame); float shortest = frame.width() >= frame.height()?frame.height():frame.width(); NSRect rect; NSColor color; shortest *= 0.75; rect = new NSRect(((frame.width() - shortest) /2), ((frame.height() - shortest) /2), shortest, shortest); sun = NSBezierPath.bezierPathWithOvalInRect(rect); thickness = 1; } public void drawRect(NSRect frame) { NSColor color; if (temperature == WinterSun) { color = NSColor.lightGrayColor(); } else if (temperature == SummerSun) { color = NSColor.orangeColor(); } else { color = NSColor.yellowColor(); } color.set(); sun.fill(); NSGraphics.frameRectWithWidth(frame, (float)thickness); } public void tempDidChange(int degree) { if (degree < 45) { temperature = WinterSun; } else if (degree > 75) { temperature = SummerSun; } else temperature = SpringSun; setNeedsDisplay(true); } public void mouseDown(NSEvent e) { if (thickness == 3) { thickness = 1; } else { thickness++; } setNeedsDisplay(true); } public boolean acceptsFirstResponder() { return true; } }
Previous | Next
© 1998 Apple Computer, Inc.