diff --git a/samples/training/AnimationsDemo/AndroidManifest.xml b/samples/training/AnimationsDemo/AndroidManifest.xml
new file mode 100755
index 000000000..dcb911a12
--- /dev/null
+++ b/samples/training/AnimationsDemo/AndroidManifest.xml
@@ -0,0 +1,85 @@
+
+
+
This sample shows an "info" action bar button that shows the back of a "card", rotating the + * front of the card out and the back of the card in. The reverse animation is played when the user + * presses the system Back button or the "photo" action bar button.
+ */ +public class CardFlipActivity extends Activity + implements FragmentManager.OnBackStackChangedListener { + /** + * A handler object, used for deferring UI operations. + */ + private Handler mHandler = new Handler(); + + /** + * Whether or not we're showing the back of the card (otherwise showing the front). + */ + private boolean mShowingBack = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_card_flip); + + if (savedInstanceState == null) { + // If there is no saved instance state, add a fragment representing the + // front of the card to this activity. If there is saved instance state, + // this fragment will have already been added to the activity. + getFragmentManager() + .beginTransaction() + .add(R.id.container, new CardFrontFragment()) + .commit(); + } else { + mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0); + } + + // Monitor back stack changes to ensure the action bar shows the appropriate + // button (either "photo" or "info"). + getFragmentManager().addOnBackStackChangedListener(this); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + + // Add either a "photo" or "finish" button to the action bar, depending on which page + // is currently selected. + MenuItem item = menu.add(Menu.NONE, R.id.action_flip, Menu.NONE, + mShowingBack + ? R.string.action_photo + : R.string.action_info); + item.setIcon(mShowingBack + ? R.drawable.ic_action_photo + : R.drawable.ic_action_info); + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // Navigate "up" the demo structure to the launchpad activity. + // See http://developer.android.com/design/patterns/navigation.html for more. + NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class)); + return true; + + case R.id.action_flip: + flipCard(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + 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(); + + // Defer an invalidation of the options menu (on modern devices, the action bar). This + // can't be done immediately because the transaction may not yet be committed. Commits + // are asynchronous in that they are posted to the main thread's message loop. + mHandler.post(new Runnable() { + @Override + public void run() { + invalidateOptionsMenu(); + } + }); + } + + @Override + public void onBackStackChanged() { + mShowingBack = (getFragmentManager().getBackStackEntryCount() > 0); + + // When the back stack changes, invalidate the options menu (action bar). + invalidateOptionsMenu(); + } + + /** + * A fragment representing the front of the card. + */ + public static class CardFrontFragment extends Fragment { + public CardFrontFragment() { + } + + @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 static class CardBackFragment extends Fragment { + public CardBackFragment() { + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_card_back, container, false); + } + } +} diff --git a/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/CrossfadeActivity.java b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/CrossfadeActivity.java new file mode 100644 index 000000000..b0f2509f8 --- /dev/null +++ b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/CrossfadeActivity.java @@ -0,0 +1,137 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.animationsdemo; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.NavUtils; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +/** + * This sample demonstrates cross-fading between two overlapping views. + * + *In this sample, the two overlapping views are a loading indicator and some text content. The + * active view is toggled by touching the toggle button in the action bar. In real-world + * applications, this toggle would occur as soon as content was available. Note that if content is + * immediately available, a loading spinner shouldn't be presented and there should be no + * animation.
+ */ +public class CrossfadeActivity extends Activity { + /** + * The flag indicating whether content is loaded (text is shown) or not (loading spinner is + * shown). + */ + private boolean mContentLoaded; + + /** + * The view (or view group) containing the content. This is one of two overlapping views. + */ + private View mContentView; + + /** + * The view containing the loading indicator. This is the other of two overlapping views. + */ + private View mLoadingView; + + /** + * 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_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); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.activity_crossfade, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // Navigate "up" the demo structure to the launchpad activity. + // See http://developer.android.com/design/patterns/navigation.html for more. + NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class)); + return true; + + case R.id.action_toggle: + // Toggle whether content is loaded. + mContentLoaded = !mContentLoaded; + showContentOrLoadingIndicator(mContentLoaded); + return true; + } + + return super.onOptionsItemSelected(item); + } + + /** + * Cross-fades between {@link #mContentView} and {@link #mLoadingView}. + */ + private void showContentOrLoadingIndicator(boolean contentLoaded) { + // Decide which view to hide and which to show. + final View showView = contentLoaded ? mContentView : mLoadingView; + final View hideView = contentLoaded ? mLoadingView : mContentView; + + // Set the "show" view to 0% opacity but visible, so that it is visible + // (but fully transparent) during the animation. + showView.setAlpha(0f); + showView.setVisibility(View.VISIBLE); + + // Animate the "show" view to 100% opacity, and clear any animation listener set on + // the view. Remember that listeners are not limited to the specific animation + // describes in the chained method calls. Listeners are set on the + // ViewPropertyAnimator object for the view, which persists across several + // animations. + showView.animate() + .alpha(1f) + .setDuration(mShortAnimationDuration) + .setListener(null); + + // Animate the "hide" 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.) + hideView.animate() + .alpha(0f) + .setDuration(mShortAnimationDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + hideView.setVisibility(View.GONE); + } + }); + } +} diff --git a/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/LayoutChangesActivity.java b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/LayoutChangesActivity.java new file mode 100644 index 000000000..42e3475a1 --- /dev/null +++ b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/LayoutChangesActivity.java @@ -0,0 +1,117 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.animationsdemo; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.NavUtils; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * This sample demonstrates how to use system-provided, automatic layout transitions. Layout + * transitions are animations that occur when views are added to, removed from, or changed within + * a {@link ViewGroup}. + * + *In this sample, the user can add rows to and remove rows from a vertical + * {@link android.widget.LinearLayout}.
+ */ +public class LayoutChangesActivity extends Activity { + /** + * The container view which has layout change animations turned on. In this sample, this view + * is a {@link android.widget.LinearLayout}. + */ + private ViewGroup mContainerView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_layout_changes); + + mContainerView = (ViewGroup) findViewById(R.id.container); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.activity_layout_changes, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // Navigate "up" the demo structure to the launchpad activity. + // See http://developer.android.com/design/patterns/navigation.html for more. + NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class)); + return true; + + case R.id.action_add_item: + // Hide the "empty" view since there is now at least one item in the list. + findViewById(android.R.id.empty).setVisibility(View.GONE); + addItem(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private void addItem() { + // Instantiate a new "row" view. + final ViewGroup newView = (ViewGroup) LayoutInflater.from(this).inflate( + R.layout.list_item_example, mContainerView, false); + + // Set the text in the new row to a random country. + ((TextView) newView.findViewById(android.R.id.text1)).setText( + COUNTRIES[(int) (Math.random() * COUNTRIES.length)]); + + // Set a click listener for the "X" button in the row that will remove the row. + newView.findViewById(R.id.delete_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Remove the row from its parent (the container view). + // Because mContainerView has android:animateLayoutChanges set to true, + // this removal is automatically animated. + mContainerView.removeView(newView); + + // If there are no rows remaining, show the empty view. + if (mContainerView.getChildCount() == 0) { + findViewById(android.R.id.empty).setVisibility(View.VISIBLE); + } + } + }); + + // Because mContainerView has android:animateLayoutChanges set to true, + // adding this view is automatically animated. + mContainerView.addView(newView, 0); + } + + /** + * A static list of country names. + */ + private static final String[] COUNTRIES = new String[]{ + "Belgium", "France", "Italy", "Germany", "Spain", + "Austria", "Russia", "Poland", "Croatia", "Greece", + "Ukraine", + }; +} diff --git a/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/MainActivity.java b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/MainActivity.java new file mode 100755 index 000000000..5d4802a05 --- /dev/null +++ b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/MainActivity.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.animationsdemo; + +import android.app.Activity; +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +/** + * The launchpad activity for this sample project. This activity launches other activities that + * demonstrate implementations of common animations. + */ +public class MainActivity extends ListActivity { + /** + * This class describes an individual sample (the sample title, and the activity class that + * demonstrates this sample). + */ + private class Sample { + private CharSequence title; + private Class extends Activity> activityClass; + + public Sample(int titleResId, Class extends Activity> activityClass) { + this.activityClass = activityClass; + this.title = getResources().getString(titleResId); + } + + @Override + public String toString() { + return title.toString(); + } + } + + /** + * The collection of all samples in the app. This gets instantiated in {@link + * #onCreate(android.os.Bundle)} because the {@link Sample} constructor needs access to {@link + * android.content.res.Resources}. + */ + private static Sample[] mSamples; + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // Instantiate the list of samples. + mSamples = new Sample[]{ + new Sample(R.string.title_crossfade, CrossfadeActivity.class), + new Sample(R.string.title_card_flip, CardFlipActivity.class), + new Sample(R.string.title_screen_slide, ScreenSlideActivity.class), + new Sample(R.string.title_zoom, ZoomActivity.class), + new Sample(R.string.title_layout_changes, LayoutChangesActivity.class), + }; + + setListAdapter(new ArrayAdapterThis sample shows a "next" button that advances the user to the next step in a wizard, + * animating the current screen out (to the left) and the next screen in (from the right). The + * reverse animation is played when the user presses the "previous" button.
+ * + * @see ScreenSlidePageFragment + */ +public class ScreenSlideActivity 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); + + // Instantiate a ViewPager and a PagerAdapter. + mPager = (ViewPager) findViewById(R.id.pager); + mPagerAdapter = new ScreenSlidePagerAdapter(getFragmentManager()); + mPager.setAdapter(mPagerAdapter); + mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + // When changing pages, reset the action bar actions since they are dependent + // on which page is currently active. An alternative approach is to have each + // fragment expose actions itself (rather than the activity exposing actions), + // but for simplicity, the activity provides the actions in this sample. + invalidateOptionsMenu(); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.activity_screen_slide, menu); + + menu.findItem(R.id.action_previous).setEnabled(mPager.getCurrentItem() > 0); + + // Add either a "next" or "finish" button to the action bar, depending on which page + // is currently selected. + MenuItem item = menu.add(Menu.NONE, R.id.action_next, Menu.NONE, + (mPager.getCurrentItem() == mPagerAdapter.getCount() - 1) + ? R.string.action_finish + : R.string.action_next); + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // Navigate "up" the demo structure to the launchpad activity. + // See http://developer.android.com/design/patterns/navigation.html for more. + NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class)); + return true; + + case R.id.action_previous: + // Go to the previous step in the wizard. If there is no previous step, + // setCurrentItem will do nothing. + mPager.setCurrentItem(mPager.getCurrentItem() - 1); + return true; + + case R.id.action_next: + // Advance to the next step in the wizard. If there is no next step, setCurrentItem + // will do nothing. + mPager.setCurrentItem(mPager.getCurrentItem() + 1); + return true; + } + + return super.onOptionsItemSelected(item); + } + + /** + * A simple pager adapter that represents 5 {@link ScreenSlidePageFragment} objects, in + * sequence. + */ + private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { + public ScreenSlidePagerAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public Fragment getItem(int position) { + return ScreenSlidePageFragment.create(position); + } + + @Override + public int getCount() { + return NUM_PAGES; + } + } +} diff --git a/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/ScreenSlidePageFragment.java b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/ScreenSlidePageFragment.java new file mode 100644 index 000000000..887c926bd --- /dev/null +++ b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/ScreenSlidePageFragment.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.animationsdemo; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +/** + * A fragment representing a single step in a wizard. The fragment shows a dummy title indicating + * the page number, along with some dummy text. + * + *This class is used by the {@link CardFlipActivity} and {@link + * ScreenSlideActivity} samples.
+ */ +public class ScreenSlidePageFragment extends Fragment { + /** + * The argument key for the page number this fragment represents. + */ + public static final String ARG_PAGE = "page"; + + /** + * The fragment's page number, which is set to the argument value for {@link #ARG_PAGE}. + */ + private int mPageNumber; + + /** + * Factory method for this fragment class. Constructs a new fragment for the given page number. + */ + public static ScreenSlidePageFragment create(int pageNumber) { + ScreenSlidePageFragment fragment = new ScreenSlidePageFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_PAGE, pageNumber); + fragment.setArguments(args); + return fragment; + } + + public ScreenSlidePageFragment() { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mPageNumber = getArguments().getInt(ARG_PAGE); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout containing a title and body text. + ViewGroup rootView = (ViewGroup) inflater + .inflate(R.layout.fragment_screen_slide_page, container, false); + + // Set the title view to show the page number. + ((TextView) rootView.findViewById(android.R.id.text1)).setText( + getString(R.string.title_template_step, mPageNumber + 1)); + + return rootView; + } + + /** + * Returns the page number represented by this fragment object. + */ + public int getPageNumber() { + return mPageNumber; + } +} diff --git a/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/TouchHighlightImageButton.java b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/TouchHighlightImageButton.java new file mode 100644 index 000000000..8fc3e0193 --- /dev/null +++ b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/TouchHighlightImageButton.java @@ -0,0 +1,109 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.animationsdemo; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ImageButton; + +/** + * An image button that uses a blue highlight (@link android.R.attr.selectableItemBackground} to + * indicate pressed and focused states. + */ +public class TouchHighlightImageButton extends ImageButton { + /** + * The highlight drawable. This generally a {@link android.graphics.drawable.StateListDrawable} + * that's transparent in the default state, and contains a semi-transparent overlay + * for the focused and pressed states. + */ + private Drawable mForegroundDrawable; + + /** + * The cached bounds of the view. + */ + private Rect mCachedBounds = new Rect(); + + public TouchHighlightImageButton(Context context) { + super(context); + init(); + } + + public TouchHighlightImageButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public TouchHighlightImageButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + /** + * General view initialization used common to all constructors of the view. + */ + private void init() { + // Reset default ImageButton background and padding. + setBackgroundColor(0); + setPadding(0, 0, 0, 0); + + // Retrieve the drawable resource assigned to the android.R.attr.selectableItemBackground + // theme attribute from the current theme. + TypedArray a = getContext() + .obtainStyledAttributes(new int[]{android.R.attr.selectableItemBackground}); + mForegroundDrawable = a.getDrawable(0); + mForegroundDrawable.setCallback(this); + a.recycle(); + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + + // Update the state of the highlight drawable to match + // the state of the button. + if (mForegroundDrawable.isStateful()) { + mForegroundDrawable.setState(getDrawableState()); + } + + // Trigger a redraw. + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + // First draw the image. + super.onDraw(canvas); + + // Then draw the highlight on top of it. If the button is neither focused + // nor pressed, the drawable will be transparent, so just the image + // will be drawn. + mForegroundDrawable.setBounds(mCachedBounds); + mForegroundDrawable.draw(canvas); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + // Cache the view bounds. + mCachedBounds.set(0, 0, w, h); + } +} diff --git a/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/ZoomActivity.java b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/ZoomActivity.java new file mode 100644 index 000000000..70dcfcbd5 --- /dev/null +++ b/samples/training/AnimationsDemo/src/com/example/android/animationsdemo/ZoomActivity.java @@ -0,0 +1,233 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.animationsdemo; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Intent; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.NavUtils; +import android.view.MenuItem; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; + +/** + * A sample showing how to zoom an image thumbnail to full-screen, by animating the bounds of the + * zoomed image from the thumbnail bounds to the screen bounds. + * + *In this sample, the user can touch one of two images. Touching an image zooms it in, covering + * the entire activity content area. Touching the zoomed-in image hides it.
+ */ +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); + } + }); + + final View thumb2View = findViewById(R.id.thumb_button_2); + thumb2View.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + zoomImageFromThumb(thumb2View, R.drawable.image2); + } + }); + + // Retrieve and cache the system's default "short" animation time. + mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // Navigate "up" the demo structure to the launchpad activity. + // See http://developer.android.com/design/patterns/navigation.html for more. + NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class)); + return true; + } + + return super.onOptionsItemSelected(item); + } + + /** + * "Zooms" in a thumbnail view by assigning the high resolution image to a hidden "zoomed-in" + * image view and animating its bounds to fit the entire activity content area. More + * specifically: + * + *