HOMEWORK 5: More Animation and Physics

This is an INDIVIDUAL assignment.

Objective

In this final assignment, we'll focus on the content area, adding an at-a-glance view of all pages, with animated transitions to this view.

The learning goals for this assignment are:

Description

In this homework we'll implement a nifty feature that allows the user to browse through pages in ways beyond simply the "next" and "previous" buttons.

We will provide a way for the user to zoom out of the current page in the content area, to see all the pages in the journal, and select one to zoom back in to it. You can see the zoom out effect in the Courier video here at around 1:15, where the user spreads out the pages with photos through a reverse-pinch gesture.

Page Overview mode

You'll need to add a new button to the application to kick you into Overview Mode. When the user clicks this button the content area is animated so that the user is presented with an overview of all of the current pages in the journal. The page overview should present the user with shrunken thumbnail representations of all of the pages, arrayed in a grid. The pages should be scaled appropriately so that they roughly fill the overview, no matter how many pages the user has (so, if there are lots of pages, you will have really small thumbnails).

When the user clicks one of these pages, a reverse animation should be done that zooms you in to the selected page, which then works normally again.

Basic Architecture

There are a lot of possible ways to implement this assignment. Here's one way that seems fairly straightforward to me; let me know if you find a different/better way to do it.

Start by creating a new component that will act as a container (parent) for all of the individual page components in your application, by subclassing JComponent. This container would be integrated into your Courier component hierarchy as the parent for your page components, so you'll have to tweak your component hierarchy a bit. Now, whenever a Page component is created by your application, it should be added as a child to this container, via the add() method that is already built in to containers (this method adds the new child component to a list of children, which you can access via getChildren()).

Once you've done this, you should first make sure that this change doesn't break anything in your application. It should still display the child page correctly, handle resize correctly, and doing next/prev/delete should all still work.

Hint: When you create this component, in the "normal" mode you'll only want to show one child at a time, the page that is selected as the current page. An easy way to do this is to add a variable to the container that has the index of the current page, and then override paintChildren() (the method that containers normally use to recursively draw their children) so that it only paints whatever the current page is, or makes the other pages invisible by calling setVisible(false). (This is similar to the way CardLayout only shows one child at a time.)

Hint: There are a couple of ways to ensure that the currently displayed page component is the right size, i.e., that it's the same size as the container. First is to use a LayoutManager, such as BorderLayout with the current page component as the Center. But another way is to just use no layout manager (call setLayout(null) on the container) and then manually set the size of the child to be the same as the size of the container, and its location within the container to be (0,0). You'd need to detect when the container is resized so that you can resize the child, of course. Hint: Having repaint problems? Are your changes not getting reflected on screen? Be sure to call revalidate() anytime the set of children changes, or your size changes.

Once you've got the current functionality working with the new container in place, the next step is to create the animation for the activation of Overview Mode. When Overview Mode is activated, the callback for the Overview button should start an animation (via javax.swing.Timer) that cycle through an animation that zooms out, rendering all of the pages as they shrink and move into their final positions in the grid. It does this by first setting an "overview mode" flag in the container, updating whatever variables need to be updated as you cycle through the animation (such as the current zoom factor for the page thumbnails at this stage in the animation), and then calling repaint() so that Swing will cause your application to be painted for this current cycle of the animation.

Just like in the page animation homework previously, the actual rendering happens inside the container's paintComponent() method. Override the container's paintComponent() method so that, if the "overview mode" flag is set, it draws the current stage in the animation. To render the thumbnails, I'd suggest creating a Graphics2D object that's scaled with the current value (you can call scale() on the Graphics2D object), and then iterate over each of the child page components, passing the Graphics2D object to their paint() method to have them each draw themselves. In other words, you're using your page components' existing drawing code to create the animation effect, by calling it "manually" from within your container.

Hint: When you're not in overview mode, the container's paintComponent() method would likely just draw nothing (i.e., just return) since paintChildren() is already taking care of drawing the currently displayed page.

Hint: When you are in overview mode, you don't want the container's paintChildren() method to draw the current page, because all of the page thumbnails are already being rendered by the container's paintComponent() method. So, in paintChildren(), you should check to see if you're in "overview mode" and, if so, not draw any children.

Finally, the last step is to get out of overview mode and return to the normal page view. Listen for mouse clicks in the container to figure out when a click is made; translate that to the bounds of the shrunken pages in the grid, and then start the animation to bring the selected page to the foreground. After this zoom-out transition, you can go back to the "default" mode where you just show the one current page. Note that while in the zoomed out view, page components don't need to be responsive to drawing or text input or any of the normal things they do when they're filling the screen.

Extra Credit

As usual, there are a lot of ways you might make this assignment much fancier than described:

Deliverable

See here for instructions on how to submit your homework. These instructions will be the same for each assignment.