diff options
| author | Chet Haase <chet@google.com> | 2013-05-30 15:05:47 -0700 |
|---|---|---|
| committer | Android Git Automerger <android-git-automerger@android.com> | 2013-05-30 15:05:47 -0700 |
| commit | 9c6fb05ed859cd9939504edc8fa40a0c2ea91516 (patch) | |
| tree | b5d5604c01deabd10dcfb330375bd82b38b85669 /samples | |
| parent | 5e56f62570cd3307ada046cb35cbbc41e3dbb4eb (diff) | |
| parent | 58cbe71cc434d787e54d3b3a1d7d86af9727857c (diff) | |
am 58cbe71c: am 8654b6c6: am 3ff1591c: am 28b127ec: New DevBytes animation demos
* commit '58cbe71cc434d787e54d3b3a1d7d86af9727857c':
New DevBytes animation demos
Diffstat (limited to 'samples')
127 files changed, 5529 insertions, 0 deletions
diff --git a/samples/devbytes/animation/ActivityAnimations/AndroidManifest.xml b/samples/devbytes/animation/ActivityAnimations/AndroidManifest.xml new file mode 100644 index 000000000..eeb53781e --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ActivityAnimations/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p1.jpg b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p1.jpg Binary files differnew file mode 100644 index 000000000..974581894 --- /dev/null +++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p1.jpg diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p2.jpg b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p2.jpg Binary files differnew file mode 100644 index 000000000..db8731f01 --- /dev/null +++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p2.jpg diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p3.jpg b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p3.jpg Binary files differnew file mode 100644 index 000000000..b240b3a02 --- /dev/null +++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p3.jpg diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p4.jpg b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p4.jpg Binary files differnew file mode 100644 index 000000000..4de929221 --- /dev/null +++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-nodpi/p4.jpg diff --git a/samples/devbytes/animation/ActivityAnimations/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ActivityAnimations/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/ActivityAnimations/res/drawable-xhdpi/ic_launcher.png 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 000000000..c11a568e7 --- /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 000000000..cb2ced229 --- /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 000000000..aab540e09 --- /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 000000000..f9409b551 --- /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 000000000..9d8334244 --- /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 000000000..4a3e0d95b --- /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 000000000..a8034dce4 --- /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 000000000..eb2d1264a --- /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 000000000..e1674c91e --- /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 000000000..b3bc96180 --- /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 000000000..c9415245f --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/Anticipation/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/animation/Anticipation/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/Anticipation/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/Anticipation/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/animation/Anticipation/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/Anticipation/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/Anticipation/res/drawable-xhdpi/ic_launcher.png 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 000000000..7da093f48 --- /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 000000000..6e9521a16 --- /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 000000000..90976294b --- /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 000000000..27658b7d6 --- /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 000000000..707765bb9 --- /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 000000000..4ef8f62db --- /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 000000000..195faae23 --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/CurvedMotion/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/animation/CurvedMotion/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/CurvedMotion/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/CurvedMotion/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/animation/CurvedMotion/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/CurvedMotion/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/CurvedMotion/res/drawable-xhdpi/ic_launcher.png 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 000000000..10adea314 --- /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 000000000..556d2da2c --- /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 000000000..6e9521a16 --- /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 000000000..90db76b39 --- /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 000000000..a070e157f --- /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 000000000..27658b7d6 --- /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 000000000..d030c6a16 --- /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 000000000..357d72de9 --- /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 000000000..e68d4cdae --- /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 000000000..ba1a05ecc --- /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 000000000..173b18852 --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 000000000..6226b9e76 --- /dev/null +++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/shadowed_background.9.png 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 Binary files differnew file mode 100644 index 000000000..e6e3cd58d --- /dev/null +++ b/samples/devbytes/animation/ListViewItemAnimations/res/drawable-xhdpi/tv_background_with_divider.9.png 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 000000000..8290927c0 --- /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 000000000..aa03362c9 --- /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 000000000..8290927c0 --- /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 000000000..c37f62d31 --- /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 000000000..556d2da2c --- /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 000000000..6e9521a16 --- /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 000000000..90db76b39 --- /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 000000000..2cc5f7135 --- /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 000000000..ee3c56cea --- /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 000000000..40ac39af7 --- /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 000000000..52410224e --- /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 000000000..68a765125 --- /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 000000000..4bddc5eaa --- /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 000000000..f03074726 --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 000000000..f905bbc46 --- /dev/null +++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/shadowed_background.9.png 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 Binary files differnew file mode 100644 index 000000000..e6e3cd58d --- /dev/null +++ b/samples/devbytes/animation/ListViewRemovalAnimation/res/drawable-xhdpi/tv_background_with_divider.9.png 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 000000000..26aa1230a --- /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 000000000..c37f62d31 --- /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 000000000..139d283c1 --- /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 000000000..8ac452213 --- /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 000000000..71256184e --- /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 000000000..c7f2e7966 --- /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 000000000..1d0e806fa --- /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 000000000..4f4b88bc1 --- /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 000000000..1e3ee8c44 --- /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 000000000..948676e7e --- /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 000000000..280c2f8e3 --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/LiveButton/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/animation/LiveButton/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/LiveButton/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/LiveButton/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/animation/LiveButton/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/LiveButton/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/LiveButton/res/drawable-xhdpi/ic_launcher.png 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 000000000..6900650e1 --- /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 000000000..6e9521a16 --- /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 000000000..5c5ff1757 --- /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 000000000..27658b7d6 --- /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 000000000..d9f8d5e7d --- /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 000000000..6a7a5935b --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/MultiPropertyAnimations/res/drawable-xhdpi/ic_launcher.png 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 000000000..f4826b6fb --- /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 000000000..6e9521a16 --- /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 000000000..90db76b39 --- /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 000000000..5487857b5 --- /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 000000000..27658b7d6 --- /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 000000000..e1c805457 --- /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 000000000..82e9b1b48 --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/SquashAndStretch/res/drawable-hdpi/ic_launcher.png diff --git a/samples/devbytes/animation/SquashAndStretch/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/SquashAndStretch/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/SquashAndStretch/res/drawable-mdpi/ic_launcher.png diff --git a/samples/devbytes/animation/SquashAndStretch/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/animation/SquashAndStretch/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/SquashAndStretch/res/drawable-xhdpi/ic_launcher.png 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 000000000..ea6793ddc --- /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 000000000..aab540e09 --- /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 000000000..6e9521a16 --- /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 000000000..3fb1b9672 --- /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 000000000..27658b7d6 --- /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 000000000..328dd5018 --- /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 000000000..505b7ce9f --- /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 Binary files differnew file mode 100644 index 000000000..96a442e5b --- /dev/null +++ b/samples/devbytes/animation/ToonGame/res/drawable-hdpi/ic_launcher.png 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 000000000..2ffe16a3e --- /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 000000000..53ea6aca3 --- /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 000000000..3ed5b67dc --- /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 Binary files differnew file mode 100644 index 000000000..db974ba2f --- /dev/null +++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_down.9.png 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 000000000..03b83c3e6 --- /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 Binary files differnew file mode 100644 index 000000000..0c2f5c850 --- /dev/null +++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/green_up.9.png diff --git a/samples/devbytes/animation/ToonGame/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000..359047dfa --- /dev/null +++ b/samples/devbytes/animation/ToonGame/res/drawable-mdpi/ic_launcher.png 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 000000000..675b0dcc1 --- /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 000000000..f2276a6cf --- /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 000000000..d03f684ee --- /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 Binary files differnew file mode 100644 index 000000000..71c6d760f --- /dev/null +++ b/samples/devbytes/animation/ToonGame/res/drawable-xhdpi/ic_launcher.png 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 000000000..d65301b5f --- /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 000000000..c55d39be5 --- /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 000000000..fc229f68e --- /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 000000000..fb256e6c2 --- /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 000000000..9d91d6a8e --- /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 000000000..fce11c3ef --- /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 000000000..9ea15cada --- /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 000000000..b03eeeb17 --- /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); + } + + +} |
