am ea502b27: Merge "docs: animations au class" into jb-dev-docs

* commit 'ea502b27020872003f1d2be1fb2644b04cc59a67':
  docs: animations au class
This commit is contained in:
Robert Ly
2012-10-25 16:26:57 -07:00
committed by Android Git Automerger
23 changed files with 1269 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,365 @@
page.title=Displaying Card Flip Animations
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>
This lesson teaches you to
</h2>
<ol>
<li>
<a href="#animators">Create the Animators</a>
</li>
<li>
<a href="#views">Create the Views</a>
</li>
<li>
<a href="#fragment">Create the Fragment</a>
</li>
<li>
<a href="#animate">Animate the Card Flip</a>
</li>
</ol>
</div>
</div>
<p> This lesson shows you how to do a card flip
animation with custom fragment animations.
Card flips animate between views of content by showing an animation that emulates
a card flipping over.
</p>
<p>Here's what a card flip looks like:
</p>
<div class="framed-galaxynexus-land-span-8">
<video class="play-on-hover" autoplay>
<source src="anim_card_flip.mp4" type="video/mp4">
<source src="anim_card_flip.webm" type="video/webm">
<source src="anim_card_flip.ogv" type="video/ogg">
</video>
</div>
<div class="figure-caption">
Card flip animation
<div class="video-instructions">&nbsp;</div>
</div>
<p>
If you want to jump ahead and see a full working example,
<a href="{@docRoot}shareables/training/Animations.zip">download</a> and
run the sample app and select the Card Flip example. See the following
files for the code implementation:
</p>
<ul>
<li>
<code>src/CardFlipActivity.java</code>
</li>
<li>
<code>animator/card_flip_right_in.xml</code>
</li>
<li>
<code>animator/card_flip_right_out.xml</code>
</li>
<li>
<code>animator/card_flip_right_in.xml</code>
</li>
<li>
<code>animator/card_flip_left_out.xml</code>
</li>
<li>
<code>layout/fragment_card_back.xml</code>
</li>
<li>
<code>layout/fragment_card_front.xml</code>
</li>
</ul>
<h2 id="animate">
Create the Animators
</h2>
<p>
Create the animations for the card flips. You'll need two animators for when the front
of the card animates out and to the left and in and from the left. You'll also need two animators
for when the back of the card animates in and from the right and out and to the right.
</p>
<h4>
card_flip_left_in.xml
</h4>
<pre>
&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
&lt;!-- Before rotating, immediately set the alpha to 0. --&gt;
&lt;objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" /&gt;
&lt;!-- Rotate. --&gt;
&lt;objectAnimator
android:valueFrom="-180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" /&gt;
&lt;!-- Half-way through the rotation (see startOffset), set the alpha to 1. --&gt;
&lt;objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" /&gt;
&lt;/set&gt;
</pre>
<h4>
card_flip_left_out.xml
</h4>
<pre>
&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
&lt;!-- Rotate. --&gt;
&lt;objectAnimator
android:valueFrom="0"
android:valueTo="180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" /&gt;
&lt;!-- Half-way through the rotation (see startOffset), set the alpha to 0. --&gt;
&lt;objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" /&gt;
&lt;/set&gt;
</pre>
<h4>
card_flip_right_in.xml
</h4>
<pre>
&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
&lt;!-- Before rotating, immediately set the alpha to 0. --&gt;
&lt;objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:duration="0" /&gt;
&lt;!-- Rotate. --&gt;
&lt;objectAnimator
android:valueFrom="180"
android:valueTo="0"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" /&gt;
&lt;!-- Half-way through the rotation (see startOffset), set the alpha to 1. --&gt;
&lt;objectAnimator
android:valueFrom="0.0"
android:valueTo="1.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" /&gt;
</set>
</pre>
<h4>
card_flip_right_out.xml
</h4>
<pre>
&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
&lt;!-- Rotate. --&gt;
&lt;objectAnimator
android:valueFrom="0"
android:valueTo="-180"
android:propertyName="rotationY"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:duration="@integer/card_flip_time_full" /&gt;
&lt;!-- Half-way through the rotation (see startOffset), set the alpha to 0. --&gt;
&lt;objectAnimator
android:valueFrom="1.0"
android:valueTo="0.0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:duration="1" /&gt;
&lt;/set&gt;
</pre>
<h2 id="views">
Create the Views
</h2>
<p>
Each side of the "card" is a separate layout that can contain any content you want,
such as two screens of text, two images, or any combination of views to flip between. You'll then
use the two layouts in the fragments that you'll later animate. The following layouts
create one side of a card that shows text:
</p>
<pre>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#a6c"
android:padding="16dp"
android:gravity="bottom"&gt;
&lt;TextView android:id="@android:id/text1"
style="?android:textAppearanceLarge"
android:textStyle="bold"
android:textColor="#fff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_title" /&gt;
&lt;TextView style="?android:textAppearanceSmall"
android:textAllCaps="true"
android:textColor="#80ffffff"
android:textStyle="bold"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/card_back_description" /&gt;
&lt;/LinearLayout&gt;
</pre>
<p>
and the other side of the card that displays an {@link android.widget.ImageView}:
</p>
<pre>
&lt;ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/image1"
android:scaleType="centerCrop"
android:contentDescription="@string/description_image_1" /&gt;
</pre>
<h2 id="fragment">
Create the Fragment
</h2>
<p>
Create fragment classes for the front and back of the card. These classes return the layouts
that you created previously in the {@link android.app.Fragment#onCreateView onCreateView()} method
of each fragment. You can then create instances of this fragment in the parent activity
where you want to show the card. The following example shows nested fragment classes inside
of the parent activity that uses them:
</p>
<pre>
public class CardFlipActivity extends Activity {
...
/**
* A fragment representing the front of the card.
*/
public class CardFrontFragment extends Fragment {
&#64;Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_card_front, container, false);
}
}
/**
* A fragment representing the back of the card.
*/
public class CardBackFragment extends Fragment {
&#64;Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_card_back, container, false);
}
}
}
</pre>
<h2 id="animate">
Animate the Card Flip
</h2>
<p> Now, you'll need to display the fragments inside of a parent activity.
To do this, first create the layout for your activity. The following example creates a
{@link android.widget.FrameLayout} that you
can add fragments to at runtime:</p>
<pre>
&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" /&gt;
</pre>
<p>In the activity code, set the content view to be the layout that you just created. It's also
good idea to show a default fragment when the activity is created, so the following example
activity shows you how to display the front of the card by default:
</p>
<pre>
public class CardFlipActivity extends Activity {
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activity_card_flip);
if (savedInstanceState == null) {
getFragmentManager()
.beginTransaction()
.add(R.id.container, new CardFrontFragment())
.commit();
}
}
...
}
</pre>
<p>
Now that you have the front of the card showing, you can show the back of the card
with the flip animation at an appropriate time. Create a method to show the other
side of the card that does the following things:
</p>
<ul>
<li>Sets the custom animations that you created earlier for the fragment transitions.
</li>
<li>Replaces the currently displayed fragment with a new fragment and animates this event
with the custom animations that you created.
</li>
<li>Adds the previously displayed fragment to the fragment back stack
so when the user presses the <em>Back</em> button, the card flips back over.
</li>
</ul>
<pre>
private void flipCard() {
if (mShowingBack) {
getFragmentManager().popBackStack();
return;
}
// Flip to the back.
mShowingBack = true;
// Create and commit a new fragment transaction that adds the fragment for the back of
// the card, uses custom animations, and is part of the fragment manager's back stack.
getFragmentManager()
.beginTransaction()
// Replace the default fragment animations with animator resources representing
// rotations when switching to the back of the card, as well as animator
// resources representing rotations when flipping back to the front (e.g. when
// the system Back button is pressed).
.setCustomAnimations(
R.animator.card_flip_right_in, R.animator.card_flip_right_out,
R.animator.card_flip_left_in, R.animator.card_flip_left_out)
// Replace any fragments currently in the container view with a fragment
// representing the next page (indicated by the just-incremented currentPage
// variable).
.replace(R.id.container, new CardBackFragment())
// Add this transaction to the back stack, allowing users to press Back
// to get to the front of the card.
.addToBackStack(null)
// Commit the transaction.
.commit();
}
</pre>

View File

@@ -0,0 +1,208 @@
page.title=Crossfading Two Views
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>
This lesson teaches you to:
</h2>
<ol>
<li>
<a href="#views">Create the Views</a>
</li>
<li>
<a href="#setup">Set up the Animation</a>
</li>
<li>
<a href="#animate">Crossfade the Views</a>
</li>
</ol>
</div>
</div>
<p>
Crossfade animations (also know as dissolve) gradually fade out one UI component while simultaneously fading in
another. This animation is useful for situations where you want to switch content or views
in your app. Crossfades are very subtle and short but offer a fluid transition from one screen to the
next. When you don't use them, however, transitions often feel abrupt or hurried.
</p>
<p>Here's an example of a crossfade from a progress indicator to some text content.
</p>
<div class="framed-galaxynexus-land-span-8">
<video class="play-on-hover" autoplay>
<source src="anim_crossfade.mp4" type="video/mp4">
<source src="anim_crossfade.webm" type="video/webm">
<source src="anim_crossfade.ogv" type="video/ogg">
</video>
</div>
<div class="figure-caption">
Crossfade animation
<div class="video-instructions">&nbsp;</div>
</div>
<p>
If you want to jump ahead and see a full working example,
<a href="{@docRoot}shareables/training/Animations.zip">download</a>
and run the sample app and select the Crossfade example.
See the following files for the code implementation:
</p>
<ul>
<li>
<code>src/CrossfadeActivity.java</code>
</li>
<li>
<code>layout/activity_crossfade.xml</code>
</li>
<li>
<code>menu/activity_crossfade.xml</code>
</li>
</ul>
<h2 id="views">
Create the Views
</h2>
<p>
Create the two views that you want to crossfade. The following example creates a progress
indicator and a scrollable text view:
</p>
<pre>
&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"&gt;
&lt;ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"&gt;
&lt;TextView style="?android:textAppearanceMedium"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lorem_ipsum"
android:padding="16dp" /&gt;
&lt;/ScrollView&gt;
&lt;ProgressBar android:id="@+id/loading_spinner"
style="?android:progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" /&gt;
&lt;/FrameLayout&gt;
</pre>
<h2 id="setup">
Set up the Animation
</h2>
<p>
To set up the animation:
</p>
<ol>
<li>Create member variables for the views that you want to crossfade. You need
these references later when modifying the views during the animation.
</li>
<li>For the view that is being faded in, set its visibility to {@link
android.view.View#GONE}. This prevents the view from taking up layout space and omits it
from layout calculations, speeding up processing.
</li>
<li>Cache the <code>{@link android.R.integer#config_shortAnimTime}</code>
system property in a member variable. This property defines a standard
"short" duration for the animation. This duration is ideal for subtle animations or
animations that occur very frequently. {@link android.R.integer#config_longAnimTime} and
{@link android.R.integer#config_mediumAnimTime} are also available if you wish to use them.
</li>
</ol>
<p>
Here's an example using the layout from the previous code snippet as the activity content
view:
</p>
<pre>
public class CrossfadeActivity extends Activity {
private View mContentView;
private View mLoadingView;
private int mShortAnimationDuration;
...
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crossfade);
mContentView = findViewById(R.id.content);
mLoadingView = findViewById(R.id.loading_spinner);
// Initially hide the content view.
mContentView.setVisibility(View.GONE);
// Retrieve and cache the system's default "short" animation time.
mShortAnimationDuration = getResources().getInteger(
android.R.integer.config_shortAnimTime);
}
</pre>
<h2 id="animate">
Crossfade the Views
</h2>
<p>
Now that the views are properly set up, crossfade them by doing the following:
</p>
<ol>
<li>For the view that is fading in, set the alpha value to <code>0</code> and the visibility
to {@link android.view.View#VISIBLE}. (Remember that it was initially set to {@link
android.view.View#GONE}.) This makes the view visible but completely transparent.
</li>
<li>For the view that is fading in, animate its alpha value from <code>0</code> to
<code>1</code>. At the same time, for the view that is fading out, animate the alpha value
from <code>1</code> to <code>0</code>.
</li>
<li>Using {@link android.animation.Animator.AnimatorListener#onAnimationEnd onAnimationEnd()}
in an {@link android.animation.Animator.AnimatorListener}, set the visibility of the view
that was fading out to {@link android.view.View#GONE}. Even though the alpha value is <code>0</code>,
setting the view's visibility to {@link android.view.View#GONE} prevents the view from taking
up layout space and omits it from layout calculations, speeding up processing.
</li>
</ol>
<p>
The following method shows an example of how to do this:
</p>
<pre>
private View mContentView;
private View mLoadingView;
private int mShortAnimationDuration;
...
private void crossfade() {
// Set the content view to 0% opacity but visible, so that it is visible
// (but fully transparent) during the animation.
mContentView.setAlpha(0f);
mContentView.setVisibility(View.VISIBLE);
// Animate the content view to 100% opacity, and clear any animation
// listener set on the view.
mContentView.animate()
.alpha(1f)
.setDuration(mShortAnimationDuration)
.setListener(null);
// Animate the loading view to 0% opacity. After the animation ends,
// set its visibility to GONE as an optimization step (it won't
// participate in layout passes, etc.)
mHideView.animate()
.alpha(0f)
.setDuration(mShortAnimationDuration)
.setListener(new AnimatorListenerAdapter() {
&#64;Override
public void onAnimationEnd(Animator animation) {
mHideView.setVisibility(View.GONE);
}
});
}
</pre>

View File

@@ -0,0 +1,86 @@
page.title=Adding Animations
trainingnavtop=true
startpage=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>
Dependencies and prerequisites
</h2>
<ul>
<li>Android 4.0 or later
</li>
<li>Experience building an Android <a href="{@docRoot}guide/topics/ui/index.html">User
Interface</a>
</li>
</ul>
<h2>
You should also read
</h2>
<ul>
<li>
<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>
</li>
</ul>
<h2>
Try it out
</h2>
<div class="download-box">
<a href="{@docRoot}shareables/training/Animations.zip" class=
"button">Download the sample app</a>
<p class="filename">
Animations.zip
</p>
</div>
</div>
</div>
<p>
Animations can add subtle visual cues that notify users about what's going on in your app and
improve their mental model of your app's interface. Animations are especially useful when the
screen changes state, such as when content loads or new actions become available. Animations
can also add a polished look to your app, which gives your app a higher quality feel.
</p>
<p>
Keep in mind though, that overusing animations or using them at the wrong time can be
detrimental, such as when they cause delays. This training class shows you how to
implement some common types of animations that can increase usability and add flair without
annoying your users.
</p>
<h2>
Lessons
</h2>
<dl>
<dt>
<b><a href="crossfade.html">Crossfading Two Views</a></b>
</dt>
<dd>
Learn how to crossfade between two overlapping views. This lesson shows you how to crossfade a progress
indicator to a view that contains text content.
</dd>
<dt>
<b><a href="screen-slide.html">Using ViewPager for Screen Slides</a></b>
</dt>
<dd>
Learn how to animate between horizontally adjacent screens with a sliding transition.
</dd>
<dt>
<b><a href="cardflip.html">Displaying Card Flip Animations</a></b>
</dt>
<dd>
Learn how to animate between two views with a flipping motion.
</dd>
<dt>
<b><a href="zoom.html">Zooming a View</a></b>
</dt>
<dd>
Learn how to enlarge views with a touch-to-zoom animation.
</dd>
<dt>
<b><a href="layout.html">Animating Layout Changes</a></b>
</dt>
<dd>
Learn how to enable built-in animations when adding, removing, or updating child views in a layout.
</dd>
</dl>

View File

@@ -0,0 +1,77 @@
page.title=Animating Layout Changes
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to:</h2>
<ol>
<li><a href="#views">Create the Layout</a></li>
<li><a href="#add">Add, Update, or Remove Items from the Layout</a></li>
</ol>
</div>
</div>
<p>A layout animation is a pre-loaded animation that the system runs each time you make a change
to the layout configuration. All you need to do is set an attribute in the layout to tell the
Android system to animate these layout changes, and system-default animations are carried out for you.
</p>
<p class="note"><strong>Tip</strong>: If you want to supply custom layout animations,
create a {@link android.animation.LayoutTransition} object and supply it to
the layout with the {@link android.view.ViewGroup#setLayoutTransition setLayoutTransition()}
method.
</p>
Here's what a default layout animation looks like when adding items to a list:
</p>
<div class="framed-galaxynexus-land-span-8">
<video class="play-on-hover" autoplay>
<source src="anim_layout_changes.mp4" type="video/mp4">
<source src="anim_layout_changes.webm" type="video/webm">
<source src="anim_layout_changes.ogv" type="video/ogg">
</video>
</div>
<div class="figure-caption">
Layout animation
<div class="video-instructions">&nbsp;</div>
</div>
<p>If you want to jump ahead and see a full working example,
<a href="{@docRoot}shareables/training/Animations.zip">download</a> and
run the sample app and select the Crossfade example. See the following files for the
code implementation:</p>
<ol>
<li><code>src/LayoutChangesActivity.java</code></li>
<li><code>layout/activity_layout_changes.xml</code></li>
<li><code>menu/activity_layout_changes.xml</code></li>
</ol>
<h2 id="views">Create the Layout</h2>
<p>In your activity's layout XML file, set the <code>android:animateLayoutChanges</code>
attribute to <code>true</code> for the layout that you want to enable animations for.
For instance:</p>
<pre>
&lt;LinearLayout android:id="@+id/container"
android:animateLayoutChanges="true"
...
/&gt;
</pre>
<h2 id="activity">Add, Update, or Remove Items from the Layout</h2>
<p>
Now, all you need to do is add, remove, or update items in the layout
and the items are animated automatically:
</p>
<pre>
private ViewGroup mContainerView;
...
private void addItem() {
View newView;
...
mContainerView.addView(newView, 0);
}
</pre>

View File

@@ -0,0 +1,185 @@
page.title=Using ViewPager for Screen Slides
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#views">Create the Views</a></li>
<li><a href="#fragment">Create the Fragment</a></li>
<li><a href="#viewpager">Animate the Screen Slide</a></li>
</ol>
</div>
</div>
<p>
Screen slides are transitions between one entire screen to another and are common with UIs
like setup wizards or slideshows. This lesson shows you how to do screen slides with
a {@link android.support.v4.view.ViewPager} provided by the <a href=
"{@docRoot}/tools/extras/support-library.html">support library</a>.
{@link android.support.v4.view.ViewPager}s can animate screen slides
automatically. Here's what a screen slide looks like that transitions from
one screen of content to the next:
</p>
<div class="framed-galaxynexus-land-span-8">
<video class="play-on-hover" autoplay>
<source src="anim_screenslide.mp4" type="video/mp4">
<source src="anim_screenslide.webm" type="video/webm">
<source src="anim_screenslide.ogv" type="video/ogg">
</video>
</div>
<div class="figure-caption">
Screen slide animation
<div class="video-instructions">&nbsp;</div>
</div>
<p>If you want to jump ahead and see a full working example,
<a href="{@docRoot}shareables/training/Animations.zip">download</a>
and run the sample app and select the Screen Slide example. See the
following files for the code implementation:</p>
<ul>
<li><code>src/ScreenSlidePageFragment.java</code></li>
<li><code>src/ScreenSlideActivity.java</code></li>
<li><code>layout/activity_screen_slide.xml</code></li>
<li><code>layout/fragment_screen_slide_page.xml</code></li>
</ul>
<h2 id="views">Create the Views</h2>
<p>Create a layout file that you'll later use for the content of a fragment. The following example
contains a text view to display some text:
<pre>
&lt;com.example.android.animationsdemo.ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"&gt;
&lt;TextView style="?android:textAppearanceMedium"
android:padding="16dp"
android:lineSpacingMultiplier="1.2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/lorem_ipsum" /&gt;
&lt;/com.example.android.animationsdemo.ScrollView&gt;
</pre>
<h2 id="fragment">Create the Fragment</h2>
<p>Create a {@link android.support.v4.app.Fragment} class that returns the layout
that you just created in the {@link android.app.Fragment#onCreateView onCreateView()}
method. You can then create instances of this fragment in the parent activity whenever you need a new page to
display to the user:</p>
<pre>
public class ScreenSlidePageFragment extends Fragment {
&#64;Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.fragment_screen_slide_page, container, false);
return rootView;
}
}
</pre>
<h2 id="viewpager">Screen Slides with ViewPager</h2>
<p>{@link android.support.v4.view.ViewPager}s have built-in swipe gestures to transition
through pages, and they display screen slide animations by default, so you don't need to create any. {@link android.support.v4.view.ViewPager}s use
{@link android.support.v4.view.PagerAdapter}s as a supply for new pages to display, so the {@link android.support.v4.view.PagerAdapter} will use the
fragment class that you created earlier.
</p>
<p>To begin, create a layout that contains a {@link android.support.v4.view.ViewPager}:</p>
<pre>
&lt;android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" /&gt;
</pre>
<p>Create an activity that does the following things:
</p>
<ul>
<li>Sets the content view to be the layout with the {@link android.support.v4.view.ViewPager}.</li>
<li>Create a class that extends the {@link android.support.v13.app.FragmentStatePagerAdapter} abstract class. Implement
the {@link android.support.v4.app.FragmentStatePagerAdapter#getItem getItem()} method to supply
instances of <code>ScreenSlidePageFragment</code> as new pages. The pager adapter also requires that you implement the
{@link android.support.v4.view.PagerAdapter#getCount getCount()} method, which returns the amount of pages the adapter will create (five in the example).
<li>Hooks up the {@link android.support.v4.view.PagerAdapter} to the {@link android.support.v4.view.ViewPager}</code>.</li>
<li>Handle's the device's back button by moving backwards in the virtual stack of fragments.
If the user is already on the first page, go back on the activity back stack.</li>
</ul>
<pre>
public class ScreenSlidePagerActivity extends FragmentActivity {
/**
* The number of pages (wizard steps) to show in this demo.
*/
private static final int NUM_PAGES = 5;
/**
* The pager widget, which handles animation and allows swiping horizontally to access previous
* and next wizard steps.
*/
private ViewPager mPager;
/**
* The pager adapter, which provides the pages to the view pager widget.
*/
private PagerAdapter mPagerAdapter;
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_slide_pager);
// Instantiate a ViewPager and a PagerAdapter.
mPager = (ViewPager) findViewById(R.id.pager);
mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager());
mPager.setAdapter(mPagerAdapter);
}
&#64;Override
public void onBackPressed() {
if (mPager.getCurrentItem() == 0) {
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
super.onBackPressed();
} else {
// Otherwise, select the previous step.
mPager.setCurrentItem(mPager.getCurrentItem() - 1);
}
}
/**
* A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
* sequence.
*/
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
&#64;Override
public Fragment getItem(int position) {
return new ScreenSlidePageFragment();
}
&#64;Override
public int getCount() {
return NUM_PAGES;
}
}
}
</pre>

View File

@@ -0,0 +1,320 @@
page.title=Zooming a View
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>
This lesson teaches you to:
</h2>
<ol>
<li>
<a href="#views">Create the Views</a>
</li>
<li>
<a href="#setup">Set up the Zoom Animation</a>
</li>
<li>
<a href="#animate">Zoom the View</a>
</li>
</ol>
</div>
</div>
<p>
This lesson demonstrates how to do a touch-to-zoom animation, which is useful for apps such as photo
galleries to animate a view from a thumbnail to a full-size image that fills the screen.
</p>
<p>Here's what a touch-to-zoom animation looks like that
expands an image thumbnail to fill the screen:
</p>
<div class="framed-galaxynexus-land-span-8">
<video class="play-on-hover" autoplay>
<source src="anim_zoom.mp4" type="video/mp4">
<source src="anim_zoom.webm" type="video/webm">
<source src="anim_zoom.ogv" type="video/ogg">
</video>
</div>
<div class="figure-caption">
Zoom animation
<div class="video-instructions">&nbsp;</div>
</div>
<p>
If you want to jump ahead and see a full working example,
<a href="{@docRoot}shareables/training/Animations.zip">download</a> and
run the sample app and select the
Zoom example. See the following files for the code implementation:
</p>
<ul>
<li>
<code>src/TouchHighlightImageButton.java</code> (a simple helper class that shows a blue
touch highlight when the image button is pressed)
</li>
<li>
<code>src/ZoomActivity.java</code>
</li>
<li>
<code>layout/activity_zoom.xml</code>
</li>
</ul>
<h2 id="views">
Create the Views
</h2>
<p>
Create a layout file that contains the small and large version of the content that you want
to zoom. The following example creates an {@link android.widget.ImageButton} for clickable image thumbnail
and an {@link android.widget.ImageView} that displays the enlarged view of the image:
</p>
<pre>
&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"&gt;
&lt;LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"&gt;
&lt;ImageButton
android:id="@+id/thumb_button_1"
android:layout_width="100dp"
android:layout_height="75dp"
android:layout_marginRight="1dp"
android:src="@drawable/thumb1"
android:scaleType="centerCrop"
android:contentDescription="@string/description_image_1" /&gt;
&lt;/LinearLayout&gt;
&lt;!-- This initially-hidden ImageView will hold the expanded/zoomed version of
the images above. Without transformations applied, it takes up the entire
screen. To achieve the "zoom" animation, this view's bounds are animated
from the bounds of the thumbnail button above, to its final laid-out
bounds.
--&gt;
&lt;ImageView
android:id="@+id/expanded_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
android:contentDescription="@string/description_zoom_touch_close" /&gt;
&lt;/FrameLayout&gt;
</pre>
<h2 id="setup">
Set up the Zoom Animation
</h2>
<p>
Once you apply your layout, set up the event handlers that trigger the zoom animation.
The following example adds a {@link android.view.View.OnClickListener} to the {@link
android.widget.ImageButton} to execute the zoom animation when the user
clicks the image button:
</p>
<pre>
public class ZoomActivity extends FragmentActivity {
// Hold a reference to the current animator,
// so that it can be canceled mid-way.
private Animator mCurrentAnimator;
// The system "short" animation time duration, in milliseconds. This
// duration is ideal for subtle animations or animations that occur
// very frequently.
private int mShortAnimationDuration;
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_zoom);
// Hook up clicks on the thumbnail views.
final View thumb1View = findViewById(R.id.thumb_button_1);
thumb1View.setOnClickListener(new View.OnClickListener() {
&#64;Override
public void onClick(View view) {
zoomImageFromThumb(thumb1View, R.drawable.image1);
}
});
// Retrieve and cache the system's default "short" animation time.
mShortAnimationDuration = getResources().getInteger(
android.R.integer.config_shortAnimTime);
}
...
}
</pre>
<h2 id="animate">
Zoom the View
</h2>
<p>
You'll now need to animate from the normal sized view to the zoomed view
when appropriate. In general, you need to animate from the bounds of the normal-sized view to the
bounds of the larger-sized view. The following method shows you how to implement a zoom animation that
zooms from an image thumbnail to an enlarged view by doing the following things:
</p>
<ol>
<li>Assign the high-res image to the hidden "zoomed-in" (enlarged) {@link
android.widget.ImageView}. The following example loads a large image resource on the UI
thread for simplicity. You will want to do this loading in a separate thread to prevent
blocking on the UI thread and then set the bitmap on the UI thread. Ideally, the bitmap
should not be larger than the screen size.
</li>
<li>Calculate the starting and ending bounds for the {@link android.widget.ImageView}.
</li>
<li>Animate each of the four positioning and sizing properties <code>{@link
android.view.View#X}</code>, <code>{@link android.view.View#Y}</code>, ({@link
android.view.View#SCALE_X}, and <code>{@link android.view.View#SCALE_Y}</code>)
simultaneously, from the starting bounds to the ending bounds. These four animations are
added to an {@link android.animation.AnimatorSet} so that they can be started at the same
time.
</li>
<li>Zoom back out by running a similar animation but in reverse when the user touches the
screen when the image is zoomed in. You can do this by adding a {@link
android.view.View.OnClickListener} to the {@link android.widget.ImageView}. When clicked, the
{@link android.widget.ImageView} minimizes back down to the size of the image thumbnail and
sets its visibility to {@link android.view.View#GONE} to hide it.
</li>
</ol>
<pre>
private void zoomImageFromThumb(final View thumbView, int imageResId) {
// If there's an animation in progress, cancel it
// immediately and proceed with this one.
if (mCurrentAnimator != null) {
mCurrentAnimator.cancel();
}
// Load the high-resolution "zoomed-in" image.
final ImageView expandedImageView = (ImageView) findViewById(
R.id.expanded_image);
expandedImageView.setImageResource(imageResId);
// Calculate the starting and ending bounds for the zoomed-in image.
// This step involves lots of math. Yay, math.
final Rect startBounds = new Rect();
final Rect finalBounds = new Rect();
final Point globalOffset = new Point();
// The start bounds are the global visible rectangle of the thumbnail,
// and the final bounds are the global visible rectangle of the container
// view. Also set the container view's offset as the origin for the
// bounds, since that's the origin for the positioning animation
// properties (X, Y).
thumbView.getGlobalVisibleRect(startBounds);
findViewById(R.id.container)
.getGlobalVisibleRect(finalBounds, globalOffset);
startBounds.offset(-globalOffset.x, -globalOffset.y);
finalBounds.offset(-globalOffset.x, -globalOffset.y);
// Adjust the start bounds to be the same aspect ratio as the final
// bounds using the "center crop" technique. This prevents undesirable
// stretching during the animation. Also calculate the start scaling
// factor (the end scaling factor is always 1.0).
float startScale;
if ((float) finalBounds.width() / finalBounds.height()
&gt; (float) startBounds.width() / startBounds.height()) {
// Extend start bounds horizontally
startScale = (float) startBounds.height() / finalBounds.height();
float startWidth = startScale * finalBounds.width();
float deltaWidth = (startWidth - startBounds.width()) / 2;
startBounds.left -= deltaWidth;
startBounds.right += deltaWidth;
} else {
// Extend start bounds vertically
startScale = (float) startBounds.width() / finalBounds.width();
float startHeight = startScale * finalBounds.height();
float deltaHeight = (startHeight - startBounds.height()) / 2;
startBounds.top -= deltaHeight;
startBounds.bottom += deltaHeight;
}
// Hide the thumbnail and show the zoomed-in view. When the animation
// begins, it will position the zoomed-in view in the place of the
// thumbnail.
thumbView.setAlpha(0f);
expandedImageView.setVisibility(View.VISIBLE);
// Set the pivot point for SCALE_X and SCALE_Y transformations
// to the top-left corner of the zoomed-in view (the default
// is the center of the view).
expandedImageView.setPivotX(0f);
expandedImageView.setPivotY(0f);
// Construct and run the parallel animation of the four translation and
// scale properties (X, Y, SCALE_X, and SCALE_Y).
AnimatorSet set = new AnimatorSet();
set
.play(ObjectAnimator.ofFloat(expandedImageView, View.X,
startBounds.left, finalBounds.left))
.with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
startBounds.top, finalBounds.top))
.with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X,
startScale, 1f)).with(ObjectAnimator.ofFloat(expandedImageView,
View.SCALE_Y, startScale, 1f));
set.setDuration(mShortAnimationDuration);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
&#64;Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimator = null;
}
&#64;Override
public void onAnimationCancel(Animator animation) {
mCurrentAnimator = null;
}
});
set.start();
mCurrentAnimator = set;
// Upon clicking the zoomed-in image, it should zoom back down
// to the original bounds and show the thumbnail instead of
// the expanded image.
final float startScaleFinal = startScale;
expandedImageView.setOnClickListener(new View.OnClickListener() {
&#64;Override
public void onClick(View view) {
if (mCurrentAnimator != null) {
mCurrentAnimator.cancel();
}
// Animate the four positioning/sizing properties in parallel,
// back to their original values.
AnimatorSet set = new AnimatorSet();
set.play(ObjectAnimator
.ofFloat(expandedImageView, View.X, startBounds.left))
.with(ObjectAnimator
.ofFloat(expandedImageView,
View.Y,startBounds.top))
.with(ObjectAnimator
.ofFloat(expandedImageView,
View.SCALE_X, startScaleFinal))
.with(ObjectAnimator
.ofFloat(expandedImageView,
View.SCALE_Y, startScaleFinal));
set.setDuration(mShortAnimationDuration);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
&#64;Override
public void onAnimationEnd(Animator animation) {
thumbView.setAlpha(1f);
expandedImageView.setVisibility(View.GONE);
mCurrentAnimator = null;
}
&#64;Override
public void onAnimationCancel(Animator animation) {
thumbView.setAlpha(1f);
expandedImageView.setVisibility(View.GONE);
mCurrentAnimator = null;
}
});
set.start();
mCurrentAnimator = set;
}
});
}
</pre>

View File

@@ -287,6 +287,34 @@
</li>
</ul>
</li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/animation/index.html">
<span class="en">Adding Animations</span>
</a></div>
<ul>
<li><a href="<?cs var:toroot ?>training/animation/crossfade.html">
<span class="en">Crossfading Two Views</span>
</a>
</li>
<li><a href="<?cs var:toroot ?>training/animation/screen-slide.html">
<span class="en">Using ViewPager for Screen Slide</span>
</a>
</li>
<li><a href="<?cs var:toroot ?>training/animation/cardflip.html">
<span class="en">Displaying Card Flip Animations</span>
</a>
</li>
<li><a href="<?cs var:toroot ?>training/animation/zoom.html">
<span class="en">Zooming a View</span>
</a>
</li>
<li><a href="<?cs var:toroot ?>training/animation/layout.html">
<span class="en">Animating Layout Changes</span>
</a>
</li>
</ul>
</li>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/managing-audio/index.html">