am 8654b6c6: am 3ff1591c: am 28b127ec: New DevBytes animation demos

* commit '8654b6c6ac430c285d9ad5bc7999a1c5390efa61':
  New DevBytes animation demos
This commit is contained in:
Chet Haase
2013-05-30 15:03:54 -07:00
committed by Android Git Automerger
127 changed files with 5529 additions and 0 deletions

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"
};
}

View File

@@ -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) {
}
}
}

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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"
};
}

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Some files were not shown because too many files have changed in this diff Show More