diff --git a/docs/downloads/training/Animations.zip b/docs/downloads/training/Animations.zip new file mode 100644 index 0000000000000..5063dd1a5c845 Binary files /dev/null and b/docs/downloads/training/Animations.zip differ diff --git a/docs/html/training/animation/anim_card_flip.mp4 b/docs/html/training/animation/anim_card_flip.mp4 new file mode 100755 index 0000000000000..e885f987c413d Binary files /dev/null and b/docs/html/training/animation/anim_card_flip.mp4 differ diff --git a/docs/html/training/animation/anim_card_flip.ogv b/docs/html/training/animation/anim_card_flip.ogv new file mode 100755 index 0000000000000..33cd86c23e092 Binary files /dev/null and b/docs/html/training/animation/anim_card_flip.ogv differ diff --git a/docs/html/training/animation/anim_card_flip.webm b/docs/html/training/animation/anim_card_flip.webm new file mode 100755 index 0000000000000..a670d7831c8a4 Binary files /dev/null and b/docs/html/training/animation/anim_card_flip.webm differ diff --git a/docs/html/training/animation/anim_crossfade.mp4 b/docs/html/training/animation/anim_crossfade.mp4 new file mode 100644 index 0000000000000..ced7cc9e7c53b Binary files /dev/null and b/docs/html/training/animation/anim_crossfade.mp4 differ diff --git a/docs/html/training/animation/anim_crossfade.ogv b/docs/html/training/animation/anim_crossfade.ogv new file mode 100644 index 0000000000000..7ec417a802b8e Binary files /dev/null and b/docs/html/training/animation/anim_crossfade.ogv differ diff --git a/docs/html/training/animation/anim_crossfade.webm b/docs/html/training/animation/anim_crossfade.webm new file mode 100644 index 0000000000000..21e7228887978 Binary files /dev/null and b/docs/html/training/animation/anim_crossfade.webm differ diff --git a/docs/html/training/animation/anim_layout_changes.mp4 b/docs/html/training/animation/anim_layout_changes.mp4 new file mode 100644 index 0000000000000..0709601f432fb Binary files /dev/null and b/docs/html/training/animation/anim_layout_changes.mp4 differ diff --git a/docs/html/training/animation/anim_layout_changes.ogv b/docs/html/training/animation/anim_layout_changes.ogv new file mode 100644 index 0000000000000..75f5250107f66 Binary files /dev/null and b/docs/html/training/animation/anim_layout_changes.ogv differ diff --git a/docs/html/training/animation/anim_layout_changes.webm b/docs/html/training/animation/anim_layout_changes.webm new file mode 100644 index 0000000000000..a99a5666ba75d Binary files /dev/null and b/docs/html/training/animation/anim_layout_changes.webm differ diff --git a/docs/html/training/animation/anim_screenslide.mp4 b/docs/html/training/animation/anim_screenslide.mp4 new file mode 100755 index 0000000000000..3e6502642e644 Binary files /dev/null and b/docs/html/training/animation/anim_screenslide.mp4 differ diff --git a/docs/html/training/animation/anim_screenslide.ogv b/docs/html/training/animation/anim_screenslide.ogv new file mode 100755 index 0000000000000..c45ebd44db53a Binary files /dev/null and b/docs/html/training/animation/anim_screenslide.ogv differ diff --git a/docs/html/training/animation/anim_screenslide.webm b/docs/html/training/animation/anim_screenslide.webm new file mode 100755 index 0000000000000..c72adbcb13849 Binary files /dev/null and b/docs/html/training/animation/anim_screenslide.webm differ diff --git a/docs/html/training/animation/anim_zoom.mp4 b/docs/html/training/animation/anim_zoom.mp4 new file mode 100644 index 0000000000000..4326c35b3033e Binary files /dev/null and b/docs/html/training/animation/anim_zoom.mp4 differ diff --git a/docs/html/training/animation/anim_zoom.ogv b/docs/html/training/animation/anim_zoom.ogv new file mode 100644 index 0000000000000..e5793f3a76651 Binary files /dev/null and b/docs/html/training/animation/anim_zoom.ogv differ diff --git a/docs/html/training/animation/anim_zoom.webm b/docs/html/training/animation/anim_zoom.webm new file mode 100644 index 0000000000000..b3b7566c5e8c4 Binary files /dev/null and b/docs/html/training/animation/anim_zoom.webm differ diff --git a/docs/html/training/animation/cardflip.jd b/docs/html/training/animation/cardflip.jd new file mode 100644 index 0000000000000..ab3eb3af8c0f1 --- /dev/null +++ b/docs/html/training/animation/cardflip.jd @@ -0,0 +1,365 @@ +page.title=Displaying Card Flip Animations +trainingnavtop=true + +@jd:body +
+
+

+ This lesson teaches you to +

+
    +
  1. + Create the Animators +
  2. +
  3. + Create the Views +
  4. +
  5. + Create the Fragment +
  6. +
  7. + Animate the Card Flip +
  8. +
+
+
+

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. +

+

Here's what a card flip looks like: +

+ +
+ +
+
+ Card flip animation +
 
+
+ +

+ If you want to jump ahead and see a full working example, + download and + run the sample app and select the Card Flip example. See the following + files for the code implementation: +

+ + +

+ Create the Animators +

+

+ 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. +

+

+ card_flip_left_in.xml +

+
+<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>
+
+

+ card_flip_left_out.xml +

+
+<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>
+    
+

+ card_flip_right_in.xml +

+
+<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" />
+
+
+
+

+ card_flip_right_out.xml +

+
+<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>
+
+

+ Create the Views +

+

+ 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: +

+ +
+<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>
+
+

+and the other side of the card that displays an {@link android.widget.ImageView}: +

+
+<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" />
+
+

+ Create the Fragment +

+

+ 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: +

+
+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);
+        }
+    }
+}
+
+

+ Animate the Card Flip +

+ +

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:

+ +
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
+
+ +

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: +

+ + + +
+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();
+        }
+    }
+    ...
+}
+
+

+ 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: +

+ +
+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();
+}
+
\ No newline at end of file diff --git a/docs/html/training/animation/crossfade.jd b/docs/html/training/animation/crossfade.jd new file mode 100644 index 0000000000000..99e879bbc0033 --- /dev/null +++ b/docs/html/training/animation/crossfade.jd @@ -0,0 +1,208 @@ +page.title=Crossfading Two Views +trainingnavtop=true + + +@jd:body + +
+
+

+ This lesson teaches you to: +

+
    +
  1. + Create the Views +
  2. +
  3. + Set up the Animation +
  4. +
  5. + Crossfade the Views +
  6. +
+
+
+ +

+ 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. +

+

Here's an example of a crossfade from a progress indicator to some text content. +

+ +
+ +
+
+Crossfade animation +
 
+
+ +

+ If you want to jump ahead and see a full working example, + download + and run the sample app and select the Crossfade example. + See the following files for the code implementation: +

+ +

+ Create the Views +

+

+ Create the two views that you want to crossfade. The following example creates a progress + indicator and a scrollable text view: +

+
+<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>
+
+

+ Set up the Animation +

+

+ To set up the animation: +

+
    +
  1. Create member variables for the views that you want to crossfade. You need + these references later when modifying the views during the animation. +
  2. +
  3. 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. +
  4. +
  5. Cache the {@link android.R.integer#config_shortAnimTime} + 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. +
  6. +
+

+ Here's an example using the layout from the previous code snippet as the activity content + view: +

+
+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);
+    }
+
+
+

+ Crossfade the Views +

+

+ Now that the views are properly set up, crossfade them by doing the following: +

+
    +
  1. For the view that is fading in, set the alpha value to 0 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. +
  2. +
  3. For the view that is fading in, animate its alpha value from 0 to + 1. At the same time, for the view that is fading out, animate the alpha value + from 1 to 0. +
  4. +
  5. 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 0, + 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. +
  6. +
+

+ The following method shows an example of how to do this: +

+
+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);
+                }
+            });
+}
+
\ No newline at end of file diff --git a/docs/html/training/animation/index.jd b/docs/html/training/animation/index.jd new file mode 100644 index 0000000000000..9cc7e6c405c0e --- /dev/null +++ b/docs/html/training/animation/index.jd @@ -0,0 +1,86 @@ +page.title=Adding Animations +trainingnavtop=true +startpage=true + +@jd:body +
+
+

+ Dependencies and prerequisites +

+ +

+ You should also read +

+ +

+ Try it out +

+
+ Download the sample app +

+ Animations.zip +

+
+
+
+

+ 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. +

+

+ 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. +

+ +

+ Lessons +

+
+
+ Crossfading Two Views +
+
+ 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. +
+
+ Using ViewPager for Screen Slides +
+
+ Learn how to animate between horizontally adjacent screens with a sliding transition. +
+
+ Displaying Card Flip Animations +
+
+ Learn how to animate between two views with a flipping motion. +
+
+ Zooming a View +
+
+ Learn how to enlarge views with a touch-to-zoom animation. +
+
+ Animating Layout Changes +
+
+ Learn how to enable built-in animations when adding, removing, or updating child views in a layout. +
+
\ No newline at end of file diff --git a/docs/html/training/animation/layout.jd b/docs/html/training/animation/layout.jd new file mode 100644 index 0000000000000..b8e00777dcbb3 --- /dev/null +++ b/docs/html/training/animation/layout.jd @@ -0,0 +1,77 @@ +page.title=Animating Layout Changes +trainingnavtop=true + +@jd:body + +
+
+ +

This lesson teaches you to:

+
    +
  1. Create the Layout
  2. +
  3. Add, Update, or Remove Items from the Layout
  4. +
+ +
+
+ +

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. +

+

Tip: 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. +

+ Here's what a default layout animation looks like when adding items to a list: +

+ +
+ +
+
+ Layout animation +
 
+
+ +

If you want to jump ahead and see a full working example, +download and +run the sample app and select the Crossfade example. See the following files for the +code implementation:

+
    +
  1. src/LayoutChangesActivity.java
  2. +
  3. layout/activity_layout_changes.xml
  4. +
  5. menu/activity_layout_changes.xml
  6. +
+ +

Create the Layout

+

In your activity's layout XML file, set the android:animateLayoutChanges + attribute to true for the layout that you want to enable animations for. + For instance:

+ +
+<LinearLayout android:id="@+id/container"
+    android:animateLayoutChanges="true"
+    ...
+/>
+
+ +

Add, Update, or Remove Items from the Layout

+

+Now, all you need to do is add, remove, or update items in the layout +and the items are animated automatically: +

+
+private ViewGroup mContainerView;
+...
+private void addItem() {
+    View newView;
+    ...
+    mContainerView.addView(newView, 0);
+}
+
diff --git a/docs/html/training/animation/screen-slide.jd b/docs/html/training/animation/screen-slide.jd new file mode 100755 index 0000000000000..8a7af672e916f --- /dev/null +++ b/docs/html/training/animation/screen-slide.jd @@ -0,0 +1,185 @@ +page.title=Using ViewPager for Screen Slides +trainingnavtop=true + +@jd:body + +
+
+

This lesson teaches you to

+
    +
  1. Create the Views
  2. +
  3. Create the Fragment
  4. +
  5. Animate the Screen Slide
  6. +
+
+
+

+ 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 support library. + {@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: +

+ +
+ +
+ +
+ Screen slide animation +
 
+
+ +

If you want to jump ahead and see a full working example, +download +and run the sample app and select the Screen Slide example. See the +following files for the code implementation:

+ + +

Create the Views

+

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: + +

+<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>
+
+ +

Create the Fragment

+

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:

+ + +
+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;
+    }
+}
+
+ +

Screen Slides with ViewPager

+ +

{@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. +

+ +

To begin, create a layout that contains a {@link android.support.v4.view.ViewPager}:

+ +
+<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" />
+
+ +

Create an activity that does the following things: +

+ + + +
+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;
+        }
+    }
+}
+
\ No newline at end of file diff --git a/docs/html/training/animation/zoom.jd b/docs/html/training/animation/zoom.jd new file mode 100644 index 0000000000000..5dc2b6c25b925 --- /dev/null +++ b/docs/html/training/animation/zoom.jd @@ -0,0 +1,320 @@ +page.title=Zooming a View +trainingnavtop=true + +@jd:body + +
+
+

+ This lesson teaches you to: +

+
    +
  1. + Create the Views +
  2. +
  3. + Set up the Zoom Animation +
  4. +
  5. + Zoom the View +
  6. +
+
+
+

+ 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. +

+

Here's what a touch-to-zoom animation looks like that + expands an image thumbnail to fill the screen: +

+ +
+ +
+
+ Zoom animation +
 
+
+ +

+ If you want to jump ahead and see a full working example, + download and + run the sample app and select the + Zoom example. See the following files for the code implementation: +

+ +

+ Create the Views +

+

+ 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: +

+
+<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>
+
+

+ Set up the Zoom Animation +

+

+ 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: +

+
+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);
+    }
+    ...
+}
+
+

+ Zoom the View +

+

+ 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: +

+
    +
  1. 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. +
  2. +
  3. Calculate the starting and ending bounds for the {@link android.widget.ImageView}. +
  4. +
  5. Animate each of the four positioning and sizing properties {@link + android.view.View#X}, {@link android.view.View#Y}, ({@link + android.view.View#SCALE_X}, and {@link android.view.View#SCALE_Y}) + 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. +
  6. +
  7. 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. +
  8. +
+
+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;
+        }
+    });
+}
+
\ No newline at end of file diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs index 4ad13533bfc28..4a5b0fadc5ddd 100644 --- a/docs/html/training/training_toc.cs +++ b/docs/html/training/training_toc.cs @@ -287,6 +287,34 @@ + +