| Log In | Not a Member? |
Contact ADC
|
|
![]() |
|
|
| Previous Section | Table of Contents | Next Section |
Module 5- Custom Media Controllers in QTJ Part 2 Creating a Custom Button Class QuickTime has two built-in button classes that can be used for custom controllers - Our custom controller is fairly modal. Each button is aware of the overall state of the controller. For example, when the play button is the active button, it has a little blue indicator on it. QuickTime provides us with three built-in button classes that are all derrived from the QTButton class. These three classes, PressActionButton, PressReleaseButton, and ReleaseButton all behave in a slightly different manner. The PressActionButton fires an event when it is first pressed and continues to fire an event until it is released. This is great for a button that has a periodic action such as a scroll bar arrow. The scroll bar continues to scroll as long as you hold down the button (or until the end of the bar is reached). The PressReleaseButton fires an event when it is pressed and also when it is released. This type of button is good to represent a toggle button. For example, if you had a mute button that muted audio as long as it was held down, you would use this button class. The audio state would be toggled when the button was pressed (from on to off) and then toggled back when the button was released. The last class, the ReleaseButton only sends an event when the button is released. This is the most general purpose button class and is the type we will be using for our controller. When you create a ReleaseButton, you specify the default image, the pressed image, and the deactive image. You may also specify an additional rollover image, if you wish. When you specify these images, the button class automatically handles responding to the user input and swapping the images based on the user interaction. This is great for most button types, but is not very good for our custom button which has three states: Active, Pressed, and Inactive. The ReleaseButton deactiveImage parameter is designed to be used when the entire window is deactivated. This is not the correct behaviour for our button. We need to be able to toggle both the play button and the stop button between the active and inactive states based on the status of the movie. In order to do this, we need to create a new class and derive it from ReleaseButton. Create a new java source file and name it MovieButton.java. The source for the file is the following:
After importing the appropriate classes, we declare a public class MovieButton that derives from ReleaseButton. Our constructor simply calls the constructor of the superclass and passes the appropriate parameters, and we override setCurrentImage( ) from the UIElement class. We override this method because it is protected, and we need to make it public so that we can change the default button image to our active or inactive state. The implementation simply calls our inherited method. Now that we have created our own button class that makes the methods we need accesible, it's time to use them:
This section of code modifications seems long, but is actually straightforward. Don't be alarmed! We will start at the bottom of the AnimalPane source file and declare the three button objects. These objects are instances of our own custom class, and we declare one for each of our buttons: play, stop, and rewind. Next, for each of the buttons, we perform three basic steps:
Finally, we add each button to the compositor. Now that our buttons are created, we need to "wire them" to our movie so that they work correctly when the user clicks on them. We will do this over the next several steps. Creating a Button Listener Class Our buttons fire an action event when they are pressed. In order to respond to these action events, we must create a ButtonListener class:
In order to handle controller related action events (QTActionEvents), we need to implement the QTActionListener interface. We create a public inner class called ButtonListener that implements this interface. As is customary with classes derrived from EventListener, we implement a single method called actionPerformed that takes a QTActionEvent parameter. Once we register this listener with the buttons, we will receive notification of any QTActionEvents that are broadcast from the MovieButton objects. We set up a try / catch block in the body of our action performed method
in anticipation of intercepting any exceptions that will be thrown in
this method. In our next step we will implement the actionPerformed method:
Next, we check to see if the source of the event is the playButton. If it is, we know that the user clicked on the play button in the controller. When the user clicks the play button, there are two possibilities. The first is that the movie is already playing. In this case, we don't need to do anything to the movie because it is the correct state. All we need to do is make sure that the button images are correct. The second possibility is that the movie is stopped. If this is the case, we need to start the movie and then update the button states. In order to do this correctly, we declare a conditional what checks to see if the rate of the md variable (the MoviePlayer) is 0. If the rate of the player is zero, the movie is stopped, and we need to start it by setting the rate to 1. If it is already playing, we can skip this step. Next, we make sure that the buttons are using the correct images. This means that we need to make the stop button no longer active, and activate the play button. To do so, we call our setCurrentImage( ) method with the image of the button in the non-active state. We also call setReleasedImage( ), a method of the ReleaseButton class that sets the image that will be displayed when the button is released, with this same image so that the button will be restored correctly when it is pressed. Similarly, we change the current and release image of the play button to the image of the button with the active blue highlight indicator. Next, we will add code to handle the stop and rewind buttons:
After checking to make sure that the source of the action event is the stop button, we check to see if the movie is playing. If it is, we stop the movie by setting the rate of the movie presenter to 0. Next we update the button images using the same methodology as we used for the play button. Finally, if the source was neither the play button, nor the stop button, it is the rewind button. We respond to the button press by rewinding the movie. In order to do this, we call setTime( ) from the movie presenter object with the parameter 0. Now that we have a method that handles QTAction events, we need to create our controller and register the action listener with each of the buttons. Registering the Action Listeners In order to make the buttons respond to user interaction, we need to register an action listener with each button:
While we are registering our listeners, it is a good time to add exception handling to our code. Several of the methods we used previously could throw exceptions, so it is a good time to put our code in a try / catch block. The actual action listener registration is very simple. First, we create a new instance of our ButtonListener class we created in the previous step and assign it to a temporary variable. Next, we register our ButtonListener object with each of our buttons by calling the addActionListener( ) method. Once we register our listener, when the user clicks on a button and releases it, the button sends its QTActionEvent to each QTActionListener that is registered with that button. This in turn will call the actionPerformed method of our ButtonListener which then checks the source of the action event and responds appropriately. Finally, we catch any specific exceptions that may be thrown. At the risk of sounding like a broken record, it is a good time to mention that your code should preform better error handling. So far, we have buttons that broadcast action events and a ButtonListener registered with each of these buttons that responds to the QTActionEvents. But this isn't quite enough. We need to let QuickTime know that our collection of buttons is a controller. In order for our buttons to work properly, we need to register a controller with QuickTime. To do this, we will use a QTMouseTargetController. This class allows us to specify an area or target that will receive events from QTJ in response to user interaction. These events occur on (but are not limited to) MouseEnter, MouseExit, MouseMove, and MouseDrag events. This controller can act as a container and have members added to it which will also receive notification of mouseEvents. This is convenient for us because we have a number of buttons that we can add to the controller and have them automatically respond appropriately. We used the QTButton subclasses because they had almost all the functionality that we needed, but it is not a requirement to use these objects.
Let's skip down to the very bottom of our source file where we will declare a data member object, buttonController, which is a QTMouseTargetController. Next we go back up to our addMovie( ) routine, just after the code we added for adding our buttons to the compositor. We create a new QTMouseTargetController instance and assign it to our buttonController data member, passing false as the constructor parameter. This specificies that we do not want to use wholespace mode. If we were to specify whole space, every single object in our compositor would act like it was a member of the controller. Our background image, and movie would be members of the controller and inherit the same behavior. This is useful if you want a general behavior that pertained to every object in our compositor, such as the ability to be dragged around. We don't want this behavior, however, so we specify false. This requires us to register objects with the controller that we want the controller to be associated with. Thus, the next three lines of code we add do exactly that. We call the addMember( ) method from the controller with each button object. Finally, we add the controller to the compositor using the addController( ) method. This associates the controller with the compositor and ensures that all mouse events that occur within the compositor will be passed on to the controller. So far, we have created three buttons, a MouseListener, and registered the listener with each of the buttons. Then, we created a QTMouseTargetController, added our buttons to it, and then added the controller to the compositor. But we aren't quite done yet. There's one critical step that we've missed. We have to register each of the buttons with the controller so that they can receive events from the controller. Registering the Buttons with the Controller You may be thinking that things are really starting to get confusing. The following animation illustrates how all this works: |
|
The animation above is slightly over-simplified in order to get the point across. The most important thing to understand is that all user interaction is first handled by the QTCanvas which then passes the message off to any registered controllers which in our case is the QTMouseTarget controller. In order to get the message from the controller to our button class, we need to use a ButtonActivator class:
The ButtonActivator class is an intermediary between the QTMouseTargetController and our button class. It processes the events in the target controller and sends them to the button class which we are listening to with our ButtonListener object. In the source above, we add a protected ButtonActivator data member, and in the code body, create a new instance of that class. Let's now go look at where this ButtonActivator object is used. You may recall that we created a new class called ZooPane and derrived AnimalPane from that class. ZooPane had a start( ) and a stop( ) method that we must override in AnimalPane. These methods will tie our buttons into the event notification mechanism of the controller. Let's look first at start( ):
The start method is responsible for a number of things. Primarily, it adds the buttonActivator object to the buttonController as a registered mouse listener. Secondly, it makes sure that the controller is in the correct initial state and that the movie is playing,. After declaring our start( ) method, we register the button activator with the controller by calling addQTMouseListener( ). Then, within a try / catch block, we set up the initial button images so that the stop button is not active, and play button is active. In a second try block, we set the rate of the moviePresenter to one so that it starts playing immediately. We also move the compositor.getTimer( ).setRate( 1 ) routine from addMovie to the end of the start( ) function. While we are at it, we declare our stop method which we will implement in the next step. The stop method is designed to be called after we are finished with the movie controller and want to disassociate the buttons with the controller.
The stop method is very similar to the start method, except that we are not concerned with making sure that the button is in the correct visible state. Since this method is designed to be used for cleaning things (for example when we have been removed from the compositor), we only care about stopping the movie from playing and unregistering our listeners. We use the removeQTMouseListener( ) routine to unregister the button activator object with the controller. Then we change the rate of the compositor to stop it. Finally, we have a try / catch block that we need for stopping playback of the movie presenter. This completes the source modifications for this file. We did a lot of work to get a custom controller, but the results will be worth it. We created a subclass of ReleaseButton and then a QTMouseListener subclass that we registered with each of our buttons. Then we created a QTMouseTargetController, added it to the compositor, and added our buttons to it. We then created a ButtonActivator object and registered that with the controller in the start( ) method. Finally, we added a stop( ) method that we can call when we do cleanup. Now we have a little more modifications to do in Zoo5.java before we are finished. Final Modifications to the Zoo Class
In our main Zoo file, we add calls to the start( ) and stop( ) methods in our AnimalPane. We add the start method right after we create the AnimalPane object and set the client of the canvas. The stop( ) routine call is added in the windowClosing( ) method and will be called when the user clicks the close box of the window. This completes the source code modifications to module 5. It's time to run your application and see the results. If you would like to see the entire source file of either Zoo5.java, ZooPane.java, AnimalPane.java, or MovieButton.java, please follow the appropriate links. This module demonstated how to create and use custom QuickTime controllers for the presentation of an alternate user interface for time-based media interaction. We learned how to use QuickTime for Java's built-in button classes to create user interface elements that mimic traditional push-button controls. We extended the functionality of the button class to provide multiple state support, and we learned how to events are propogated from the compositor via the event listener mechanism. The clever reader will realize quickly that the information that was presented here only scratches the surface of what could be done using QuickTime for Java. Although we will expand this example in future modules, there are few things that you could do on your own as exploration that will help you better understand how QuickTime for Java works. Each one of these examples has a source code solution, so if you have trouble, feel free to consult it. Experiment with one or more of the following:
|
| Previous Section | Table of Contents | Next Section |