am 58cbe71c: am 8654b6c6: am 3ff1591c: am 28b127ec: New DevBytes animation demos
* commit '58cbe71cc434d787e54d3b3a1d7d86af9727857c':
New DevBytes animation demos
diff --git a/samples/devbytes/animation/ActivityAnimations/AndroidManifest.xml b/samples/devbytes/animation/ActivityAnimations/AndroidManifest.xml
new file mode 100644
index 0000000..eeb5378
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.activityanim"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="16"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.example.android.activityanim.ActivityAnimations"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name="com.example.android.activityanim.PictureDetailsActivity"
+ android:label="@string/subactivity_name"
+ android:theme="@style/Transparent" >
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/ActivityAnimations/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ActivityAnimations/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p1.jpg b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p1.jpg
new file mode 100644
index 0000000..9745818
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p1.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p2.jpg b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p2.jpg
new file mode 100644
index 0000000..db8731f
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p2.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p3.jpg b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p3.jpg
new file mode 100644
index 0000000..b240b3a
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p3.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p4.jpg b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p4.jpg
new file mode 100644
index 0000000..4de9292
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p4.jpg
Binary files differ
diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ActivityAnimations/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ActivityAnimations/res/layout/activity_animations.xml b/samples/devbytes/animation/ActivityAnimations/res/layout/activity_animations.xml
new file mode 100644
index 0000000..c11a568
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/layout/activity_animations.xml
@@ -0,0 +1,21 @@
+<!-- Copyright (C) 2013 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.
+-->
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal"
+ android:id="@+id/gridLayout" >
+
+</GridLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ActivityAnimations/res/layout/picture_info.xml b/samples/devbytes/animation/ActivityAnimations/res/layout/picture_info.xml
new file mode 100644
index 0000000..cb2ced2
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/layout/picture_info.xml
@@ -0,0 +1,41 @@
+<!-- Copyright (C) 2013 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/topLevelLayout">
+
+ <view
+ class="com.example.android.activityanim.ShadowLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/shadowLayout"
+ android:visibility="visible" >
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/imageView" />
+ <ImageView
+ android:id="@+id/imageView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:scaleType="centerInside" />
+
+
+ </view>
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ActivityAnimations/res/menu/activity_better_window_animations.xml b/samples/devbytes/animation/ActivityAnimations/res/menu/activity_better_window_animations.xml
new file mode 100644
index 0000000..aab540e
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/menu/activity_better_window_animations.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2013 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_slow"
+ android:orderInCategory="100"
+ android:showAsAction="never"
+ android:title="@string/menu_slow_animations"
+ android:checkable="true"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ActivityAnimations/res/values/strings.xml b/samples/devbytes/animation/ActivityAnimations/res/values/strings.xml
new file mode 100644
index 0000000..f9409b5
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/values/strings.xml
@@ -0,0 +1,21 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">Activity Animations</string>
+ <string name="subactivity_name">PictureInfo!</string>
+ <string name="menu_slow_animations">Slow</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ActivityAnimations/res/values/styles.xml b/samples/devbytes/animation/ActivityAnimations/res/values/styles.xml
new file mode 100644
index 0000000..9d83342
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/res/values/styles.xml
@@ -0,0 +1,39 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+ <style name="Transparent">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/ActivityAnimations.java b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/ActivityAnimations.java
new file mode 100644
index 0000000..4a3e0d9
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/ActivityAnimations.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2013 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.activityanim;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.GridLayout;
+import android.widget.ImageView;
+
+/**
+ * This example shows how to create a custom activity animation when you want something more
+ * than window animations can provide. The idea is to disable window animations for the
+ * activities and to instead launch or return from the sub-activity immediately, but use
+ * property animations inside the activities to customize the transition.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
+ * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
+ */
+public class ActivityAnimations extends Activity {
+
+ private static final String PACKAGE = "com.example.android.activityanim";
+ static float sAnimatorScale = 1;
+
+ GridLayout mGridLayout;
+ HashMap<ImageView, PictureData> mPicturesData = new HashMap<ImageView, PictureData>();
+ BitmapUtils mBitmapUtils = new BitmapUtils();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_animations);
+
+ // Grayscale filter used on all thumbnails
+ ColorMatrix grayMatrix = new ColorMatrix();
+ grayMatrix.setSaturation(0);
+ ColorMatrixColorFilter grayscaleFilter = new ColorMatrixColorFilter(grayMatrix);
+
+ mGridLayout = (GridLayout) findViewById(R.id.gridLayout);
+ mGridLayout.setColumnCount(3);
+ mGridLayout.setUseDefaultMargins(true);
+
+ // add all photo thumbnails to layout
+ Resources resources = getResources();
+ ArrayList<PictureData> pictures = mBitmapUtils.loadPhotos(resources);
+ for (int i = 0; i < pictures.size(); ++i) {
+ PictureData pictureData = pictures.get(i);
+ BitmapDrawable thumbnailDrawable =
+ new BitmapDrawable(resources, pictureData.thumbnail);
+ thumbnailDrawable.setColorFilter(grayscaleFilter);
+ ImageView imageView = new ImageView(this);
+ imageView.setOnClickListener(thumbnailClickListener);
+ imageView.setImageDrawable(thumbnailDrawable);
+ mPicturesData.put(imageView, pictureData);
+ mGridLayout.addView(imageView);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity_better_window_animations, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_slow) {
+ sAnimatorScale = item.isChecked() ? 1 : 5;
+ item.setChecked(!item.isChecked());
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * When the user clicks a thumbnail, bundle up information about it and launch the
+ * details activity.
+ */
+ private View.OnClickListener thumbnailClickListener = new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ // Interesting data to pass across are the thumbnail size/location, the
+ // resourceId of the source bitmap, the picture description, and the
+ // orientation (to avoid returning back to an obsolete configuration if
+ // the device rotates again in the meantime)
+ int[] screenLocation = new int[2];
+ v.getLocationOnScreen(screenLocation);
+ PictureData info = mPicturesData.get(v);
+ Intent subActivity = new Intent(ActivityAnimations.this,
+ PictureDetailsActivity.class);
+ int orientation = getResources().getConfiguration().orientation;
+ subActivity.
+ putExtra(PACKAGE + ".orientation", orientation).
+ putExtra(PACKAGE + ".resourceId", info.resourceId).
+ putExtra(PACKAGE + ".left", screenLocation[0]).
+ putExtra(PACKAGE + ".top", screenLocation[1]).
+ putExtra(PACKAGE + ".width", v.getWidth()).
+ putExtra(PACKAGE + ".height", v.getHeight()).
+ putExtra(PACKAGE + ".description", info.description);
+ startActivity(subActivity);
+
+ // Override transitions: we don't want the normal window animation in addition
+ // to our custom one
+ overridePendingTransition(0, 0);
+ }
+ };
+
+}
diff --git a/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/BitmapUtils.java b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/BitmapUtils.java
new file mode 100644
index 0000000..a8034dc
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/BitmapUtils.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2013 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.activityanim;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.widget.ImageView;
+
+public class BitmapUtils {
+
+ int[] mPhotos = {
+ R.drawable.p1,
+ R.drawable.p2,
+ R.drawable.p3,
+ R.drawable.p4
+ };
+
+ String[] mDescriptions = {
+ "This picture was taken while sunbathing in a natural hot spring, which was " +
+ "unfortunately filled with acid, which is a lasting memory from that trip, whenever I " +
+ "I look at my own skin.",
+ "I took this shot with a pinhole camera mounted on a tripod constructed out of " +
+ "soda straws. I felt that that combination best captured the beauty of the landscape " +
+ "in juxtaposition with the detritus of mankind.",
+ "I don't remember where or when I took this picture. All I know is that I was really " +
+ "drunk at the time, and I woke up without my left sock.",
+ "Right before I took this picture, there was a busload of school children right " +
+ "in my way. I knew the perfect shot was coming, so I quickly yelled 'Free candy!!!' " +
+ "and they scattered.",
+ };
+
+ static HashMap<Integer, Bitmap> sBitmapResourceMap = new HashMap<Integer, Bitmap>();
+
+ /**
+ * Load pictures and descriptions. A real app wouldn't do it this way, but that's
+ * not the point of this animation demo. Loading asynchronously is a better way to go
+ * for what can be time-consuming operations.
+ */
+ public ArrayList<PictureData> loadPhotos(Resources resources) {
+ ArrayList<PictureData> pictures = new ArrayList<PictureData>();
+ for (int i = 0; i < 30; ++i) {
+ int resourceId = mPhotos[(int) (Math.random() * mPhotos.length)];
+ Bitmap bitmap = getBitmap(resources, resourceId);
+ Bitmap thumbnail = getThumbnail(bitmap, 200);
+ String description = mDescriptions[(int) (Math.random() * mDescriptions.length)];
+ pictures.add(new PictureData(resourceId, description, thumbnail));
+ }
+ return pictures;
+ }
+
+ /**
+ * Utility method to get bitmap from cache or, if not there, load it
+ * from its resource.
+ */
+ static Bitmap getBitmap(Resources resources, int resourceId) {
+ Bitmap bitmap = sBitmapResourceMap.get(resourceId);
+ if (bitmap == null) {
+ bitmap = BitmapFactory.decodeResource(resources, resourceId);
+ sBitmapResourceMap.put(resourceId, bitmap);
+ }
+ return bitmap;
+ }
+
+ /**
+ * Create and return a thumbnail image given the original source bitmap and a max
+ * dimension (width or height).
+ */
+ private Bitmap getThumbnail(Bitmap original, int maxDimension) {
+ int width = original.getWidth();
+ int height = original.getHeight();
+ int scaledWidth, scaledHeight;
+ if (width >= height) {
+ float scaleFactor = (float) maxDimension / width;
+ scaledWidth = 200;
+ scaledHeight = (int) (scaleFactor * height);
+ } else {
+ float scaleFactor = (float) maxDimension / height;
+ scaledWidth = (int) (scaleFactor * width);
+ scaledHeight = 200;
+ }
+ Bitmap thumbnail = Bitmap.createScaledBitmap(original, scaledWidth, scaledHeight, true);
+
+ return thumbnail;
+ }
+
+
+}
diff --git a/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/PictureData.java b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/PictureData.java
new file mode 100644
index 0000000..eb2d126
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/PictureData.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2013 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.activityanim;
+
+import android.graphics.Bitmap;
+
+public class PictureData {
+ int resourceId;
+ String description;
+ Bitmap thumbnail;
+
+ public PictureData(int resourceId, String description, Bitmap thumbnail) {
+ this.resourceId = resourceId;
+ this.description = description;
+ this.thumbnail = thumbnail;
+ }
+}
diff --git a/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/PictureDetailsActivity.java b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/PictureDetailsActivity.java
new file mode 100644
index 0000000..e1674c9
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/PictureDetailsActivity.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2013 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.activityanim;
+
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+/**
+ * This sub-activity shows a zoomed-in view of a specific photo, along with the
+ * picture's text description. Most of the logic is for the animations that will
+ * be run when the activity is being launched and exited. When launching,
+ * the large version of the picture will resize from the thumbnail version in the
+ * main activity, colorizing it from the thumbnail's grayscale version at the
+ * same time. Meanwhile, the black background of the activity will fade in and
+ * the description will eventually slide into place. The exit animation runs all
+ * of this in reverse.
+ *
+ */
+public class PictureDetailsActivity extends Activity {
+
+ private static final TimeInterpolator sDecelerator = new DecelerateInterpolator();
+ private static final TimeInterpolator sAccelerator = new AccelerateInterpolator();
+ private static final String PACKAGE_NAME = "com.example.android.activityanim";
+ private static final int ANIM_DURATION = 500;
+
+ private BitmapDrawable mBitmapDrawable;
+ private ColorMatrix colorizerMatrix = new ColorMatrix();
+ ColorDrawable mBackground;
+ int mLeftDelta;
+ int mTopDelta;
+ float mWidthScale;
+ float mHeightScale;
+ private ImageView mImageView;
+ private TextView mTextView;
+ private FrameLayout mTopLevelLayout;
+ private ShadowLayout mShadowLayout;
+ private int mOriginalOrientation;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.picture_info);
+ mImageView = (ImageView) findViewById(R.id.imageView);
+ mTopLevelLayout = (FrameLayout) findViewById(R.id.topLevelLayout);
+ mShadowLayout = (ShadowLayout) findViewById(R.id.shadowLayout);
+ mTextView = (TextView) findViewById(R.id.description);
+
+ // Retrieve the data we need for the picture/description to display and
+ // the thumbnail to animate it from
+ Bundle bundle = getIntent().getExtras();
+ Bitmap bitmap = BitmapUtils.getBitmap(getResources(),
+ bundle.getInt(PACKAGE_NAME + ".resourceId"));
+ String description = bundle.getString(PACKAGE_NAME + ".description");
+ final int thumbnailTop = bundle.getInt(PACKAGE_NAME + ".top");
+ final int thumbnailLeft = bundle.getInt(PACKAGE_NAME + ".left");
+ final int thumbnailWidth = bundle.getInt(PACKAGE_NAME + ".width");
+ final int thumbnailHeight = bundle.getInt(PACKAGE_NAME + ".height");
+ mOriginalOrientation = bundle.getInt(PACKAGE_NAME + ".orientation");
+
+ mBitmapDrawable = new BitmapDrawable(getResources(), bitmap);
+ mImageView.setImageDrawable(mBitmapDrawable);
+ mTextView.setText(description);
+
+ mBackground = new ColorDrawable(Color.BLACK);
+ mTopLevelLayout.setBackground(mBackground);
+
+ // Only run the animation if we're coming from the parent activity, not if
+ // we're recreated automatically by the window manager (e.g., device rotation)
+ if (savedInstanceState == null) {
+ ViewTreeObserver observer = mImageView.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ mImageView.getViewTreeObserver().removeOnPreDrawListener(this);
+
+ // Figure out where the thumbnail and full size versions are, relative
+ // to the screen and each other
+ int[] screenLocation = new int[2];
+ mImageView.getLocationOnScreen(screenLocation);
+ mLeftDelta = thumbnailLeft - screenLocation[0];
+ mTopDelta = thumbnailTop - screenLocation[1];
+
+ // Scale factors to make the large version the same size as the thumbnail
+ mWidthScale = (float) thumbnailWidth / mImageView.getWidth();
+ mHeightScale = (float) thumbnailHeight / mImageView.getHeight();
+
+ runEnterAnimation();
+
+ return true;
+ }
+ });
+ }
+ }
+
+ /**
+ * The enter animation scales the picture in from its previous thumbnail
+ * size/location, colorizing it in parallel. In parallel, the background of the
+ * activity is fading in. When the pictue is in place, the text description
+ * drops down.
+ */
+ public void runEnterAnimation() {
+ final long duration = (long) (ANIM_DURATION * ActivityAnimations.sAnimatorScale);
+
+ // Set starting values for properties we're going to animate. These
+ // values scale and position the full size version down to the thumbnail
+ // size/location, from which we'll animate it back up
+ mImageView.setPivotX(0);
+ mImageView.setPivotY(0);
+ mImageView.setScaleX(mWidthScale);
+ mImageView.setScaleY(mHeightScale);
+ mImageView.setTranslationX(mLeftDelta);
+ mImageView.setTranslationY(mTopDelta);
+
+ // We'll fade the text in later
+ mTextView.setAlpha(0);
+
+ // Animate scale and translation to go from thumbnail to full size
+ mImageView.animate().setDuration(duration).
+ scaleX(1).scaleY(1).
+ translationX(0).translationY(0).
+ setInterpolator(sDecelerator).
+ withEndAction(new Runnable() {
+ public void run() {
+ // Animate the description in after the image animation
+ // is done. Slide and fade the text in from underneath
+ // the picture.
+ mTextView.setTranslationY(-mTextView.getHeight());
+ mTextView.animate().setDuration(duration/2).
+ translationY(0).alpha(1).
+ setInterpolator(sDecelerator);
+ }
+ });
+
+ // Fade in the black background
+ ObjectAnimator bgAnim = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
+ bgAnim.setDuration(duration);
+ bgAnim.start();
+
+ // Animate a color filter to take the image from grayscale to full color.
+ // This happens in parallel with the image scaling and moving into place.
+ ObjectAnimator colorizer = ObjectAnimator.ofFloat(PictureDetailsActivity.this,
+ "saturation", 0, 1);
+ colorizer.setDuration(duration);
+ colorizer.start();
+
+ // Animate a drop-shadow of the image
+ ObjectAnimator shadowAnim = ObjectAnimator.ofFloat(mShadowLayout, "shadowDepth", 0, 1);
+ shadowAnim.setDuration(duration);
+ shadowAnim.start();
+ }
+
+ /**
+ * The exit animation is basically a reverse of the enter animation, except that if
+ * the orientation has changed we simply scale the picture back into the center of
+ * the screen.
+ *
+ * @param endAction This action gets run after the animation completes (this is
+ * when we actually switch activities)
+ */
+ public void runExitAnimation(final Runnable endAction) {
+ final long duration = (long) (ANIM_DURATION * ActivityAnimations.sAnimatorScale);
+
+ // No need to set initial values for the reverse animation; the image is at the
+ // starting size/location that we want to start from. Just animate to the
+ // thumbnail size/location that we retrieved earlier
+
+ // Caveat: configuration change invalidates thumbnail positions; just animate
+ // the scale around the center. Also, fade it out since it won't match up with
+ // whatever's actually in the center
+ final boolean fadeOut;
+ if (getResources().getConfiguration().orientation != mOriginalOrientation) {
+ mImageView.setPivotX(mImageView.getWidth() / 2);
+ mImageView.setPivotY(mImageView.getHeight() / 2);
+ mLeftDelta = 0;
+ mTopDelta = 0;
+ fadeOut = true;
+ } else {
+ fadeOut = false;
+ }
+
+ // First, slide/fade text out of the way
+ mTextView.animate().translationY(-mTextView.getHeight()).alpha(0).
+ setDuration(duration/2).setInterpolator(sAccelerator).
+ withEndAction(new Runnable() {
+ public void run() {
+ // Animate image back to thumbnail size/location
+ mImageView.animate().setDuration(duration).
+ scaleX(mWidthScale).scaleY(mHeightScale).
+ translationX(mLeftDelta).translationY(mTopDelta).
+ withEndAction(endAction);
+ if (fadeOut) {
+ mImageView.animate().alpha(0);
+ }
+ // Fade out background
+ ObjectAnimator bgAnim = ObjectAnimator.ofInt(mBackground, "alpha", 0);
+ bgAnim.setDuration(duration);
+ bgAnim.start();
+
+ // Animate the shadow of the image
+ ObjectAnimator shadowAnim = ObjectAnimator.ofFloat(mShadowLayout,
+ "shadowDepth", 1, 0);
+ shadowAnim.setDuration(duration);
+ shadowAnim.start();
+
+ // Animate a color filter to take the image back to grayscale,
+ // in parallel with the image scaling and moving into place.
+ ObjectAnimator colorizer =
+ ObjectAnimator.ofFloat(PictureDetailsActivity.this,
+ "saturation", 1, 0);
+ colorizer.setDuration(duration);
+ colorizer.start();
+ }
+ });
+
+
+ }
+
+ /**
+ * Overriding this method allows us to run our exit animation first, then exiting
+ * the activity when it is complete.
+ */
+ @Override
+ public void onBackPressed() {
+ runExitAnimation(new Runnable() {
+ public void run() {
+ // *Now* go ahead and exit the activity
+ finish();
+ }
+ });
+ }
+
+ /**
+ * This is called by the colorizing animator. It sets a saturation factor that is then
+ * passed onto a filter on the picture's drawable.
+ * @param value
+ */
+ public void setSaturation(float value) {
+ colorizerMatrix.setSaturation(value);
+ ColorMatrixColorFilter colorizerFilter = new ColorMatrixColorFilter(colorizerMatrix);
+ mBitmapDrawable.setColorFilter(colorizerFilter);
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+
+ // override transitions to skip the standard window animations
+ overridePendingTransition(0, 0);
+ }
+}
diff --git a/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/ShadowLayout.java b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/ShadowLayout.java
new file mode 100644
index 0000000..b3bc961
--- /dev/null
+++ b/samples/devbytes/animation/ActivityAnimations/src/com/example/android/activityanim/ShadowLayout.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 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.activityanim;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.BlurMaskFilter.Blur;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+/**
+ * This custom layout paints a drop shadow behind all children. The size and opacity
+ * of the drop shadow is determined by a "depth" factor that can be set and animated.
+ */
+public class ShadowLayout extends RelativeLayout {
+
+ Paint mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ float mShadowDepth;
+ Bitmap mShadowBitmap;
+ static final int BLUR_RADIUS = 6;
+ static final RectF sShadowRectF = new RectF(0, 0, 200, 200);
+ static final Rect sShadowRect = new Rect(0, 0, 200 + 2 * BLUR_RADIUS, 200 + 2 * BLUR_RADIUS);
+ static RectF tempShadowRectF = new RectF(0, 0, 0, 0);
+
+ public ShadowLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ public ShadowLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public ShadowLayout(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Called by the constructors - sets up the drawing parameters for the drop shadow.
+ */
+ private void init() {
+ mShadowPaint.setColor(Color.BLACK);
+ mShadowPaint.setStyle(Style.FILL);
+ setWillNotDraw(false);
+ mShadowBitmap = Bitmap.createBitmap(sShadowRect.width(),
+ sShadowRect.height(), Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(mShadowBitmap);
+ mShadowPaint.setMaskFilter(new BlurMaskFilter(BLUR_RADIUS, Blur.NORMAL));
+ c.translate(BLUR_RADIUS, BLUR_RADIUS);
+ c.drawRoundRect(sShadowRectF, sShadowRectF.width() / 40,
+ sShadowRectF.height() / 40, mShadowPaint);
+ }
+
+ /**
+ * The "depth" factor determines the offset distance and opacity of the shadow (shadows that
+ * are further away from the source are offset greater and are more translucent).
+ * @param depth
+ */
+ public void setShadowDepth(float depth) {
+ if (depth != mShadowDepth) {
+ mShadowDepth = depth;
+ mShadowPaint.setAlpha((int) (100 + 150 * (1 - mShadowDepth)));
+ invalidate(); // We need to redraw when the shadow attributes change
+ }
+ }
+
+ /**
+ * Overriding onDraw allows us to draw shadows behind every child of this container.
+ * onDraw() is called to draw a layout's content before the children are drawn, so the
+ * shadows will be drawn first, behind the children (which is what we want).
+ */
+ @Override
+ protected void onDraw(Canvas canvas) {
+ for (int i = 0; i < getChildCount(); ++i) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != View.VISIBLE || child.getAlpha() == 0) {
+ continue;
+ }
+ int depthFactor = (int) (80 * mShadowDepth);
+ canvas.save();
+ canvas.translate(child.getLeft() + depthFactor,
+ child.getTop() + depthFactor);
+ canvas.concat(child.getMatrix());
+ tempShadowRectF.right = child.getWidth();
+ tempShadowRectF.bottom = child.getHeight();
+ canvas.drawBitmap(mShadowBitmap, sShadowRect, tempShadowRectF, mShadowPaint);
+ canvas.restore();
+ }
+ }
+
+
+}
diff --git a/samples/devbytes/animation/Anticipation/AndroidManifest.xml b/samples/devbytes/animation/Anticipation/AndroidManifest.xml
new file mode 100644
index 0000000..c941524
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.anticipation"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="14"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.example.android.anticipation.Anticipation"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/devbytes/animation/Anticipation/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/Anticipation/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/Anticipation/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/Anticipation/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/Anticipation/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/Anticipation/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/Anticipation/res/layout/main.xml b/samples/devbytes/animation/Anticipation/res/layout/main.xml
new file mode 100644
index 0000000..7da093f
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/res/layout/main.xml
@@ -0,0 +1,28 @@
+<!-- Copyright (C) 2013 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container"
+ android:clipChildren="false"
+ tools:context=".Anticipation" >
+
+ <view
+ class="com.example.android.anticipation.AnticiButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="AnticiButton"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/Anticipation/res/values-v14/styles.xml b/samples/devbytes/animation/Anticipation/res/values-v14/styles.xml
new file mode 100644
index 0000000..6e9521a
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/res/values-v14/styles.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/Anticipation/res/values/strings.xml b/samples/devbytes/animation/Anticipation/res/values/strings.xml
new file mode 100644
index 0000000..9097629
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">Anticipation</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="menu_settings">Settings</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/Anticipation/res/values/styles.xml b/samples/devbytes/animation/Anticipation/res/values/styles.xml
new file mode 100644
index 0000000..27658b7
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/res/values/styles.xml
@@ -0,0 +1,34 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/Anticipation/src/com/example/android/anticipation/AnticiButton.java b/samples/devbytes/animation/Anticipation/src/com/example/android/anticipation/AnticiButton.java
new file mode 100644
index 0000000..707765b
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/src/com/example/android/anticipation/AnticiButton.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2013 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.anticipation;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.OvershootInterpolator;
+import android.widget.Button;
+
+/**
+ * Custom button which can be deformed by skewing the top left and right, to simulate
+ * anticipation and follow-through animation effects. Clicking on the button runs
+ * an animation which moves the button left or right, applying the skew effect to the
+ * button. The logic of drawing the button with a skew transform is handled in the
+ * draw() override.
+ */
+public class AnticiButton extends Button {
+
+ private static final LinearInterpolator sLinearInterpolator = new LinearInterpolator();
+ private static final DecelerateInterpolator sDecelerator = new DecelerateInterpolator(8);
+ private static final AccelerateInterpolator sAccelerator = new AccelerateInterpolator();
+ private static final OvershootInterpolator sOvershooter = new OvershootInterpolator();
+ private static final DecelerateInterpolator sQuickDecelerator = new DecelerateInterpolator();
+
+ private float mSkewX = 0;
+ ObjectAnimator downAnim = null;
+ boolean mOnLeft = true;
+ RectF mTempRect = new RectF();
+
+ public AnticiButton(Context context) {
+ super(context);
+ init();
+ }
+
+ public AnticiButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ public AnticiButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ setOnTouchListener(mTouchListener);
+ setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ runClickAnim();
+ }
+ });
+ }
+
+ /**
+ * The skew effect is handled by changing the transform of the Canvas
+ * and then calling the usual superclass draw() method.
+ */
+ @Override
+ public void draw(Canvas canvas) {
+ if (mSkewX != 0) {
+ canvas.translate(0, getHeight());
+ canvas.skew(mSkewX, 0);
+ canvas.translate(0, -getHeight());
+ }
+ super.draw(canvas);
+ }
+
+ /**
+ * Anticipate the future animation by rearing back, away from the direction of travel
+ */
+ private void runPressAnim() {
+ downAnim = ObjectAnimator.ofFloat(this, "skewX", mOnLeft ? .5f : -.5f);
+ downAnim.setDuration(2500);
+ downAnim.setInterpolator(sDecelerator);
+ downAnim.start();
+ }
+
+ /**
+ * Finish the "anticipation" animation (skew the button back from the direction of
+ * travel), animate it to the other side of the screen, then un-skew the button
+ * with an Overshoot effect.
+ */
+ private void runClickAnim() {
+ // Anticipation
+ ObjectAnimator finishDownAnim = null;
+ if (downAnim != null && downAnim.isRunning()) {
+ // finish the skew animation quickly
+ downAnim.cancel();
+ finishDownAnim = ObjectAnimator.ofFloat(this, "skewX",
+ mOnLeft ? .5f : -.5f);
+ finishDownAnim.setDuration(150);
+ finishDownAnim.setInterpolator(sQuickDecelerator);
+ }
+
+ // Slide. Use LinearInterpolator in this rare situation where we want to start
+ // and end fast (no acceleration or deceleration, since we're doing that part
+ // during the anticipation and overshoot phases).
+ ObjectAnimator moveAnim = ObjectAnimator.ofFloat(this,
+ View.TRANSLATION_X, mOnLeft ? 400 : 0);
+ moveAnim.setInterpolator(sLinearInterpolator);
+ moveAnim.setDuration(150);
+
+ // Then overshoot by stopping the movement but skewing the button as if it couldn't
+ // all stop at once
+ ObjectAnimator skewAnim = ObjectAnimator.ofFloat(this, "skewX",
+ mOnLeft ? -.5f : .5f);
+ skewAnim.setInterpolator(sQuickDecelerator);
+ skewAnim.setDuration(100);
+ // and wobble it
+ ObjectAnimator wobbleAnim = ObjectAnimator.ofFloat(this, "skewX", 0);
+ wobbleAnim.setInterpolator(sOvershooter);
+ wobbleAnim.setDuration(150);
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(moveAnim, skewAnim, wobbleAnim);
+ if (finishDownAnim != null) {
+ set.play(finishDownAnim).before(moveAnim);
+ }
+ set.start();
+ mOnLeft = !mOnLeft;
+ }
+
+ /**
+ * Restore the button to its un-pressed state
+ */
+ private void runCancelAnim() {
+ if (downAnim != null && downAnim.isRunning()) {
+ downAnim.cancel();
+ ObjectAnimator reverser = ObjectAnimator.ofFloat(this, "skewX", 0);
+ reverser.setDuration(200);
+ reverser.setInterpolator(sAccelerator);
+ reverser.start();
+ downAnim = null;
+ }
+ }
+
+ /**
+ * Handle touch events directly since we want to react on down/up events, not just
+ * button clicks
+ */
+ private View.OnTouchListener mTouchListener = new View.OnTouchListener() {
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_UP:
+ if (isPressed()) {
+ performClick();
+ setPressed(false);
+ break;
+ }
+ // No click: Fall through; equivalent to cancel event
+ case MotionEvent.ACTION_CANCEL:
+ // Run the cancel animation in either case
+ runCancelAnim();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ float x = event.getX();
+ float y = event.getY();
+ boolean isInside = (x > 0 && x < getWidth() &&
+ y > 0 && y < getHeight());
+ if (isPressed() != isInside) {
+ setPressed(isInside);
+ }
+ break;
+ case MotionEvent.ACTION_DOWN:
+ setPressed(true);
+ runPressAnim();
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ };
+
+ public float getSkewX() {
+ return mSkewX;
+ }
+
+ /**
+ * Sets the amount of left/right skew on the button, which determines how far the button
+ * leans.
+ */
+ public void setSkewX(float value) {
+ if (value != mSkewX) {
+ mSkewX = value;
+ invalidate(); // force button to redraw with new skew value
+ invalidateSkewedBounds(); // also invalidate appropriate area of parent
+ }
+ }
+
+ /**
+ * Need to invalidate proper area of parent for skewed bounds
+ */
+ private void invalidateSkewedBounds() {
+ if (mSkewX != 0) {
+ Matrix matrix = new Matrix();
+ matrix.setSkew(-mSkewX, 0);
+ mTempRect.set(0, 0, getRight(), getBottom());
+ matrix.mapRect(mTempRect);
+ mTempRect.offset(getLeft() + getTranslationX(), getTop() + getTranslationY());
+ ((View) getParent()).invalidate((int) mTempRect.left, (int) mTempRect.top,
+ (int) (mTempRect.right +.5f), (int) (mTempRect.bottom + .5f));
+ }
+ }
+}
diff --git a/samples/devbytes/animation/Anticipation/src/com/example/android/anticipation/Anticipation.java b/samples/devbytes/animation/Anticipation/src/com/example/android/anticipation/Anticipation.java
new file mode 100644
index 0000000..4ef8f62
--- /dev/null
+++ b/samples/devbytes/animation/Anticipation/src/com/example/android/anticipation/Anticipation.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 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.anticipation;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+
+/**
+ * This example shows how to animate some simple deformations on a standard UI widget
+ * to achieve some interactive and cartoon-ish effects.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
+ * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
+ */
+public class Anticipation extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+
+}
diff --git a/samples/devbytes/animation/CurvedMotion/AndroidManifest.xml b/samples/devbytes/animation/CurvedMotion/AndroidManifest.xml
new file mode 100644
index 0000000..195faae
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.curvedmotion"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="11"
+ android:targetSdkVersion="11" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.example.android.curvedmotion.CurvedMotion"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CurvedMotion/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/CurvedMotion/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/CurvedMotion/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/CurvedMotion/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/CurvedMotion/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/CurvedMotion/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/CurvedMotion/res/layout/activity_curved_motion.xml b/samples/devbytes/animation/CurvedMotion/res/layout/activity_curved_motion.xml
new file mode 100644
index 0000000..10adea3
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/layout/activity_curved_motion.xml
@@ -0,0 +1,33 @@
+<!-- Copyright (C) 2013 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context=".CurvedMotion" >
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:id="@+id/button"
+ android:text="Click Me!" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CurvedMotion/res/values-v11/styles.xml b/samples/devbytes/animation/CurvedMotion/res/values-v11/styles.xml
new file mode 100644
index 0000000..556d2da
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/values-v11/styles.xml
@@ -0,0 +1,25 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CurvedMotion/res/values-v14/styles.xml b/samples/devbytes/animation/CurvedMotion/res/values-v14/styles.xml
new file mode 100644
index 0000000..6e9521a
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/values-v14/styles.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CurvedMotion/res/values/dimens.xml b/samples/devbytes/animation/CurvedMotion/res/values/dimens.xml
new file mode 100644
index 0000000..90db76b
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CurvedMotion/res/values/strings.xml b/samples/devbytes/animation/CurvedMotion/res/values/strings.xml
new file mode 100644
index 0000000..a070e15
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">CurvedMotion</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CurvedMotion/res/values/styles.xml b/samples/devbytes/animation/CurvedMotion/res/values/styles.xml
new file mode 100644
index 0000000..27658b7
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/res/values/styles.xml
@@ -0,0 +1,34 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/AnimatorPath.java b/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/AnimatorPath.java
new file mode 100644
index 0000000..d030c6a
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/AnimatorPath.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 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.curvedmotion;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * A simple Path object that holds information about the points along
+ * a path. The API allows you to specify a move location (which essentially
+ * jumps from the previous point in the path to the new one), a line location
+ * (which creates a line segment from the previous location) and a curve
+ * location (which creates a B�zier curve from the previous location).
+ */
+public class AnimatorPath {
+
+ // The points in the path
+ ArrayList<PathPoint> mPoints = new ArrayList<PathPoint>();
+
+
+ /**
+ * Move from the current path point to the new one
+ * specified by x and y. This will create a discontinuity if this point is
+ * neither the first point in the path nor the same as the previous point
+ * in the path.
+ */
+ public void moveTo(float x, float y) {
+ mPoints.add(PathPoint.moveTo(x, y));
+ }
+
+ /**
+ * Create a straight line from the current path point to the new one
+ * specified by x and y.
+ */
+ public void lineTo(float x, float y) {
+ mPoints.add(PathPoint.lineTo(x, y));
+ }
+
+ /**
+ * Create a quadratic B�zier curve from the current path point to the new one
+ * specified by x and y. The curve uses the current path location as the first anchor
+ * point, the control points (c0X, c0Y) and (c1X, c1Y), and (x, y) as the end anchor.
+ */
+ public void curveTo(float c0X, float c0Y, float c1X, float c1Y, float x, float y) {
+ mPoints.add(PathPoint.curveTo(c0X, c0Y, c1X, c1Y, x, y));
+ }
+
+ /**
+ * Returns a Collection of PathPoint objects that describe all points in the path.
+ */
+ public Collection<PathPoint> getPoints() {
+ return mPoints;
+ }
+}
diff --git a/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/CurvedMotion.java b/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/CurvedMotion.java
new file mode 100644
index 0000000..357d72d
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/CurvedMotion.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2013 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.curvedmotion;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+import android.widget.RelativeLayout.LayoutParams;
+
+/**
+ * This app shows how to move a view in a curved path between two endpoints.
+ * The real work is done by PathEvaluator, which interpolates along a path
+ * using Bezier control and anchor points in the path.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
+ * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
+ */
+public class CurvedMotion extends Activity {
+
+ private static final DecelerateInterpolator sDecelerateInterpolator =
+ new DecelerateInterpolator();
+ boolean mTopLeft = true;
+ Button mButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_curved_motion);
+
+ mButton = (Button) findViewById(R.id.button);
+ mButton.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ // Capture current location of button
+ final int oldLeft = mButton.getLeft();
+ final int oldTop = mButton.getTop();
+
+ // Change layout parameters of button to move it
+ moveButton();
+
+ // Add OnPreDrawListener to catch button after layout but before drawing
+ mButton.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ mButton.getViewTreeObserver().removeOnPreDrawListener(this);
+
+ // Capture new location
+ int left = mButton.getLeft();
+ int top = mButton.getTop();
+ int deltaX = left - oldLeft;
+ int deltaY = top - oldTop;
+
+ // Set up path to new location using a B�zier spline curve
+ AnimatorPath path = new AnimatorPath();
+ path.moveTo(-deltaX, -deltaY);
+ path.curveTo(-(deltaX/2), -deltaY, 0, -deltaY/2, 0, 0);
+
+ // Set up the animation
+ final ObjectAnimator anim = ObjectAnimator.ofObject(
+ CurvedMotion.this, "buttonLoc",
+ new PathEvaluator(), path.getPoints().toArray());
+ anim.setInterpolator(sDecelerateInterpolator);
+ anim.start();
+ return true;
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * Toggles button location on click between top-left and bottom-right
+ */
+ private void moveButton() {
+ LayoutParams params = (LayoutParams) mButton.getLayoutParams();
+ if (mTopLeft) {
+ params.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
+ params.removeRule(RelativeLayout.ALIGN_PARENT_TOP);
+ params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+ params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+ } else {
+ params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+ params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ params.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+ params.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+ }
+ mButton.setLayoutParams(params);
+ mTopLeft = !mTopLeft;
+ }
+
+ /**
+ * We need this setter to translate between the information the animator
+ * produces (a new "PathPoint" describing the current animated location)
+ * and the information that the button requires (an xy location). The
+ * setter will be called by the ObjectAnimator given the 'buttonLoc'
+ * property string.
+ */
+ public void setButtonLoc(PathPoint newLoc) {
+ mButton.setTranslationX(newLoc.mX);
+ mButton.setTranslationY(newLoc.mY);
+ }
+
+}
diff --git a/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/PathEvaluator.java b/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/PathEvaluator.java
new file mode 100644
index 0000000..e68d4cd
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/PathEvaluator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 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.curvedmotion;
+
+import android.animation.TypeEvaluator;
+
+/**
+ * This evaluator interpolates between two PathPoint values given the value t, the
+ * proportion traveled between those points. The value of the interpolation depends
+ * on the operation specified by the endValue (the operation for the interval between
+ * PathPoints is always specified by the end point of that interval).
+ */
+public class PathEvaluator implements TypeEvaluator<PathPoint> {
+ @Override
+ public PathPoint evaluate(float t, PathPoint startValue, PathPoint endValue) {
+ float x, y;
+ if (endValue.mOperation == PathPoint.CURVE) {
+ float oneMinusT = 1 - t;
+ x = oneMinusT * oneMinusT * oneMinusT * startValue.mX +
+ 3 * oneMinusT * oneMinusT * t * endValue.mControl0X +
+ 3 * oneMinusT * t * t * endValue.mControl1X +
+ t * t * t * endValue.mX;
+ y = oneMinusT * oneMinusT * oneMinusT * startValue.mY +
+ 3 * oneMinusT * oneMinusT * t * endValue.mControl0Y +
+ 3 * oneMinusT * t * t * endValue.mControl1Y +
+ t * t * t * endValue.mY;
+ } else if (endValue.mOperation == PathPoint.LINE) {
+ x = startValue.mX + t * (endValue.mX - startValue.mX);
+ y = startValue.mY + t * (endValue.mY - startValue.mY);
+ } else {
+ x = endValue.mX;
+ y = endValue.mY;
+ }
+ return PathPoint.moveTo(x, y);
+ }
+}
diff --git a/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/PathPoint.java b/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/PathPoint.java
new file mode 100644
index 0000000..ba1a05e
--- /dev/null
+++ b/samples/devbytes/animation/CurvedMotion/src/com/example/android/curvedmotion/PathPoint.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 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.curvedmotion;
+
+/**
+ * A class that holds information about a location and how the path should get to that
+ * location from the previous path location (if any). Any PathPoint holds the information for
+ * its location as well as the instructions on how to traverse the preceding interval from the
+ * previous location.
+ */
+public class PathPoint {
+
+ /**
+ * The possible path operations that describe how to move from a preceding PathPoint to the
+ * location described by this PathPoint.
+ */
+ public static final int MOVE = 0;
+ public static final int LINE = 1;
+ public static final int CURVE = 2;
+
+ /**
+ * The location of this PathPoint
+ */
+ float mX, mY;
+
+ /**
+ * The first control point, if any, for a PathPoint of type CURVE
+ */
+ float mControl0X, mControl0Y;
+
+ /**
+ * The second control point, if any, for a PathPoint of type CURVE
+ */
+ float mControl1X, mControl1Y;
+
+ /**
+ * The motion described by the path to get from the previous PathPoint in an AnimatorPath
+ * to the location of this PathPoint. This can be one of MOVE, LINE, or CURVE.
+ */
+ int mOperation;
+
+
+
+ /**
+ * Line/Move constructor
+ */
+ private PathPoint(int operation, float x, float y) {
+ mOperation = operation;
+ mX = x;
+ mY = y;
+ }
+
+ /**
+ * Curve constructor
+ */
+ private PathPoint(float c0X, float c0Y, float c1X, float c1Y, float x, float y) {
+ mControl0X = c0X;
+ mControl0Y = c0Y;
+ mControl1X = c1X;
+ mControl1Y = c1Y;
+ mX = x;
+ mY = y;
+ mOperation = CURVE;
+ }
+
+ /**
+ * Constructs and returns a PathPoint object that describes a line to the given xy location.
+ */
+ public static PathPoint lineTo(float x, float y) {
+ return new PathPoint(LINE, x, y);
+ }
+
+ /**
+ * Constructs and returns a PathPoint object that describes a curve to the given xy location
+ * with the control points at c0 and c1.
+ */
+ public static PathPoint curveTo(float c0X, float c0Y, float c1X, float c1Y, float x, float y) {
+ return new PathPoint(c0X, c0Y, c1X, c1Y, x, y);
+ }
+
+ /**
+ * Constructs and returns a PathPoint object that describes a discontinuous move to the given
+ * xy location.
+ */
+ public static PathPoint moveTo(float x, float y) {
+ return new PathPoint(MOVE, x, y);
+ }
+}
diff --git a/samples/devbytes/animation/ListViewItemAnimations/AndroidManifest.xml b/samples/devbytes/animation/ListViewItemAnimations/AndroidManifest.xml
new file mode 100644
index 0000000..173b188
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.listviewitemanimations"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="9"
+ android:targetSdkVersion="16" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:theme="@style/WithoutBackground"
+ android:name="com.example.android.listviewitemanimations.ListViewItemAnimations"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/shadowed_background.9.png b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/shadowed_background.9.png
new file mode 100644
index 0000000..6226b9e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/shadowed_background.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/tv_background_with_divider.9.png b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/tv_background_with_divider.9.png
new file mode 100644
index 0000000..e6e3cd5
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/tv_background_with_divider.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/layout-v9/activity_list_view_item_animations.xml b/samples/devbytes/animation/ListViewItemAnimations/res/layout-v9/activity_list_view_item_animations.xml
new file mode 100644
index 0000000..8290927
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/layout-v9/activity_list_view_item_animations.xml
@@ -0,0 +1,37 @@
+<!-- Copyright (C) 2013 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".ListViewAnimations" >
+
+ <view
+ class="com.example.android.listviewitemanimations.BackgroundContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/listViewBackground">
+
+ <ListView
+ android:id="@+id/listview"
+ android:divider="@null"
+ android:dividerHeight="0dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </view>
+
+</LinearLayout>
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/layout-v9/opaque_text_view.xml b/samples/devbytes/animation/ListViewItemAnimations/res/layout-v9/opaque_text_view.xml
new file mode 100644
index 0000000..aa03362
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/layout-v9/opaque_text_view.xml
@@ -0,0 +1,25 @@
+<!-- Copyright (C) 2013 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/tv_background_with_divider"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:textSize="20dp"
+ android:gravity="center_vertical"
+ android:minHeight="48dp"
+ />
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/layout/activity_list_view_item_animations.xml b/samples/devbytes/animation/ListViewItemAnimations/res/layout/activity_list_view_item_animations.xml
new file mode 100644
index 0000000..8290927
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/layout/activity_list_view_item_animations.xml
@@ -0,0 +1,37 @@
+<!-- Copyright (C) 2013 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".ListViewAnimations" >
+
+ <view
+ class="com.example.android.listviewitemanimations.BackgroundContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/listViewBackground">
+
+ <ListView
+ android:id="@+id/listview"
+ android:divider="@null"
+ android:dividerHeight="0dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </view>
+
+</LinearLayout>
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/layout/opaque_text_view.xml b/samples/devbytes/animation/ListViewItemAnimations/res/layout/opaque_text_view.xml
new file mode 100644
index 0000000..c37f62d
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/layout/opaque_text_view.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2013 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/tv_background_with_divider"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+/>
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/values-v11/styles.xml b/samples/devbytes/animation/ListViewItemAnimations/res/values-v11/styles.xml
new file mode 100644
index 0000000..556d2da
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/values-v11/styles.xml
@@ -0,0 +1,25 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/values-v14/styles.xml b/samples/devbytes/animation/ListViewItemAnimations/res/values-v14/styles.xml
new file mode 100644
index 0000000..6e9521a
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/values-v14/styles.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/values/dimens.xml b/samples/devbytes/animation/ListViewItemAnimations/res/values/dimens.xml
new file mode 100644
index 0000000..90db76b
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/values/strings.xml b/samples/devbytes/animation/ListViewItemAnimations/res/values/strings.xml
new file mode 100644
index 0000000..2cc5f71
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">ListViewItemAnimations</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/values/styles.xml b/samples/devbytes/animation/ListViewItemAnimations/res/values/styles.xml
new file mode 100644
index 0000000..ee3c56c
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/res/values/styles.xml
@@ -0,0 +1,38 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+ <style name="WithoutBackground" parent="AppTheme">
+ <item name="android:windowBackground">@null</item>
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/BackgroundContainer.java b/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/BackgroundContainer.java
new file mode 100644
index 0000000..40ac39a
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/BackgroundContainer.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 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.listviewitemanimations;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class BackgroundContainer extends FrameLayout {
+
+ boolean mShowing = false;
+ Drawable mShadowedBackground;
+ int mOpenAreaTop, mOpenAreaBottom, mOpenAreaHeight;
+ boolean mUpdateBounds = false;
+
+ public BackgroundContainer(Context context) {
+ super(context);
+ init();
+ }
+
+ public BackgroundContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public BackgroundContainer(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ mShadowedBackground = getContext().getResources().getDrawable(R.drawable.shadowed_background);
+ }
+
+ public void showBackground(int top, int bottom) {
+ setWillNotDraw(false);
+ mOpenAreaTop = top;
+ mOpenAreaHeight = bottom;
+ mShowing = true;
+ mUpdateBounds = true;
+ }
+
+ public void hideBackground() {
+ setWillNotDraw(true);
+ mShowing = false;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mShowing) {
+ if (mUpdateBounds) {
+ mShadowedBackground.setBounds(0, 0, getWidth(), mOpenAreaHeight);
+ }
+ canvas.save();
+ canvas.translate(0, mOpenAreaTop);
+ mShadowedBackground.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+}
diff --git a/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/Cheeses.java b/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/Cheeses.java
new file mode 100644
index 0000000..5241022
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/Cheeses.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2013 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.listviewitemanimations;
+
+public class Cheeses {
+
+ public static final String[] sCheeseStrings = {
+ "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
+ "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
+ "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
+ "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
+ "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
+ "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
+ "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
+ "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
+ "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
+ "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
+ "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
+ "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
+ "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
+ "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
+ "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
+ "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
+ "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
+ "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
+ "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
+ "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
+ "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
+ "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
+ "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
+ "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
+ "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
+ "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
+ "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
+ "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
+ "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
+ "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
+ "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
+ "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
+ "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
+ "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
+ "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
+ "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
+ "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
+ "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
+ "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
+ "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
+ "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
+ "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
+ "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
+ "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
+ "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
+ "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
+ "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
+ "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
+ "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
+ "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
+ "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
+ "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
+ "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
+ "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
+ "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
+ "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
+ "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
+ "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
+ "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
+ "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
+ "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
+ "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
+ "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
+ "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
+ "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
+ "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
+ "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
+ "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
+ "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
+ "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
+ "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
+ "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
+ "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
+ "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
+ "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
+ "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
+ "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
+ "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
+ "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
+ "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
+ "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
+ "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
+ "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
+ "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
+ "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
+ "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
+ "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
+ "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
+ "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
+ "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
+ "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
+ "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
+ "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
+ "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
+ "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
+ "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
+ "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
+ "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
+ "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
+ "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
+ "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
+ "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
+ "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
+ "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
+ "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
+ "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
+ "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
+ "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
+ "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
+ "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
+ "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
+ "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
+ "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
+ "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
+ "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
+ "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
+ "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
+ "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
+ "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
+ "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
+ "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
+ "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
+ "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
+ "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
+ "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
+ "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
+ "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
+ "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
+ "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
+ "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
+ };
+
+}
diff --git a/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/ListViewItemAnimations.java b/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/ListViewItemAnimations.java
new file mode 100644
index 0000000..68a7651
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/ListViewItemAnimations.java
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2013 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.listviewitemanimations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationSet;
+import android.view.animation.TranslateAnimation;
+import android.widget.ListView;
+
+/**
+ * This example shows how to use a swipe effect to remove items from a ListView,
+ * and how to use animations to complete the swipe as well as to animate the other
+ * items in the list into their final places. This code works on runtimes back to Gingerbread
+ * (Android 2.3), by using the android.view.animation classes on earlier releases.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
+ * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
+ */
+public class ListViewItemAnimations extends Activity {
+
+ final ArrayList<View> mCheckedViews = new ArrayList<View>();
+ StableArrayAdapter mAdapter;
+ ListView mListView;
+ BackgroundContainer mBackgroundContainer;
+ boolean mSwiping = false;
+ boolean mItemPressed = false;
+ HashMap<Long, Integer> mItemIdTopMap = new HashMap<Long, Integer>();
+ boolean mAnimating = false;
+ float mCurrentX = 0;
+ float mCurrentAlpha = 1;
+
+ private static final int SWIPE_DURATION = 250;
+ private static final int MOVE_DURATION = 150;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_list_view_item_animations);
+
+ mBackgroundContainer = (BackgroundContainer) findViewById(R.id.listViewBackground);
+ mListView = (ListView) findViewById(R.id.listview);
+ final ArrayList<String> cheeseList = new ArrayList<String>();
+ for (int i = 0; i < Cheeses.sCheeseStrings.length; ++i) {
+ cheeseList.add(Cheeses.sCheeseStrings[i]);
+ }
+ mAdapter = new StableArrayAdapter(this,R.layout.opaque_text_view, cheeseList,
+ mTouchListener);
+ mListView.setAdapter(mAdapter);
+ }
+
+ /**
+ * Returns true if the current runtime is Honeycomb or later
+ */
+ private boolean isRuntimePostGingerbread() {
+ return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB;
+ }
+
+ private View.OnTouchListener mTouchListener = new View.OnTouchListener() {
+
+ float mDownX;
+ private int mSwipeSlop = -1;
+
+ @SuppressLint("NewApi")
+ @Override
+ public boolean onTouch(final View v, MotionEvent event) {
+ if (mSwipeSlop < 0) {
+ mSwipeSlop = ViewConfiguration.get(ListViewItemAnimations.this).
+ getScaledTouchSlop();
+ }
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if (mAnimating) {
+ // Multi-item swipes not handled
+ return true;
+ }
+ mItemPressed = true;
+ mDownX = event.getX();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ setSwipePosition(v, 0);
+ mItemPressed = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ {
+ if (mAnimating) {
+ return true;
+ }
+ float x = event.getX();
+ if (isRuntimePostGingerbread()) {
+ x += v.getTranslationX();
+ }
+ float deltaX = x - mDownX;
+ float deltaXAbs = Math.abs(deltaX);
+ if (!mSwiping) {
+ if (deltaXAbs > mSwipeSlop) {
+ mSwiping = true;
+ mListView.requestDisallowInterceptTouchEvent(true);
+ mBackgroundContainer.showBackground(v.getTop(), v.getHeight());
+ }
+ }
+ if (mSwiping) {
+ setSwipePosition(v, deltaX);
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ {
+ if (mAnimating) {
+ return true;
+ }
+ // User let go - figure out whether to animate the view out, or back into place
+ if (mSwiping) {
+ float x = event.getX();
+ if (isRuntimePostGingerbread()) {
+ x += v.getTranslationX();
+ }
+ float deltaX = x - mDownX;
+ float deltaXAbs = Math.abs(deltaX);
+ float fractionCovered;
+ float endX;
+ final boolean remove;
+ if (deltaXAbs > v.getWidth() / 4) {
+ // Greater than a quarter of the width - animate it out
+ fractionCovered = deltaXAbs / v.getWidth();
+ endX = deltaX < 0 ? -v.getWidth() : v.getWidth();
+ remove = true;
+ } else {
+ // Not far enough - animate it back
+ fractionCovered = 1 - (deltaXAbs / v.getWidth());
+ endX = 0;
+ remove = false;
+ }
+ // Animate position and alpha
+ long duration = (int) ((1 - fractionCovered) * SWIPE_DURATION);
+ animateSwipe(v, endX, duration, remove);
+ } else {
+ mItemPressed = false;
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ };
+
+ /**
+ * Animates a swipe of the item either back into place or out of the listview container.
+ * NOTE: This is a simplified version of swipe behavior, for the purposes of this demo
+ * about animation. A real version should use velocity (via the VelocityTracker class)
+ * to send the item off or back at an appropriate speed.
+ */
+ @SuppressLint("NewApi")
+ private void animateSwipe(final View view, float endX, long duration, final boolean remove) {
+ mAnimating = true;
+ mListView.setEnabled(false);
+ if (isRuntimePostGingerbread()) {
+ view.animate().setDuration(duration).
+ alpha(remove ? 0 : 1).translationX(endX).
+ setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Restore animated values
+ view.setAlpha(1);
+ view.setTranslationX(0);
+ if (remove) {
+ animateOtherViews(mListView, view);
+ } else {
+ mBackgroundContainer.hideBackground();
+ mSwiping = false;
+ mAnimating = false;
+ mListView.setEnabled(true);
+ }
+ mItemPressed = false;
+ }
+ });
+ } else {
+ TranslateAnimation swipeAnim = new TranslateAnimation(mCurrentX, endX, 0, 0);
+ AlphaAnimation alphaAnim = new AlphaAnimation(mCurrentAlpha, remove ? 0 : 1);
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(swipeAnim);
+ set.addAnimation(alphaAnim);
+ set.setDuration(duration);
+ view.startAnimation(set);
+ setAnimationEndAction(set, new Runnable() {
+ @Override
+ public void run() {
+ if (remove) {
+ animateOtherViews(mListView, view);
+ } else {
+ mBackgroundContainer.hideBackground();
+ mSwiping = false;
+ mAnimating = false;
+ mListView.setEnabled(true);
+ }
+ mItemPressed = false;
+ }
+ });
+ }
+
+ }
+
+ /**
+ * Sets the horizontal position and translucency of the view being swiped.
+ */
+ @SuppressLint("NewApi")
+ private void setSwipePosition(View view, float deltaX) {
+ float fraction = Math.abs(deltaX) / view.getWidth();
+ if (isRuntimePostGingerbread()) {
+ view.setTranslationX(deltaX);
+ view.setAlpha(1 - fraction);
+ } else {
+ // Hello, Gingerbread!
+ TranslateAnimation swipeAnim = new TranslateAnimation(deltaX, deltaX, 0, 0);
+ mCurrentX = deltaX;
+ mCurrentAlpha = (1 - fraction);
+ AlphaAnimation alphaAnim = new AlphaAnimation(mCurrentAlpha, mCurrentAlpha);
+ AnimationSet set = new AnimationSet(true);
+ set.addAnimation(swipeAnim);
+ set.addAnimation(alphaAnim);
+ set.setFillAfter(true);
+ set.setFillEnabled(true);
+ view.startAnimation(set);
+ }
+ }
+
+ /**
+ * This method animates all other views in the ListView container (not including ignoreView)
+ * into their final positions. It is called after ignoreView has been removed from the
+ * adapter, but before layout has been run. The approach here is to figure out where
+ * everything is now, then allow layout to run, then figure out where everything is after
+ * layout, and then to run animations between all of those start/end positions.
+ */
+ private void animateOtherViews(final ListView listview, View viewToRemove) {
+ int firstVisiblePosition = listview.getFirstVisiblePosition();
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ View child = listview.getChildAt(i);
+ int position = firstVisiblePosition + i;
+ long itemId = mAdapter.getItemId(position);
+ if (child != viewToRemove) {
+ mItemIdTopMap.put(itemId, child.getTop());
+ }
+ }
+ // Delete the item from the adapter
+ int position = mListView.getPositionForView(viewToRemove);
+ mAdapter.remove(mAdapter.getItem(position));
+
+ // After layout runs, capture position of all itemIDs, compare to pre-layout
+ // positions, and animate changes
+ final ViewTreeObserver observer = listview.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ observer.removeOnPreDrawListener(this);
+ boolean firstAnimation = true;
+ int firstVisiblePosition = listview.getFirstVisiblePosition();
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ final View child = listview.getChildAt(i);
+ int position = firstVisiblePosition + i;
+ long itemId = mAdapter.getItemId(position);
+ Integer startTop = mItemIdTopMap.get(itemId);
+ int top = child.getTop();
+ if (startTop == null) {
+ // Animate new views along with the others. The catch is that they did not
+ // exist in the start state, so we must calculate their starting position
+ // based on whether they're coming in from the bottom (i > 0) or top.
+ int childHeight = child.getHeight() + listview.getDividerHeight();
+ startTop = top + (i > 0 ? childHeight : -childHeight);
+ }
+ int delta = startTop - top;
+ if (delta != 0) {
+ Runnable endAction = firstAnimation ?
+ new Runnable() {
+ public void run() {
+ mBackgroundContainer.hideBackground();
+ mSwiping = false;
+ mAnimating = false;
+ mListView.setEnabled(true);
+ }
+ } :
+ null;
+ firstAnimation = false;
+ moveView(child, 0, 0, delta, 0, endAction);
+ }
+ }
+ mItemIdTopMap.clear();
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Animate a view between start and end X/Y locations, using either old (pre-3.0) or
+ * new animation APIs.
+ */
+ @SuppressLint("NewApi")
+ private void moveView(View view, float startX, float endX, float startY, float endY,
+ Runnable endAction) {
+ final Runnable finalEndAction = endAction;
+ if (isRuntimePostGingerbread()) {
+ view.animate().setDuration(MOVE_DURATION);
+ if (startX != endX) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, startX, endX);
+ anim.setDuration(MOVE_DURATION);
+ anim.start();
+ setAnimatorEndAction(anim, endAction);
+ endAction = null;
+ }
+ if (startY != endY) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
+ anim.setDuration(MOVE_DURATION);
+ anim.start();
+ setAnimatorEndAction(anim, endAction);
+ }
+ } else {
+ TranslateAnimation translator = new TranslateAnimation(startX, endX, startY, endY);
+ translator.setDuration(MOVE_DURATION);
+ view.startAnimation(translator);
+ if (endAction != null) {
+ view.getAnimation().setAnimationListener(new AnimationListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ finalEndAction.run();
+ }
+ });
+ }
+ }
+ }
+
+ @SuppressLint("NewApi")
+ private void setAnimatorEndAction(Animator animator, final Runnable endAction) {
+ if (endAction != null) {
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endAction.run();
+ }
+ });
+ }
+ }
+
+ private void setAnimationEndAction(Animation animation, final Runnable endAction) {
+ if (endAction != null) {
+ animation.setAnimationListener(new AnimationListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ endAction.run();
+ }
+ });
+ }
+ }
+
+ /**
+ * Utility, to avoid having to implement every method in AnimationListener in
+ * every implementation class
+ */
+ static class AnimationListenerAdapter implements AnimationListener {
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+ }
+
+}
diff --git a/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/StableArrayAdapter.java b/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/StableArrayAdapter.java
new file mode 100644
index 0000000..4bddc5e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewItemAnimations/src/com/example/android/listviewitemanimations/StableArrayAdapter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 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.listviewitemanimations;
+
+import java.util.HashMap;
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+public class StableArrayAdapter extends ArrayAdapter<String> {
+
+ HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
+ View.OnTouchListener mTouchListener;
+
+ public StableArrayAdapter(Context context, int textViewResourceId,
+ List<String> objects, View.OnTouchListener listener) {
+ super(context, textViewResourceId, objects);
+ mTouchListener = listener;
+ for (int i = 0; i < objects.size(); ++i) {
+ mIdMap.put(objects.get(i), i);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ String item = getItem(position);
+ return mIdMap.get(item);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = super.getView(position, convertView, parent);
+ if (view != convertView) {
+ // Add touch listener to every new view to track swipe motion
+ view.setOnTouchListener(mTouchListener);
+ }
+ return view;
+ }
+
+}
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/AndroidManifest.xml b/samples/devbytes/animation/ListViewRemovalAnimation/AndroidManifest.xml
new file mode 100644
index 0000000..f030747
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.listviewremovalanimation"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="16"
+ android:targetSdkVersion="16" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:theme="@style/WithoutBackground"
+ android:name="com.example.android.listviewremovalanimation.ListViewRemovalAnimation"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/shadowed_background.9.png b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/shadowed_background.9.png
new file mode 100644
index 0000000..f905bbc
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/shadowed_background.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/tv_background_with_divider.9.png b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/tv_background_with_divider.9.png
new file mode 100644
index 0000000..e6e3cd5
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/tv_background_with_divider.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/layout/activity_list_view_deletion.xml b/samples/devbytes/animation/ListViewRemovalAnimation/res/layout/activity_list_view_deletion.xml
new file mode 100644
index 0000000..26aa123
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/layout/activity_list_view_deletion.xml
@@ -0,0 +1,37 @@
+<!-- Copyright (C) 2013 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".ListViewAnimations" >
+
+ <view
+ class="com.example.android.listviewremovalanimation.BackgroundContainer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/listViewBackground">
+
+ <ListView
+ android:id="@+id/listview"
+ android:divider="@null"
+ android:dividerHeight="0dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </view>
+
+</LinearLayout>
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/layout/opaque_text_view.xml b/samples/devbytes/animation/ListViewRemovalAnimation/res/layout/opaque_text_view.xml
new file mode 100644
index 0000000..c37f62d
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/layout/opaque_text_view.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2013 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/tv_background_with_divider"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+/>
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/values-v11/styles.xml b/samples/devbytes/animation/ListViewRemovalAnimation/res/values-v11/styles.xml
new file mode 100644
index 0000000..139d283
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/values-v11/styles.xml
@@ -0,0 +1,25 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/values-v14/styles.xml b/samples/devbytes/animation/ListViewRemovalAnimation/res/values-v14/styles.xml
new file mode 100644
index 0000000..8ac4522
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/values-v14/styles.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/values/strings.xml b/samples/devbytes/animation/ListViewRemovalAnimation/res/values/strings.xml
new file mode 100644
index 0000000..7125618
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/values/strings.xml
@@ -0,0 +1,21 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">ListViewRemovalAnimation</string>
+ <string name="use_positions">Use Positions</string>
+ <string name="delete_selected">Delete Selected</string>
+
+</resources>
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/values/styles.xml b/samples/devbytes/animation/ListViewRemovalAnimation/res/values/styles.xml
new file mode 100644
index 0000000..c7f2e79
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/values/styles.xml
@@ -0,0 +1,37 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+ <style name="WithoutBackground" parent="AppTheme">
+ <item name="android:windowBackground">@null</item>
+ </style>
+</resources>
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/BackgroundContainer.java b/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/BackgroundContainer.java
new file mode 100644
index 0000000..1d0e806
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/BackgroundContainer.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 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.listviewremovalanimation;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class BackgroundContainer extends FrameLayout {
+
+ boolean mShowing = false;
+ Drawable mShadowedBackground;
+ int mOpenAreaTop, mOpenAreaBottom, mOpenAreaHeight;
+ boolean mUpdateBounds = false;
+
+ public BackgroundContainer(Context context) {
+ super(context);
+ init();
+ }
+
+ public BackgroundContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public BackgroundContainer(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ mShadowedBackground =
+ getContext().getResources().getDrawable(R.drawable.shadowed_background);
+ }
+
+ public void showBackground(int top, int bottom) {
+ setWillNotDraw(false);
+ mOpenAreaTop = top;
+ mOpenAreaHeight = bottom;
+ mShowing = true;
+ mUpdateBounds = true;
+ }
+
+ public void hideBackground() {
+ setWillNotDraw(true);
+ mShowing = false;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mShowing) {
+ if (mUpdateBounds) {
+ mShadowedBackground.setBounds(0, 0, getWidth(), mOpenAreaHeight);
+ }
+ canvas.save();
+ canvas.translate(0, mOpenAreaTop);
+ mShadowedBackground.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+}
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/Cheeses.java b/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/Cheeses.java
new file mode 100644
index 0000000..4f4b88b
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/Cheeses.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2013 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.listviewremovalanimation;
+
+public class Cheeses {
+
+ public static final String[] sCheeseStrings = {
+ "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
+ "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
+ "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese",
+ "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh", "Anthoriro", "Appenzell",
+ "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String", "Aromes au Gene de Marc",
+ "Asadero", "Asiago", "Aubisque Pyrenees", "Autun", "Avaxtskyr", "Baby Swiss",
+ "Babybel", "Baguette Laonnaise", "Bakers", "Baladi", "Balaton", "Bandal", "Banon",
+ "Barry's Bay Cheddar", "Basing", "Basket Cheese", "Bath Cheese", "Bavarian Bergkase",
+ "Baylough", "Beaufort", "Beauvoorde", "Beenleigh Blue", "Beer Cheese", "Bel Paese",
+ "Bergader", "Bergere Bleue", "Berkswell", "Beyaz Peynir", "Bierkase", "Bishop Kennedy",
+ "Blarney", "Bleu d'Auvergne", "Bleu de Gex", "Bleu de Laqueuille",
+ "Bleu de Septmoncel", "Bleu Des Causses", "Blue", "Blue Castello", "Blue Rathgore",
+ "Blue Vein (Australian)", "Blue Vein Cheeses", "Bocconcini", "Bocconcini (Australian)",
+ "Boeren Leidenkaas", "Bonchester", "Bosworth", "Bougon", "Boule Du Roves",
+ "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou", "Bra", "Braudostur",
+ "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois", "Brebis du Puyfaucon",
+ "Bresse Bleu", "Brick", "Brie", "Brie de Meaux", "Brie de Melun", "Brillat-Savarin",
+ "Brin", "Brin d' Amour", "Brin d'Amour", "Brinza (Burduf Brinza)",
+ "Briquette de Brebis", "Briquette du Forez", "Broccio", "Broccio Demi-Affine",
+ "Brousse du Rove", "Bruder Basil", "Brusselae Kaas (Fromage de Bruxelles)", "Bryndza",
+ "Buchette d'Anjou", "Buffalo", "Burgos", "Butte", "Butterkase", "Button (Innes)",
+ "Buxton Blue", "Cabecou", "Caboc", "Cabrales", "Cachaille", "Caciocavallo", "Caciotta",
+ "Caerphilly", "Cairnsmore", "Calenzana", "Cambazola", "Camembert de Normandie",
+ "Canadian Cheddar", "Canestrato", "Cantal", "Caprice des Dieux", "Capricorn Goat",
+ "Capriole Banon", "Carre de l'Est", "Casciotta di Urbino", "Cashel Blue", "Castellano",
+ "Castelleno", "Castelmagno", "Castelo Branco", "Castigliano", "Cathelain",
+ "Celtic Promise", "Cendre d'Olivet", "Cerney", "Chabichou", "Chabichou du Poitou",
+ "Chabis de Gatine", "Chaource", "Charolais", "Chaumes", "Cheddar",
+ "Cheddar Clothbound", "Cheshire", "Chevres", "Chevrotin des Aravis", "Chontaleno",
+ "Civray", "Coeur de Camembert au Calvados", "Coeur de Chevre", "Colby", "Cold Pack",
+ "Comte", "Coolea", "Cooleney", "Coquetdale", "Corleggy", "Cornish Pepper",
+ "Cotherstone", "Cotija", "Cottage Cheese", "Cottage Cheese (Australian)",
+ "Cougar Gold", "Coulommiers", "Coverdale", "Crayeux de Roncq", "Cream Cheese",
+ "Cream Havarti", "Crema Agria", "Crema Mexicana", "Creme Fraiche", "Crescenza",
+ "Croghan", "Crottin de Chavignol", "Crottin du Chavignol", "Crowdie", "Crowley",
+ "Cuajada", "Curd", "Cure Nantais", "Curworthy", "Cwmtawe Pecorino",
+ "Cypress Grove Chevre", "Danablu (Danish Blue)", "Danbo", "Danish Fontina",
+ "Daralagjazsky", "Dauphin", "Delice des Fiouves", "Denhany Dorset Drum", "Derby",
+ "Dessertnyj Belyj", "Devon Blue", "Devon Garland", "Dolcelatte", "Doolin",
+ "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester", "Double Worcester",
+ "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra", "Dunlop", "Dunsyre Blue",
+ "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)", "Edam", "Edelpilz",
+ "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne", "Esbareich",
+ "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo", "Exmoor Blue",
+ "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta", "Fin-de-Siecle",
+ "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis", "Flor de Guia",
+ "Flower Marie", "Folded", "Folded cheese with mint", "Fondant de Brebis",
+ "Fontainebleau", "Fontal", "Fontina Val d'Aosta", "Formaggio di capra", "Fougerus",
+ "Four Herb Gouda", "Fourme d' Ambert", "Fourme de Haute Loire", "Fourme de Montbrison",
+ "Fresh Jack", "Fresh Mozzarella", "Fresh Ricotta", "Fresh Truffles", "Fribourgeois",
+ "Friesekaas", "Friesian", "Friesla", "Frinault", "Fromage a Raclette", "Fromage Corse",
+ "Fromage de Montagne de Savoie", "Fromage Frais", "Fruit Cream Cheese",
+ "Frying Cheese", "Fynbo", "Gabriel", "Galette du Paludier", "Galette Lyonnaise",
+ "Galloway Goat's Milk Gems", "Gammelost", "Gaperon a l'Ail", "Garrotxa", "Gastanberra",
+ "Geitost", "Gippsland Blue", "Gjetost", "Gloucester", "Golden Cross", "Gorgonzola",
+ "Gornyaltajski", "Gospel Green", "Gouda", "Goutu", "Gowrie", "Grabetto", "Graddost",
+ "Grafton Village Cheddar", "Grana", "Grana Padano", "Grand Vatel",
+ "Grataron d' Areches", "Gratte-Paille", "Graviera", "Greuilh", "Greve",
+ "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
+ "Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
+ "Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
+ "Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
+ "Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
+ "Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
+ "Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
+ "Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
+ "Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
+ "La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
+ "Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
+ "Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
+ "Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
+ "Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
+ "Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
+ "Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
+ "Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
+ "Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
+ "Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
+ "Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
+ "Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
+ "Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
+ "Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
+ "Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
+ "Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
+ "Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
+ "Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
+ "Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
+ "Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
+ "Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
+ "Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
+ "Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
+ "Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
+ "Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
+ "Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
+ "Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
+ "Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
+ "Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
+ "Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
+ "Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
+ "Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
+ "Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
+ "Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
+ "Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
+ "Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
+ "Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
+ "Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
+ "Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
+ "Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
+ "Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
+ "Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
+ "Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
+ "Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
+ "Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
+ "Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
+ "Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
+ "Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
+ "Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
+ "Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
+ "Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
+ "Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
+ "Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
+ "Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
+ "Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
+ "Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
+ "Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
+ "Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
+ "Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
+ "Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
+ "Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
+ "Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
+ "Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano"
+ };
+
+}
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/ListViewRemovalAnimation.java b/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/ListViewRemovalAnimation.java
new file mode 100644
index 0000000..1e3ee8c
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/ListViewRemovalAnimation.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2013 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.listviewremovalanimation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.widget.ListView;
+
+/**
+ * This example shows how to use a swipe effect to remove items from a ListView,
+ * and how to use animations to complete the swipe as well as to animate the other
+ * items in the list into their final places.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on YouTube at https://www.youtube.com/watch?v=NewCSg2JKLk.
+ */
+public class ListViewRemovalAnimation extends Activity {
+
+ StableArrayAdapter mAdapter;
+ ListView mListView;
+ BackgroundContainer mBackgroundContainer;
+ boolean mSwiping = false;
+ boolean mItemPressed = false;
+ HashMap<Long, Integer> mItemIdTopMap = new HashMap<Long, Integer>();
+
+ private static final int SWIPE_DURATION = 250;
+ private static final int MOVE_DURATION = 150;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_list_view_deletion);
+
+ mBackgroundContainer = (BackgroundContainer) findViewById(R.id.listViewBackground);
+ mListView = (ListView) findViewById(R.id.listview);
+ android.util.Log.d("Debug", "d=" + mListView.getDivider());
+ final ArrayList<String> cheeseList = new ArrayList<String>();
+ for (int i = 0; i < Cheeses.sCheeseStrings.length; ++i) {
+ cheeseList.add(Cheeses.sCheeseStrings[i]);
+ }
+ mAdapter = new StableArrayAdapter(this,R.layout.opaque_text_view, cheeseList,
+ mTouchListener);
+ mListView.setAdapter(mAdapter);
+ }
+
+ /**
+ * Handle touch events to fade/move dragged items as they are swiped out
+ */
+ private View.OnTouchListener mTouchListener = new View.OnTouchListener() {
+
+ float mDownX;
+ private int mSwipeSlop = -1;
+
+ @Override
+ public boolean onTouch(final View v, MotionEvent event) {
+ if (mSwipeSlop < 0) {
+ mSwipeSlop = ViewConfiguration.get(ListViewRemovalAnimation.this).
+ getScaledTouchSlop();
+ }
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if (mItemPressed) {
+ // Multi-item swipes not handled
+ return false;
+ }
+ mItemPressed = true;
+ mDownX = event.getX();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ v.setAlpha(1);
+ v.setTranslationX(0);
+ mItemPressed = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ {
+ float x = event.getX() + v.getTranslationX();
+ float deltaX = x - mDownX;
+ float deltaXAbs = Math.abs(deltaX);
+ if (!mSwiping) {
+ if (deltaXAbs > mSwipeSlop) {
+ mSwiping = true;
+ mListView.requestDisallowInterceptTouchEvent(true);
+ mBackgroundContainer.showBackground(v.getTop(), v.getHeight());
+ }
+ }
+ if (mSwiping) {
+ v.setTranslationX((x - mDownX));
+ v.setAlpha(1 - deltaXAbs / v.getWidth());
+ }
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ {
+ // User let go - figure out whether to animate the view out, or back into place
+ if (mSwiping) {
+ float x = event.getX() + v.getTranslationX();
+ float deltaX = x - mDownX;
+ float deltaXAbs = Math.abs(deltaX);
+ float fractionCovered;
+ float endX;
+ float endAlpha;
+ final boolean remove;
+ if (deltaXAbs > v.getWidth() / 4) {
+ // Greater than a quarter of the width - animate it out
+ fractionCovered = deltaXAbs / v.getWidth();
+ endX = deltaX < 0 ? -v.getWidth() : v.getWidth();
+ endAlpha = 0;
+ remove = true;
+ } else {
+ // Not far enough - animate it back
+ fractionCovered = 1 - (deltaXAbs / v.getWidth());
+ endX = 0;
+ endAlpha = 1;
+ remove = false;
+ }
+ // Animate position and alpha of swiped item
+ // NOTE: This is a simplified version of swipe behavior, for the
+ // purposes of this demo about animation. A real version should use
+ // velocity (via the VelocityTracker class) to send the item off or
+ // back at an appropriate speed.
+ long duration = (int) ((1 - fractionCovered) * SWIPE_DURATION);
+ mListView.setEnabled(false);
+ v.animate().setDuration(duration).
+ alpha(endAlpha).translationX(endX).
+ withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ // Restore animated values
+ v.setAlpha(1);
+ v.setTranslationX(0);
+ if (remove) {
+ animateRemoval(mListView, v);
+ } else {
+ mBackgroundContainer.hideBackground();
+ mSwiping = false;
+ mListView.setEnabled(true);
+ }
+ }
+ });
+ }
+ }
+ mItemPressed = false;
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ };
+
+ /**
+ * This method animates all other views in the ListView container (not including ignoreView)
+ * into their final positions. It is called after ignoreView has been removed from the
+ * adapter, but before layout has been run. The approach here is to figure out where
+ * everything is now, then allow layout to run, then figure out where everything is after
+ * layout, and then to run animations between all of those start/end positions.
+ */
+ private void animateRemoval(final ListView listview, View viewToRemove) {
+ int firstVisiblePosition = listview.getFirstVisiblePosition();
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ View child = listview.getChildAt(i);
+ if (child != viewToRemove) {
+ int position = firstVisiblePosition + i;
+ long itemId = mAdapter.getItemId(position);
+ mItemIdTopMap.put(itemId, child.getTop());
+ }
+ }
+ // Delete the item from the adapter
+ int position = mListView.getPositionForView(viewToRemove);
+ mAdapter.remove(mAdapter.getItem(position));
+
+ final ViewTreeObserver observer = listview.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ observer.removeOnPreDrawListener(this);
+ boolean firstAnimation = true;
+ int firstVisiblePosition = listview.getFirstVisiblePosition();
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ final View child = listview.getChildAt(i);
+ int position = firstVisiblePosition + i;
+ long itemId = mAdapter.getItemId(position);
+ Integer startTop = mItemIdTopMap.get(itemId);
+ int top = child.getTop();
+ if (startTop != null) {
+ if (startTop != top) {
+ int delta = startTop - top;
+ child.setTranslationY(delta);
+ child.animate().setDuration(MOVE_DURATION).translationY(0);
+ if (firstAnimation) {
+ child.animate().withEndAction(new Runnable() {
+ public void run() {
+ mBackgroundContainer.hideBackground();
+ mSwiping = false;
+ mListView.setEnabled(true);
+ }
+ });
+ firstAnimation = false;
+ }
+ }
+ } else {
+ // Animate new views along with the others. The catch is that they did not
+ // exist in the start state, so we must calculate their starting position
+ // based on neighboring views.
+ int childHeight = child.getHeight() + listview.getDividerHeight();
+ startTop = top + (i > 0 ? childHeight : -childHeight);
+ int delta = startTop - top;
+ child.setTranslationY(delta);
+ child.animate().setDuration(MOVE_DURATION).translationY(0);
+ if (firstAnimation) {
+ child.animate().withEndAction(new Runnable() {
+ public void run() {
+ mBackgroundContainer.hideBackground();
+ mSwiping = false;
+ mListView.setEnabled(true);
+ }
+ });
+ firstAnimation = false;
+ }
+ }
+ }
+ mItemIdTopMap.clear();
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/StableArrayAdapter.java b/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/StableArrayAdapter.java
new file mode 100644
index 0000000..948676e
--- /dev/null
+++ b/samples/devbytes/animation/ListViewRemovalAnimation/src/com/example/android/listviewremovalanimation/StableArrayAdapter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 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.listviewremovalanimation;
+
+import java.util.HashMap;
+import java.util.List;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+public class StableArrayAdapter extends ArrayAdapter<String> {
+
+ HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
+ View.OnTouchListener mTouchListener;
+
+ public StableArrayAdapter(Context context, int textViewResourceId,
+ List<String> objects, View.OnTouchListener listener) {
+ super(context, textViewResourceId, objects);
+ mTouchListener = listener;
+ for (int i = 0; i < objects.size(); ++i) {
+ mIdMap.put(objects.get(i), i);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ String item = getItem(position);
+ return mIdMap.get(item);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = super.getView(position, convertView, parent);
+ if (view != convertView) {
+ // Add touch listener to every new view to track swipe motion
+ view.setOnTouchListener(mTouchListener);
+ }
+ return view;
+ }
+
+}
diff --git a/samples/devbytes/animation/LiveButton/AndroidManifest.xml b/samples/devbytes/animation/LiveButton/AndroidManifest.xml
new file mode 100644
index 0000000..280c2f8
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.livebutton"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="14"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.example.android.livebutton.LiveButton"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/devbytes/animation/LiveButton/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/LiveButton/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/LiveButton/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/LiveButton/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/LiveButton/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/LiveButton/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/LiveButton/res/layout/activity_overshoot.xml b/samples/devbytes/animation/LiveButton/res/layout/activity_overshoot.xml
new file mode 100644
index 0000000..6900650
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/res/layout/activity_overshoot.xml
@@ -0,0 +1,29 @@
+<!-- Copyright (C) 2013 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".LiveButton" >
+
+ <Button
+ android:id="@+id/clickMe"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Click me!" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/LiveButton/res/values-v14/styles.xml b/samples/devbytes/animation/LiveButton/res/values-v14/styles.xml
new file mode 100644
index 0000000..6e9521a
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/res/values-v14/styles.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/LiveButton/res/values/strings.xml b/samples/devbytes/animation/LiveButton/res/values/strings.xml
new file mode 100644
index 0000000..5c5ff17
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">LiveButton</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="menu_settings">Settings</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/LiveButton/res/values/styles.xml b/samples/devbytes/animation/LiveButton/res/values/styles.xml
new file mode 100644
index 0000000..27658b7
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/res/values/styles.xml
@@ -0,0 +1,34 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/LiveButton/src/com/example/android/livebutton/LiveButton.java b/samples/devbytes/animation/LiveButton/src/com/example/android/livebutton/LiveButton.java
new file mode 100644
index 0000000..d9f8d5e
--- /dev/null
+++ b/samples/devbytes/animation/LiveButton/src/com/example/android/livebutton/LiveButton.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 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.livebutton;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.OvershootInterpolator;
+import android.widget.Button;
+
+/**
+ * This app shows a simple application of anticipation and follow-through techniques as
+ * the button animates into its pressed state and animates back out of it, overshooting
+ * end state before resolving.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
+ * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
+ */
+public class LiveButton extends Activity {
+
+ DecelerateInterpolator sDecelerator = new DecelerateInterpolator();
+ OvershootInterpolator sOvershooter = new OvershootInterpolator(10f);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_overshoot);
+
+ final Button clickMeButton = (Button) findViewById(R.id.clickMe);
+ clickMeButton.animate().setDuration(200);
+
+ clickMeButton.setOnTouchListener(new View.OnTouchListener() {
+
+ @Override
+ public boolean onTouch(View arg0, MotionEvent arg1) {
+ if (arg1.getAction() == MotionEvent.ACTION_DOWN) {
+ clickMeButton.animate().setInterpolator(sDecelerator).
+ scaleX(.7f).scaleY(.7f);
+ } else if (arg1.getAction() == MotionEvent.ACTION_UP) {
+ clickMeButton.animate().setInterpolator(sOvershooter).
+ scaleX(1f).scaleY(1f);
+ }
+ return false;
+ }
+ });
+
+ }
+}
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/AndroidManifest.xml b/samples/devbytes/animation/MultiPropertyAnimations/AndroidManifest.xml
new file mode 100644
index 0000000..6a7a593
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.multipropertyanimations"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="14"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.example.android.multipropertyanimations.MultiPropertyAnimations"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/layout/activity_multi_property_animations.xml b/samples/devbytes/animation/MultiPropertyAnimations/res/layout/activity_multi_property_animations.xml
new file mode 100644
index 0000000..f4826b6
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/res/layout/activity_multi_property_animations.xml
@@ -0,0 +1,46 @@
+<!-- Copyright (C) 2013 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".MultiPropertyAnimations" >
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="runValueAnimator"
+ android:text="Animate Me!" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="runViewPropertyAnimator"
+ android:text="Animate Me!" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="runObjectAnimators"
+ android:text="Animate Me!" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="runObjectAnimator"
+ android:text="Animate Me!" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/values-v14/styles.xml b/samples/devbytes/animation/MultiPropertyAnimations/res/values-v14/styles.xml
new file mode 100644
index 0000000..6e9521a
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/res/values-v14/styles.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/values/dimens.xml b/samples/devbytes/animation/MultiPropertyAnimations/res/values/dimens.xml
new file mode 100644
index 0000000..90db76b
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/values/strings.xml b/samples/devbytes/animation/MultiPropertyAnimations/res/values/strings.xml
new file mode 100644
index 0000000..5487857
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">MultiPropertyAnimations</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/values/styles.xml b/samples/devbytes/animation/MultiPropertyAnimations/res/values/styles.xml
new file mode 100644
index 0000000..27658b7
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/res/values/styles.xml
@@ -0,0 +1,34 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/MultiPropertyAnimations/src/com/example/android/multipropertyanimations/MultiPropertyAnimations.java b/samples/devbytes/animation/MultiPropertyAnimations/src/com/example/android/multipropertyanimations/MultiPropertyAnimations.java
new file mode 100644
index 0000000..e1c8054
--- /dev/null
+++ b/samples/devbytes/animation/MultiPropertyAnimations/src/com/example/android/multipropertyanimations/MultiPropertyAnimations.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2013 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.multipropertyanimations;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * This example shows various ways of animating multiple properties in parallel.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
+ * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
+ */
+public class MultiPropertyAnimations extends Activity {
+
+ private static final float TX_START = 0;
+ private static final float TY_START = 0;
+ private static final float TX_END = 400;
+ private static final float TY_END = 200;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_multi_property_animations);
+ }
+
+ /**
+ * A very manual approach to animation uses a ValueAnimator to animate a fractional
+ * value and then turns that value into the final property values which are then set
+ * directly on the target object.
+ */
+ public void runValueAnimator(final View view) {
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 400);
+ anim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ float fraction = animator.getAnimatedFraction();
+ view.setTranslationX(TX_START + fraction * (TX_END - TX_START));
+ view.setTranslationY(TY_START + fraction * (TY_END - TY_START));
+ }
+ });
+ anim.start();
+ }
+
+ /**
+ * ViewPropertyAnimator is the cleanest and most efficient way of animating
+ * View properties, even when there are multiple properties to be animated
+ * in parallel.
+ */
+ public void runViewPropertyAnimator(View view) {
+ view.animate().translationX(TX_END).translationY(TY_END);
+ }
+
+ /**
+ * Multiple ObjectAnimator objects can be created and run in parallel.
+ */
+ public void runObjectAnimators(View view) {
+ ObjectAnimator.ofFloat(view, View.TRANSLATION_X, TX_END).start();
+ ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, TY_END).start();
+ // Optional: use an AnimatorSet to run these in parallel
+ }
+
+ /**
+ * Using PropertyValuesHolder objects enables the use of a single ObjectAnimator
+ * per target, even when there are multiple properties being animated on that target.
+ */
+ public void runObjectAnimator(View view) {
+ PropertyValuesHolder pvhTX = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, TX_END);
+ PropertyValuesHolder pvhTY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, TY_END);
+ ObjectAnimator.ofPropertyValuesHolder(view, pvhTX, pvhTY).start();
+ }
+}
diff --git a/samples/devbytes/animation/SquashAndStretch/AndroidManifest.xml b/samples/devbytes/animation/SquashAndStretch/AndroidManifest.xml
new file mode 100644
index 0000000..82e9b1b
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.squashandstretch"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="14"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.example.squashandstretch.SquashAndStretch"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/devbytes/animation/SquashAndStretch/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/SquashAndStretch/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/SquashAndStretch/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/SquashAndStretch/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/SquashAndStretch/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/SquashAndStretch/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/SquashAndStretch/res/layout/main.xml b/samples/devbytes/animation/SquashAndStretch/res/layout/main.xml
new file mode 100644
index 0000000..ea6793d
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/res/layout/main.xml
@@ -0,0 +1,31 @@
+<!-- Copyright (C) 2013 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container"
+ tools:context=".SquashAndStretch" >
+
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:onClick="onButtonClick"
+ android:text="Click Me!" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/SquashAndStretch/res/menu/main.xml b/samples/devbytes/animation/SquashAndStretch/res/menu/main.xml
new file mode 100644
index 0000000..aab540e
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/res/menu/main.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2013 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_slow"
+ android:orderInCategory="100"
+ android:showAsAction="never"
+ android:title="@string/menu_slow_animations"
+ android:checkable="true"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/animation/SquashAndStretch/res/values-v14/styles.xml b/samples/devbytes/animation/SquashAndStretch/res/values-v14/styles.xml
new file mode 100644
index 0000000..6e9521a
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/res/values-v14/styles.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/SquashAndStretch/res/values/strings.xml b/samples/devbytes/animation/SquashAndStretch/res/values/strings.xml
new file mode 100644
index 0000000..3fb1b96
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">SquashAndStretch</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="menu_slow_animations">Slow</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/SquashAndStretch/res/values/styles.xml b/samples/devbytes/animation/SquashAndStretch/res/values/styles.xml
new file mode 100644
index 0000000..27658b7
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/res/values/styles.xml
@@ -0,0 +1,34 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/SquashAndStretch/src/com/example/squashandstretch/SquashAndStretch.java b/samples/devbytes/animation/SquashAndStretch/src/com/example/squashandstretch/SquashAndStretch.java
new file mode 100644
index 0000000..328dd50
--- /dev/null
+++ b/samples/devbytes/animation/SquashAndStretch/src/com/example/squashandstretch/SquashAndStretch.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2013 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.squashandstretch;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This example shows how to add some life to a view during animation by deforming the shape.
+ * As the button "falls", it stretches along the line of travel. When it hits the bottom, it
+ * squashes, like a real object when hitting a surface. Then the button reverses these actions
+ * to bounce back up to the start.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
+ * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
+ */
+public class SquashAndStretch extends Activity {
+
+ private static final AccelerateInterpolator sAccelerator = new AccelerateInterpolator();
+ private static final DecelerateInterpolator sDecelerator = new DecelerateInterpolator();
+
+ ViewGroup mContainer = null;
+ private static final long BASE_DURATION = 300;
+ private long sAnimatorScale = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mContainer = (ViewGroup) findViewById(R.id.container);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_slow) {
+ sAnimatorScale = item.isChecked() ? 1 : 5;
+ item.setChecked(!item.isChecked());
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ public void onButtonClick(View view) {
+ long animationDuration = (long) (BASE_DURATION * sAnimatorScale);
+
+ // Scale around bottom/middle to simplify squash against the window bottom
+ view.setPivotX(view.getWidth() / 2);
+ view.setPivotY(view.getHeight());
+
+ // Animate the button down, accelerating, while also stretching in Y and squashing in X
+ PropertyValuesHolder pvhTY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y,
+ mContainer.getHeight() - view.getHeight());
+ PropertyValuesHolder pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, .7f);
+ PropertyValuesHolder pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.2f);
+ ObjectAnimator downAnim = ObjectAnimator.ofPropertyValuesHolder(
+ view, pvhTY, pvhSX, pvhSY);
+ downAnim.setInterpolator(sAccelerator);
+ downAnim.setDuration((long) (animationDuration * 2));
+
+ // Stretch in X, squash in Y, then reverse
+ pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, 2);
+ pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, .5f);
+ ObjectAnimator stretchAnim =
+ ObjectAnimator.ofPropertyValuesHolder(view, pvhSX, pvhSY);
+ stretchAnim.setRepeatCount(1);
+ stretchAnim.setRepeatMode(ValueAnimator.REVERSE);
+ stretchAnim.setInterpolator(sDecelerator);
+ stretchAnim.setDuration(animationDuration);
+
+ // Animate back to the start
+ pvhTY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0);
+ pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+ pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
+ ObjectAnimator upAnim =
+ ObjectAnimator.ofPropertyValuesHolder(view, pvhTY, pvhSX, pvhSY);
+ upAnim.setDuration((long) (animationDuration * 2));
+ upAnim.setInterpolator(sDecelerator);
+
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(downAnim, stretchAnim, upAnim);
+ set.start();
+ }
+}
diff --git a/samples/devbytes/animation/ToonGame/AndroidManifest.xml b/samples/devbytes/animation/ToonGame/AndroidManifest.xml
new file mode 100644
index 0000000..505b7ce
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/AndroidManifest.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.toongame"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="16"
+ android:targetSdkVersion="17" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.example.android.toongame.ToonGame"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name="com.example.android.toongame.PlayerSetupActivity"
+ android:windowSoftInputMode="adjustResize"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/animation/ToonGame/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/blue_oval.xml b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/blue_oval.xml
new file mode 100644
index 0000000..2ffe16a
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/blue_oval.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid android:color="#00f"/>
+
+</shape>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/cyan_oval.xml b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/cyan_oval.xml
new file mode 100644
index 0000000..53ea6ac
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/cyan_oval.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid android:color="#0ff"/>
+
+</shape>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_button.xml b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_button.xml
new file mode 100644
index 0000000..3ed5b67
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_button.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:drawable="@drawable/green_down" android:state_pressed="true"/>
+ <item android:drawable="@drawable/green_up"/>
+</selector>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_down.9.png b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_down.9.png
new file mode 100644
index 0000000..db974ba
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_down.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_oval.xml b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_oval.xml
new file mode 100644
index 0000000..03b83c3
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_oval.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid android:color="#0f0"/>
+
+</shape>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_up.9.png b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_up.9.png
new file mode 100644
index 0000000..0c2f5c8
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_up.9.png
Binary files differ
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/magenta_oval.xml b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/magenta_oval.xml
new file mode 100644
index 0000000..675b0dc
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/magenta_oval.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid android:color="#f0f"/>
+
+</shape>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/red_oval.xml b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/red_oval.xml
new file mode 100644
index 0000000..f2276a6
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/red_oval.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid android:color="#f00"/>
+
+</shape>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/yellow_oval.xml b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/yellow_oval.xml
new file mode 100644
index 0000000..d03f684
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/yellow_oval.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+
+ <solid android:color="#ff0"/>
+
+</shape>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ToonGame/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/animation/ToonGame/res/layout/activity_toon_game.xml b/samples/devbytes/animation/ToonGame/res/layout/activity_toon_game.xml
new file mode 100644
index 0000000..d65301b
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/layout/activity_toon_game.xml
@@ -0,0 +1,47 @@
+<!-- Copyright (C) 2013 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container"
+ tools:context=".ToonGame" >
+
+ <Button
+ android:id="@+id/startButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="64dip"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:textColor="#00c"
+ android:text="Play!"
+ android:background="@drawable/green_button"
+ android:visibility="invisible"
+ android:onClick="play"
+ android:textStyle="bold" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/startButton"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="39dp"
+ android:text="@string/welcome"
+ android:textColor="@android:color/holo_blue_bright"
+ android:textSize="64dip"
+ android:textStyle="bold" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/layout/player_setup_layout.xml b/samples/devbytes/animation/ToonGame/res/layout/player_setup_layout.xml
new file mode 100644
index 0000000..c55d39b
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/layout/player_setup_layout.xml
@@ -0,0 +1,187 @@
+<!-- Copyright (C) 2013 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.
+-->
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <RelativeLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false">
+
+ <view
+ android:id="@+id/nameTV"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="120dp"
+ class="com.example.android.toongame.SkewableTextView"
+ android:text="Name?"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@dimen/bigText" />
+
+ <view
+ class="com.example.android.toongame.SkewableTextView"
+ android:id="@+id/ageTV"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="120dp"
+ android:text="Difficulty?"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="@dimen/bigText" />
+
+ <view
+ class="com.example.android.toongame.SkewableTextView"
+ android:id="@+id/creditTV"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="120dp"
+ android:gravity="center"
+ android:text="Parent's Credit Card Number?"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="64sp" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:visibility="gone"
+ android:id="@+id/nameButtons"
+ android:layout_marginBottom="132dp" >
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dip"
+ android:padding="15dip"
+ android:background="@drawable/green_oval"
+ android:text="Bob"
+ android:textSize="36sp"
+ android:textColor="#fff"
+ android:textStyle="bold"
+ android:onClick="selectName"
+ android:id="@+id/bobButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dip"
+ android:padding="15dip"
+ android:background="@drawable/blue_oval"
+ android:text="Jane"
+ android:textSize="36sp"
+ android:textColor="#fff"
+ android:textStyle="bold"
+ android:onClick="selectName"
+ android:id="@+id/janeButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dip"
+ android:padding="15dip"
+ android:background="@drawable/magenta_oval"
+ android:text="Pat"
+ android:textSize="36sp"
+ android:textColor="#fff"
+ android:textStyle="bold"
+ android:onClick="selectName"
+ android:id="@+id/patButton"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:visibility="gone"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:id="@+id/difficultyButtons"
+ android:layout_marginBottom="50dp" >
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dip"
+ android:padding="15dip"
+ android:background="@drawable/green_oval"
+ android:text="Easy"
+ android:textSize="36sp"
+ android:textColor="#fff"
+ android:textStyle="bold"
+ android:onClick="selectDifficulty"
+ android:id="@+id/easyButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dip"
+ android:padding="15dip"
+ android:background="@drawable/blue_oval"
+ android:text="Hard"
+ android:textSize="36sp"
+ android:textColor="#fff"
+ android:textStyle="bold"
+ android:onClick="selectDifficulty"
+ android:id="@+id/hardButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dip"
+ android:padding="15dip"
+ android:background="@drawable/red_oval"
+ android:text="Mega Hard"
+ android:textSize="36sp"
+ android:textColor="#000"
+ android:textStyle="bold|italic"
+ android:onClick="selectDifficulty"
+ android:id="@+id/megaHardButton"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:visibility="gone"
+ android:id="@+id/creditButtons1"
+ android:layout_marginBottom="132dp" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentBottom="true"
+ android:visibility="gone"
+ android:id="@+id/creditButtons2"
+ android:layout_marginBottom="70dp" />
+
+
+ </RelativeLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/values/dimens.xml b/samples/devbytes/animation/ToonGame/res/values/dimens.xml
new file mode 100644
index 0000000..fc229f6
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="bigText">64dip</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/values/strings.xml b/samples/devbytes/animation/ToonGame/res/values/strings.xml
new file mode 100644
index 0000000..fb256e6
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <string name="app_name">ToonGame</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="welcome">Welcome!</string>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/res/values/styles.xml b/samples/devbytes/animation/ToonGame/res/values/styles.xml
new file mode 100644
index 0000000..9d91d6a
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/res/values/styles.xml
@@ -0,0 +1,34 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/PlayerSetupActivity.java b/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/PlayerSetupActivity.java
new file mode 100644
index 0000000..fce11c3
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/PlayerSetupActivity.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2013 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.toongame;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.OvalShape;
+import android.os.Bundle;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.OvershootInterpolator;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.RelativeLayout;
+
+/**
+ * This activity, launched from the ToonGame activity, takes the user between three
+ * different setup screens where they choose a name, choose a difficulty rating, and
+ * enter important financial information. All of the screens are meant to be
+ * simple, engaging, and fun.
+ */
+public class PlayerSetupActivity extends Activity {
+
+ private static final AccelerateInterpolator sAccelerator = new AccelerateInterpolator();
+ private static final LinearInterpolator sLinearInterpolator = new LinearInterpolator();
+ ViewGroup mContainer;
+ EditText mEditText;
+
+ private static final int NAME_STATE = 0;
+ private static final int DIFFICULTY_STATE = 1;
+ private static final int CREDIT_STATE = 2;
+ private int mEntryState = NAME_STATE;
+
+ SkewableTextView mNameTV, mDifficultyTV, mCreditTV;
+
+ ViewGroup mNameButtons, mDifficultyButtons, mCreditButtons1, mCreditButtons2;
+
+ Button mBobButton, mJaneButton, mPatButton;
+
+ private static final TimeInterpolator sOvershooter = new OvershootInterpolator();
+ private static final DecelerateInterpolator sDecelerator = new DecelerateInterpolator();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.player_setup_layout);
+ overridePendingTransition(0, 0);
+
+ mContainer = (ViewGroup) findViewById(R.id.container);
+ mContainer.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
+
+ mNameTV = (SkewableTextView) findViewById(R.id.nameTV);
+ mDifficultyTV = (SkewableTextView) findViewById(R.id.ageTV);
+ mCreditTV = (SkewableTextView) findViewById(R.id.creditTV);
+
+ mBobButton = setupButton(R.id.bobButton);
+ setupButton(R.id.janeButton);
+ setupButton(R.id.patButton);
+ setupButton(R.id.easyButton);
+ setupButton(R.id.hardButton);
+ setupButton(R.id.megaHardButton);
+
+ mNameButtons = (ViewGroup) findViewById(R.id.nameButtons);
+ mDifficultyButtons = (ViewGroup) findViewById(R.id.difficultyButtons);
+ mCreditButtons1 = (ViewGroup) findViewById(R.id.creditButtons1);
+ mCreditButtons2 = (ViewGroup) findViewById(R.id.creditButtons2);
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ overridePendingTransition(0, 0);
+ }
+
+ private Button setupButton(int resourceId) {
+ Button button = (Button) findViewById(resourceId);
+ button.setOnTouchListener(mButtonPressListener);
+ return button;
+ }
+
+ private View.OnTouchListener mButtonPressListener =
+ new View.OnTouchListener() {
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ v.animate().setDuration(ToonGame.SHORT_DURATION).
+ scaleX(.8f).scaleY(.8f).setInterpolator(sDecelerator);
+ break;
+ case MotionEvent.ACTION_UP:
+ v.animate().setDuration(ToonGame.SHORT_DURATION).
+ scaleX(1).scaleY(1).setInterpolator(sAccelerator);
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+ };
+
+ public void buttonClick(View clickedView, int alignmentRule) {
+ ViewGroup parent = (ViewGroup) clickedView.getParent();
+ for (int i = 0; i < parent.getChildCount(); ++i) {
+ Button child = (Button) parent.getChildAt(i);
+ if (child != clickedView) {
+ child.animate().alpha(0);
+ } else {
+ final Button buttonCopy = new Button(this);
+ child.setVisibility(View.INVISIBLE);
+ buttonCopy.setBackground(child.getBackground());
+ buttonCopy.setText(((Button) child).getText());
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.WRAP_CONTENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+ params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ params.addRule(alignmentRule);
+ params.setMargins(25, 50, 25, 50);
+ buttonCopy.setLayoutParams(params);
+ buttonCopy.setPadding(child.getPaddingLeft(), child.getPaddingTop(),
+ child.getPaddingRight(), child.getPaddingBottom());
+ buttonCopy.setTextSize(TypedValue.COMPLEX_UNIT_PX, child.getTextSize());
+ buttonCopy.setTypeface(child.getTypeface(), Typeface.BOLD);
+ ColorStateList colors = child.getTextColors();
+ buttonCopy.setTextColor(colors.getDefaultColor());
+ final int[] oldLocationInWindow = new int[2];
+ child.getLocationInWindow(oldLocationInWindow);
+ mContainer.addView(buttonCopy);
+ buttonCopy.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ buttonCopy.getViewTreeObserver().removeOnPreDrawListener(this);
+ int[] locationInWindow = new int[2];
+ buttonCopy.getLocationInWindow(locationInWindow);
+ float deltaX = oldLocationInWindow[0] - locationInWindow[0];
+ float deltaY = oldLocationInWindow[1] - locationInWindow[1];
+
+ buttonCopy.setTranslationX(deltaX);
+ buttonCopy.setTranslationY(deltaY);
+
+ PropertyValuesHolder pvhSX =
+ PropertyValuesHolder.ofFloat(View.SCALE_X, 3);
+ PropertyValuesHolder pvhSY =
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, 3);
+ ObjectAnimator bounceAnim = ObjectAnimator.ofPropertyValuesHolder(
+ buttonCopy, pvhSX, pvhSY);
+ bounceAnim.setRepeatCount(1);
+ bounceAnim.setRepeatMode(ValueAnimator.REVERSE);
+ bounceAnim.setInterpolator(sDecelerator);
+ bounceAnim.setDuration(300);
+
+ PropertyValuesHolder pvhTX =
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0);
+ PropertyValuesHolder pvhTY =
+ PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0);
+ ObjectAnimator moveAnim = ObjectAnimator.ofPropertyValuesHolder(
+ buttonCopy, pvhTX, pvhTY);
+ moveAnim.setDuration(600);
+ bounceAnim.start();
+ moveAnim.start();
+ moveAnim.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ switch (mEntryState) {
+ case (NAME_STATE) :
+ {
+ Runnable runnable = new Runnable() {
+ public void run() {
+ mDifficultyButtons.setVisibility(View.VISIBLE);
+ mNameButtons.setVisibility(View.GONE);
+ popChildrenIn(mDifficultyButtons, null);
+ }
+ };
+ slideToNext(mNameTV, mDifficultyTV, runnable);
+ mEntryState = DIFFICULTY_STATE;
+ break;
+ }
+ case (DIFFICULTY_STATE) :
+ {
+ mDifficultyButtons.setVisibility(View.GONE);
+ for (int i = 0; i < 5; ++i) {
+ mCreditButtons1.addView(setupNumberButton(i));
+ }
+ for (int i = 5; i < 10; ++i) {
+ mCreditButtons2.addView(setupNumberButton(i));
+ }
+ Runnable runnable = new Runnable() {
+ public void run() {
+ mCreditButtons1.setVisibility(View.VISIBLE);
+ Runnable runnable = new Runnable() {
+ public void run() {
+ mCreditButtons2.setVisibility(View.VISIBLE);
+ popChildrenIn(mCreditButtons2, null);
+ }
+ };
+ popChildrenIn(mCreditButtons1, runnable);
+ }
+ };
+ slideToNext(mDifficultyTV, mCreditTV, runnable);
+ mEntryState = CREDIT_STATE;
+ }
+ break;
+ }
+ }
+ });
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+ public void selectDifficulty(View clickedView) {
+ buttonClick(clickedView, RelativeLayout.ALIGN_PARENT_RIGHT);
+ }
+
+ public void selectName(View clickedView) {
+ buttonClick(clickedView, RelativeLayout.ALIGN_PARENT_LEFT);
+ }
+
+ private Button setupNumberButton(int number) {
+ Button button = new Button(PlayerSetupActivity.this);
+ button.setTextSize(15);
+ button.setTextColor(Color.WHITE);
+ button.setTypeface(mBobButton.getTypeface(), Typeface.BOLD);
+ button.setText(Integer.toString(number));
+ button.setPadding(0, 0, 0, 0);
+
+ OvalShape oval = new OvalShape();
+ ShapeDrawable drawable = new ShapeDrawable(oval);
+ drawable.getPaint().setColor(0xFF << 24 | (int) (50 + 150 * Math.random()) << 16 |
+ (int) (50 + 150 * Math.random()) << 8 | (int) (50 + 150 * Math.random()));
+ button.setBackground(drawable);
+
+ button.setOnTouchListener(mButtonPressListener);
+
+ return button;
+ }
+
+ ViewTreeObserver.OnPreDrawListener mPreDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ mContainer.getViewTreeObserver().removeOnPreDrawListener(this);
+ mContainer.setScaleX(0);
+ mContainer.setScaleY(0);
+ mContainer.animate().scaleX(1).scaleY(1).setInterpolator(new OvershootInterpolator());
+ mContainer.animate().setDuration(ToonGame.LONG_DURATION).withEndAction(new Runnable() {
+
+ @Override
+ public void run() {
+ ViewGroup buttonsParent = (ViewGroup) findViewById(R.id.nameButtons);
+ buttonsParent.setVisibility(View.VISIBLE);
+ popChildrenIn(buttonsParent, null);
+ }
+ });
+ return false;
+ }
+ };
+
+ private void popChildrenIn(ViewGroup parent, final Runnable endAction) {
+ // for all children, scale in one at a time
+ TimeInterpolator overshooter = new OvershootInterpolator();
+ int childCount = parent.getChildCount();
+ ObjectAnimator[] childAnims = new ObjectAnimator[childCount];
+ for (int i = 0; i < childCount; ++i) {
+ View child = parent.getChildAt(i);
+ child.setScaleX(0);
+ child.setScaleY(0);
+ PropertyValuesHolder pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+ PropertyValuesHolder pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(child, pvhSX, pvhSY);
+ anim.setDuration(150);
+ anim.setInterpolator(overshooter);
+ childAnims[i] = anim;
+ }
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(childAnims);
+ set.start();
+ if (endAction != null) {
+ set.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ endAction.run();
+ }
+ });
+ }
+ }
+
+ private void slideToNext(final SkewableTextView currentView,
+ final SkewableTextView nextView, final Runnable endAction) {
+ // skew/anticipate current view, slide off, set GONE, restore translation
+ ObjectAnimator currentSkewer = ObjectAnimator.ofFloat(currentView, "skewX", -.5f);
+ currentSkewer.setInterpolator(sDecelerator);
+ ObjectAnimator currentMover = ObjectAnimator.ofFloat(currentView, View.TRANSLATION_X,
+ -mContainer.getWidth());
+ currentMover.setInterpolator(sLinearInterpolator);
+ currentMover.setDuration(ToonGame.MEDIUM_DURATION);
+
+ // set next view visible, translate off to right, skew,
+ // slide on in parallel, overshoot/wobble, unskew
+ nextView.setVisibility(View.VISIBLE);
+ nextView.setSkewX(-.5f);
+ nextView.setTranslationX(mContainer.getWidth());
+
+ ObjectAnimator nextMover = ObjectAnimator.ofFloat(nextView, View.TRANSLATION_X, 0);
+ nextMover.setInterpolator(sAccelerator);
+ nextMover.setDuration(ToonGame.MEDIUM_DURATION);
+ ObjectAnimator nextSkewer = ObjectAnimator.ofFloat(nextView, "skewX", 0);
+ nextSkewer.setInterpolator(sOvershooter);
+
+ AnimatorSet moverSet = new AnimatorSet();
+ moverSet.playTogether(currentMover, nextMover);
+ AnimatorSet fullSet = new AnimatorSet();
+ fullSet.playSequentially(currentSkewer, moverSet, nextSkewer);
+ fullSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ currentView.setSkewX(0);
+ currentView.setVisibility(View.GONE);
+ currentView.setTranslationX(0);
+ if (endAction != null) {
+ endAction.run();
+ }
+ }
+ });
+
+ fullSet.start();
+ }
+
+}
diff --git a/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/SkewableTextView.java b/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/SkewableTextView.java
new file mode 100644
index 0000000..9ea15ca
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/SkewableTextView.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013 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.toongame;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * This custom TextView can be skewed to the left or right to enable anticipation and
+ * follow-through effects
+ */
+public class SkewableTextView extends TextView {
+
+ private float mSkewX;
+ RectF mTempRect = new RectF();
+
+ public SkewableTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public SkewableTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SkewableTextView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mSkewX != 0) {
+ canvas.translate(0, getHeight());
+ canvas.skew(mSkewX, 0);
+ canvas.translate(0, -getHeight());
+ }
+ super.onDraw(canvas);
+ }
+
+ public float getSkewX() {
+ return mSkewX;
+ }
+
+ public void setSkewX(float value) {
+ if (value != mSkewX) {
+ mSkewX = value;
+ invalidate(); // force redraw with new skew value
+ invalidateSkewedBounds(); // also invalidate appropriate area of parent
+ }
+ }
+
+ /**
+ * Need to invalidate proper area of parent for skewed bounds
+ */
+ private void invalidateSkewedBounds() {
+ if (mSkewX != 0) {
+ Matrix matrix = new Matrix();
+ matrix.setSkew(-mSkewX, 0);
+ mTempRect.set(0, 0, getRight(), getBottom());
+ matrix.mapRect(mTempRect);
+ mTempRect.offset(getLeft() + getTranslationX(), getTop() + getTranslationY());
+ ((View) getParent()).invalidate((int) mTempRect.left, (int) mTempRect.top,
+ (int) (mTempRect.right +.5f), (int) (mTempRect.bottom + .5f));
+ }
+ }
+}
diff --git a/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/ToonGame.java b/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/ToonGame.java
new file mode 100644
index 0000000..b03eeeb
--- /dev/null
+++ b/samples/devbytes/animation/ToonGame/src/com/example/android/toongame/ToonGame.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2013 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.toongame;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+import android.widget.Button;
+
+/**
+ * This application shows various cartoon animation techniques in the context of
+ * a larger application, to show how such animations might be used to create a more
+ * interactive, fun, and engaging experience.
+ *
+ * This main activity launches a sub-activity when the Play button is clicked. The
+ * main action in this master activity is bouncing the Play button in, randomly
+ * bouncing it while waiting for input, and animating its press and click behaviors
+ * when the user interacts with it.
+ *
+ * Watch the associated video for this demo on the DevBytes channel of developer.android.com
+ * or on the DevBytes playlist in the androiddevelopers channel on YouTube at
+ * https://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0.
+ */
+public class ToonGame extends Activity {
+
+ Button mStarter;
+ ViewGroup mContainer;
+ private static final AccelerateInterpolator sAccelerator = new AccelerateInterpolator();
+ private static final DecelerateInterpolator sDecelerator = new DecelerateInterpolator();
+ private static final LinearInterpolator sLinearInterpolator = new LinearInterpolator();
+ static long SHORT_DURATION = 100;
+ static long MEDIUM_DURATION = 200;
+ static long REGULAR_DURATION = 300;
+ static long LONG_DURATION = 500;
+
+ private static float sDurationScale = 1f;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ overridePendingTransition(0, 0);
+ setContentView(R.layout.activity_toon_game);
+
+ mStarter = (Button) findViewById(R.id.startButton);
+ mContainer = (ViewGroup) findViewById(R.id.container);
+ mStarter.setOnTouchListener(funButtonListener);
+ mStarter.animate().setDuration(100);
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mContainer.setScaleX(1);
+ mContainer.setScaleY(1);
+ mContainer.setAlpha(1);
+ mStarter.setVisibility(View.INVISIBLE);
+ mContainer.getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mStarter.removeCallbacks(mSquishRunnable);
+ }
+
+ private OnTouchListener funButtonListener = new OnTouchListener() {
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mStarter.animate().scaleX(.8f).scaleY(.8f).setInterpolator(sDecelerator);
+ mStarter.setTextColor(Color.CYAN);
+ mStarter.removeCallbacks(mSquishRunnable);
+ mStarter.setPressed(true);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ float x = event.getX();
+ float y = event.getY();
+ boolean isInside = (x > 0 && x < mStarter.getWidth() &&
+ y > 0 && y < mStarter.getHeight());
+ if (mStarter.isPressed() != isInside) {
+ mStarter.setPressed(isInside);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mStarter.isPressed()) {
+ mStarter.performClick();
+ mStarter.setPressed(false);
+ } else {
+ mStarter.animate().scaleX(1).scaleY(1).setInterpolator(sAccelerator);
+ }
+ mStarter.setTextColor(Color.BLUE);
+ break;
+ }
+ return true;
+ }
+ };
+
+ private Runnable mSquishRunnable = new Runnable() {
+ public void run() {
+ squishyBounce(mStarter, 0,
+ mContainer.getHeight() - mStarter.getTop() - mStarter.getHeight(),
+ 0, .5f, 1.5f);
+ }
+ };
+
+ public void play(View view) {
+ mContainer.animate().scaleX(5).scaleY(5).alpha(0).setDuration(LONG_DURATION).
+ setInterpolator(sLinearInterpolator).
+ withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mStarter.postOnAnimation(new Runnable() {
+ public void run() {
+ Intent intent = new Intent(ToonGame.this,
+ PlayerSetupActivity.class);
+ startActivity(intent);
+ overridePendingTransition(0, 0);
+ }
+ });
+ }
+ });
+ view.removeCallbacks(mSquishRunnable);
+ }
+
+ private ViewTreeObserver.OnPreDrawListener mOnPreDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+
+ @Override
+ public boolean onPreDraw() {
+ mContainer.getViewTreeObserver().removeOnPreDrawListener(this);
+ mContainer.postDelayed(new Runnable() {
+ public void run() {
+ // Drop in the button from off the top of the screen
+ mStarter.setVisibility(View.VISIBLE);
+ mStarter.setY(-mStarter.getHeight());
+ squishyBounce(mStarter,
+ -(mStarter.getTop() + mStarter.getHeight()),
+ mContainer.getHeight() - mStarter.getTop() -
+ mStarter.getHeight(),
+ 0, .5f, 1.5f);
+ }
+ }, 500);
+ return true;
+ }
+ };
+
+ private void squishyBounce(final View view, final float startTY, final float bottomTY,
+ final float endTY, final float squash, final float stretch) {
+ view.setPivotX(view.getWidth() / 2);
+ view.setPivotY(view.getHeight());
+ PropertyValuesHolder pvhTY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y,
+ startTY, bottomTY);
+ PropertyValuesHolder pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, .7f);
+ PropertyValuesHolder pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1.2f);
+ ObjectAnimator downAnim = ObjectAnimator.ofPropertyValuesHolder(view, pvhTY, pvhSX, pvhSY);
+ downAnim.setInterpolator(sAccelerator);
+
+ pvhTY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, bottomTY, endTY);
+ pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+ pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
+ ObjectAnimator upAnim = ObjectAnimator.ofPropertyValuesHolder(view, pvhTY, pvhSX, pvhSY);
+ upAnim.setInterpolator(sDecelerator);
+
+ pvhSX = PropertyValuesHolder.ofFloat(View.SCALE_X, stretch);
+ pvhSY = PropertyValuesHolder.ofFloat(View.SCALE_Y, squash);
+ ObjectAnimator stretchAnim = ObjectAnimator.ofPropertyValuesHolder(view, pvhSX, pvhSY);
+ stretchAnim.setRepeatCount(1);
+ stretchAnim.setRepeatMode(ValueAnimator.REVERSE);
+ stretchAnim.setInterpolator(sDecelerator);
+
+ AnimatorSet set = new AnimatorSet();
+ set.playSequentially(downAnim, stretchAnim, upAnim);
+ set.setDuration(getDuration(SHORT_DURATION));
+ set.start();
+ set.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ view.postDelayed(mSquishRunnable, (long) (500 + Math.random() * 2000));
+ }
+ });
+ }
+
+ public static long getDuration(long baseDuration) {
+ return (long) (baseDuration * sDurationScale);
+ }
+
+
+}