am ea502b27: Merge "docs: animations au class" into jb-dev-docs
* commit 'ea502b27020872003f1d2be1fb2644b04cc59a67': docs: animations au class
This commit is contained in:
BIN
docs/downloads/training/Animations.zip
Normal file
BIN
docs/downloads/training/Animations.zip
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_card_flip.mp4
Executable file
BIN
docs/html/training/animation/anim_card_flip.mp4
Executable file
Binary file not shown.
BIN
docs/html/training/animation/anim_card_flip.ogv
Executable file
BIN
docs/html/training/animation/anim_card_flip.ogv
Executable file
Binary file not shown.
BIN
docs/html/training/animation/anim_card_flip.webm
Executable file
BIN
docs/html/training/animation/anim_card_flip.webm
Executable file
Binary file not shown.
BIN
docs/html/training/animation/anim_crossfade.mp4
Normal file
BIN
docs/html/training/animation/anim_crossfade.mp4
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_crossfade.ogv
Normal file
BIN
docs/html/training/animation/anim_crossfade.ogv
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_crossfade.webm
Normal file
BIN
docs/html/training/animation/anim_crossfade.webm
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_layout_changes.mp4
Normal file
BIN
docs/html/training/animation/anim_layout_changes.mp4
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_layout_changes.ogv
Normal file
BIN
docs/html/training/animation/anim_layout_changes.ogv
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_layout_changes.webm
Normal file
BIN
docs/html/training/animation/anim_layout_changes.webm
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_screenslide.mp4
Executable file
BIN
docs/html/training/animation/anim_screenslide.mp4
Executable file
Binary file not shown.
BIN
docs/html/training/animation/anim_screenslide.ogv
Executable file
BIN
docs/html/training/animation/anim_screenslide.ogv
Executable file
Binary file not shown.
BIN
docs/html/training/animation/anim_screenslide.webm
Executable file
BIN
docs/html/training/animation/anim_screenslide.webm
Executable file
Binary file not shown.
BIN
docs/html/training/animation/anim_zoom.mp4
Normal file
BIN
docs/html/training/animation/anim_zoom.mp4
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_zoom.ogv
Normal file
BIN
docs/html/training/animation/anim_zoom.ogv
Normal file
Binary file not shown.
BIN
docs/html/training/animation/anim_zoom.webm
Normal file
BIN
docs/html/training/animation/anim_zoom.webm
Normal file
Binary file not shown.
365
docs/html/training/animation/cardflip.jd
Normal file
365
docs/html/training/animation/cardflip.jd
Normal 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"> </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>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- Before rotating, immediately set the alpha to 0. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="1.0"
|
||||||
|
android:valueTo="0.0"
|
||||||
|
android:propertyName="alpha"
|
||||||
|
android:duration="0" />
|
||||||
|
|
||||||
|
<!-- Rotate. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="-180"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:propertyName="rotationY"
|
||||||
|
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||||
|
android:duration="@integer/card_flip_time_full" />
|
||||||
|
|
||||||
|
<!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="0.0"
|
||||||
|
android:valueTo="1.0"
|
||||||
|
android:propertyName="alpha"
|
||||||
|
android:startOffset="@integer/card_flip_time_half"
|
||||||
|
android:duration="1" />
|
||||||
|
</set>
|
||||||
|
</pre>
|
||||||
|
<h4>
|
||||||
|
card_flip_left_out.xml
|
||||||
|
</h4>
|
||||||
|
<pre>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- Rotate. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="180"
|
||||||
|
android:propertyName="rotationY"
|
||||||
|
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||||
|
android:duration="@integer/card_flip_time_full" />
|
||||||
|
|
||||||
|
<!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="1.0"
|
||||||
|
android:valueTo="0.0"
|
||||||
|
android:propertyName="alpha"
|
||||||
|
android:startOffset="@integer/card_flip_time_half"
|
||||||
|
android:duration="1" />
|
||||||
|
</set>
|
||||||
|
</pre>
|
||||||
|
<h4>
|
||||||
|
card_flip_right_in.xml
|
||||||
|
</h4>
|
||||||
|
<pre>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- Before rotating, immediately set the alpha to 0. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="1.0"
|
||||||
|
android:valueTo="0.0"
|
||||||
|
android:propertyName="alpha"
|
||||||
|
android:duration="0" />
|
||||||
|
|
||||||
|
<!-- Rotate. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="180"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:propertyName="rotationY"
|
||||||
|
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||||
|
android:duration="@integer/card_flip_time_full" />
|
||||||
|
|
||||||
|
<!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="0.0"
|
||||||
|
android:valueTo="1.0"
|
||||||
|
android:propertyName="alpha"
|
||||||
|
android:startOffset="@integer/card_flip_time_half"
|
||||||
|
android:duration="1" />
|
||||||
|
</set>
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<h4>
|
||||||
|
card_flip_right_out.xml
|
||||||
|
</h4>
|
||||||
|
<pre>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- Rotate. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="0"
|
||||||
|
android:valueTo="-180"
|
||||||
|
android:propertyName="rotationY"
|
||||||
|
android:interpolator="@android:interpolator/accelerate_decelerate"
|
||||||
|
android:duration="@integer/card_flip_time_full" />
|
||||||
|
|
||||||
|
<!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
|
||||||
|
<objectAnimator
|
||||||
|
android:valueFrom="1.0"
|
||||||
|
android:valueTo="0.0"
|
||||||
|
android:propertyName="alpha"
|
||||||
|
android:startOffset="@integer/card_flip_time_half"
|
||||||
|
android:duration="1" />
|
||||||
|
</set>
|
||||||
|
</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>
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</pre>
|
||||||
|
<p>
|
||||||
|
and the other side of the card that displays an {@link android.widget.ImageView}:
|
||||||
|
</p>
|
||||||
|
<pre>
|
||||||
|
<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" />
|
||||||
|
</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 {
|
||||||
|
@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 {
|
||||||
|
@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>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</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 {
|
||||||
|
|
||||||
|
@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>
|
||||||
208
docs/html/training/animation/crossfade.jd
Normal file
208
docs/html/training/animation/crossfade.jd
Normal 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"> </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>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<ProgressBar android:id="@+id/loading_spinner"
|
||||||
|
style="?android:progressBarStyleLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</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;
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
@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() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
mHideView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
86
docs/html/training/animation/index.jd
Normal file
86
docs/html/training/animation/index.jd
Normal 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>
|
||||||
77
docs/html/training/animation/layout.jd
Normal file
77
docs/html/training/animation/layout.jd
Normal 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"> </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>
|
||||||
|
<LinearLayout android:id="@+id/container"
|
||||||
|
android:animateLayoutChanges="true"
|
||||||
|
...
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
185
docs/html/training/animation/screen-slide.jd
Executable file
185
docs/html/training/animation/screen-slide.jd
Executable 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"> </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>
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
</com.example.android.animationsdemo.ScrollView>
|
||||||
|
</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 {
|
||||||
|
|
||||||
|
@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>
|
||||||
|
<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" />
|
||||||
|
</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;
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
return new ScreenSlidePageFragment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return NUM_PAGES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
320
docs/html/training/animation/zoom.jd
Normal file
320
docs/html/training/animation/zoom.jd
Normal 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"> </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>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</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;
|
||||||
|
|
||||||
|
@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() {
|
||||||
|
@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()
|
||||||
|
> (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() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
mCurrentAnimator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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() {
|
||||||
|
@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() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
thumbView.setAlpha(1f);
|
||||||
|
expandedImageView.setVisibility(View.GONE);
|
||||||
|
mCurrentAnimator = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationCancel(Animator animation) {
|
||||||
|
thumbView.setAlpha(1f);
|
||||||
|
expandedImageView.setVisibility(View.GONE);
|
||||||
|
mCurrentAnimator = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
set.start();
|
||||||
|
mCurrentAnimator = set;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
@@ -288,6 +288,34 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</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">
|
<li class="nav-section">
|
||||||
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/managing-audio/index.html">
|
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/managing-audio/index.html">
|
||||||
<span class="en">Managing Audio Playback</span>
|
<span class="en">Managing Audio Playback</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user