am 77dfe829: Cross-Activity Scene transitions API demo.

* commit '77dfe829d14f9d254f0b535e9bf4dd382e1c9c61':
  Cross-Activity Scene transitions API demo.
This commit is contained in:
George Mount
2014-02-04 16:55:35 -08:00
committed by Android Git Automerger
21 changed files with 1139 additions and 36 deletions

View File

@@ -1293,6 +1293,25 @@
<!-- ANDROID.ANIMATION PACKAGE SAMPLES -->
<!-- ************************************* -->
<activity android:name=".animation.ActivityTransition"
android:label="Animation/Activity Transition"
android:enabled="@bool/atLeastHoneycomb"
android:theme="@style/ActivityTransitionTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".animation.ActivityTransitionDetails"
android:label="Animation/Activity Transition Details"
android:enabled="@bool/atLeastHoneycomb"
android:theme="@style/ActivityTransitionTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".animation.AnimationLoading"
android:label="Animation/Loading"
android:enabled="@bool/atLeastHoneycomb">

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:columnCount="2"
android:rowCount="4"
>
<ImageView android:id="@+id/ducky"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="center"
android:src="@drawable/ducky"
android:layout_column="0"
android:layout_row="0"
android:onClick="clicked"
/>
<ImageView android:id="@+id/woot"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="center"
android:src="@drawable/woot"
android:layout_column="1"
android:layout_row="0"
android:onClick="clicked"
/>
<ImageView android:id="@+id/ball"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="center"
android:src="@drawable/ball"
android:layout_column="0"
android:layout_row="1"
android:onClick="clicked"
/>
<ImageView android:id="@+id/block"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="center"
android:src="@drawable/block"
android:layout_column="1"
android:layout_row="1"
android:onClick="clicked"
/>
<ImageView android:id="@+id/jellies"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="center"
android:src="@drawable/jellies"
android:layout_column="0"
android:layout_row="2"
android:onClick="clicked"
/>
<ImageView android:id="@+id/mug"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="center"
android:src="@drawable/mug"
android:layout_column="1"
android:layout_row="2"
android:onClick="clicked"
/>
<ImageView android:id="@+id/pencil"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="center"
android:src="@drawable/pencil"
android:layout_column="0"
android:layout_row="3"
android:onClick="clicked"
/>
<ImageView android:id="@+id/scissors"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="center"
android:src="@drawable/scissors"
android:layout_column="1"
android:layout_row="3"
android:onClick="clicked"
/>
</GridLayout>

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<ImageView android:id="@+id/titleImage"
android:layout_height="0px"
android:layout_weight="1"
android:layout_width="match_parent"
android:scaleType="centerCrop"
android:onClick="clicked"
android:sharedElementName="hero"
/>
<LinearLayout android:layout_height="0px"
android:layout_width="match_parent"
android:layout_weight="2"
android:orientation="vertical"
>
<View android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#808080"/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Ducky"
android:textSize="30sp"
/>
<View android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#808080"/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Woot!"
android:textSize="30sp"
/>
<View android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#808080"/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Ball"
android:textSize="30sp"
/>
<View android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#808080"/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Block"
android:textSize="30sp"
/>
<View android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#808080"/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Jelly Bean"
android:textSize="30sp"
/>
<View android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#808080"/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Mug"
android:textSize="30sp"
/>
<View android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#808080"/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Pencil"
android:textSize="30sp"
/>
<View android:layout_width="match_parent"
android:layout_height="2dp"
android:background="#808080"/>
<TextView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Scissors"
android:textSize="30sp"
/>
</LinearLayout>
</LinearLayout>

View File

@@ -18,36 +18,40 @@
android:layout_height="match_parent"
android:id="@+id/container">
<View
android:layout_width="100dip"
android:layout_height="50dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:background="#f00"
android:text="Hello"
android:id="@+id/view1"/>
<View
android:layout_width="100dip"
android:layout_height="50dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:background="#0f0"
android:text="World"
android:id="@+id/view2"/>
<View
android:layout_width="100dip"
android:layout_height="50dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00f"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="Foo"
android:id="@+id/view3"/>
<View
android:layout_width="100dip"
android:layout_height="50dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0ff"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="Bar"
android:id="@+id/view4"/>
</RelativeLayout>

View File

@@ -18,36 +18,40 @@
android:layout_height="match_parent"
android:id="@+id/container">
<View
android:layout_width="100dip"
android:layout_height="100dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:background="#f00"
android:text="Hello"
android:id="@+id/view1"/>
<View
android:layout_width="100dip"
android:layout_height="100dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="#0f0"
android:text="World"
android:id="@+id/view2"/>
<View
android:layout_width="100dip"
android:layout_height="100dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00f"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="Foo"
android:id="@+id/view3"/>
<View
android:layout_width="100dip"
android:layout_height="100dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0ff"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:text="Bar"
android:id="@+id/view4"/>
</RelativeLayout>

View File

@@ -18,36 +18,40 @@
android:layout_height="match_parent"
android:id="@+id/container">
<View
android:layout_width="100dip"
android:layout_height="50dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:background="#f00"
android:text="Hello"
android:id="@+id/view1"/>
<View
android:layout_width="100dip"
android:layout_height="50dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:background="#0f0"
android:text="World"
android:id="@+id/view2"/>
<View
android:layout_width="100dip"
android:layout_height="50dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00f"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="Foo"
android:id="@+id/view3"/>
<View
android:layout_width="100dip"
android:layout_height="50dip"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#0ff"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="Bar"
android:id="@+id/view4"/>
<LinearLayout

View File

@@ -0,0 +1,17 @@
<?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.
-->
<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
</transitionManager>

View File

@@ -28,5 +28,6 @@
<color name="solid_blue">#0000ff</color>
<color name="solid_green">#f0f0</color>
<color name="solid_yellow">#ffffff00</color>
<color name="purply">#ff884488</color>
</resources>

View File

@@ -121,4 +121,13 @@
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
<style name="ActivityTransitionTheme" parent="android:style/Theme.Holo">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<!--
-->
<item name="android:windowContentTransitionManager">@transition/activity_transition_mgr</item>
</style>
</resources>

View File

@@ -0,0 +1,198 @@
/*
* 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.apis.animation;
import com.example.android.apis.R;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.RectEvaluator;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroupOverlay;
import android.view.Window;
import android.widget.ImageView;
import java.util.Random;
/**
*
*/
public class ActivityTransition extends Activity {
private static final String TAG = "ActivityTransition";
private static final String KEY_LEFT_ON_SCREEN = "ViewTransitionValues:left:";
private static final String KEY_TOP_ON_SCREEN = "ViewTransitionValues:top:";
private static final String KEY_WIDTH = "ViewTransitionValues:width:";
private static final String KEY_HEIGHT = "ViewTransitionValues:height:";
private static final String KEY_ID = "ViewTransitionValues:id";
private Random mRandom = new Random();
private boolean mComeBack;
private Fall mFall;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setEarlyBackgroundTransition(false);
getWindow().setBackgroundDrawable(new ColorDrawable(randomColor()));
setContentView(R.layout.image_block);
View hero = getHero();
if (hero != null) {
hero.setSharedElementName("hero");
}
TransitionManager transitionManager = getContentTransitionManager();
TransitionSet transitions = new TransitionSet();
Fall fall = new Fall();
fall.setDuration(600);
fall.setStartDelay(600);
fall.setHero(hero);
transitions.addTransition(fall);
transitions.addTransition(new Up());
transitionManager.setTransition("null", getContentScene(), transitions);
transitions = new TransitionSet();
mFall = new Fall();
mFall.setDuration(600);
transitions.addTransition(mFall);
transitions.addTransition(new Up());
transitionManager.setTransition(getContentScene(), "null", transitions);
}
@Override
protected void onResume() {
super.onResume();
if (mComeBack) {
mComeBack = false;
setContentView(R.layout.image_block);
}
}
private View getHero() {
Bundle transitionArgs = getTransitionArgs();
if (transitionArgs == null) {
return null;
}
int id = transitionArgs.getInt(KEY_ID);
return findViewById(id);
}
private static Property<Drawable, Rect> DRAWABLE_BOUNDS
= new Property<Drawable, Rect>(Rect.class, "bounds") {
@Override
public Rect get(Drawable object) {
return null;
}
@Override
public void set(Drawable object, Rect value) {
object.setBounds(value);
object.invalidateSelf();
}
};
@Override
public void startSharedElementTransition(Bundle transitionArgs) {
final ImageView hero = (ImageView)getHero();
hero.setSharedElementName(null);
hero.setVisibility(View.INVISIBLE);
int[] loc = new int[2];
hero.getLocationOnScreen(loc);
int endScreenLeft = loc[0];
int endScreenTop = loc[1];
int originalWidth = hero.getWidth();
int originalHeight = hero.getHeight();
hero.setVisibility(View.INVISIBLE);
ViewGroup sceneRoot = getContentScene().getSceneRoot();
sceneRoot.getLocationOnScreen(loc);
int overlayLeft = loc[0];
int overlayTop = loc[1];
final ViewGroupOverlay overlay = sceneRoot.getOverlay();
int endX = endScreenLeft - overlayLeft;
int endY = endScreenTop - overlayTop;
int startX = transitionArgs.getInt(KEY_LEFT_ON_SCREEN) - overlayLeft;
int startY = transitionArgs.getInt(KEY_TOP_ON_SCREEN) - overlayTop;
int startWidth = transitionArgs.getInt(KEY_WIDTH);
int startHeight = transitionArgs.getInt(KEY_HEIGHT);
int endHeight = originalWidth * startHeight / startWidth;
final Drawable image = hero.getDrawable();
Rect startBounds = new Rect(startX, startY, startX + startWidth, startY + startHeight);
endY += originalHeight - endHeight;
Rect endBounds = new Rect(endX, endY, endX + originalWidth, endY + endHeight);
ObjectAnimator boundsAnimator = ObjectAnimator.ofObject(image, DRAWABLE_BOUNDS,
new RectEvaluator(new Rect()), startBounds, endBounds);
hero.setImageDrawable(null);
image.setBounds(startBounds);
overlay.add(image);
boundsAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
overlay.remove(image);
hero.setImageDrawable(image);
hero.setVisibility(View.VISIBLE);
}
});
boundsAnimator.start();
}
public void clicked(View v) {
v.setSharedElementName("hero");
mFall.setHero(v);
Intent intent = new Intent(this, ActivityTransitionDetails.class);
Bundle args = getHeroInfo(v);
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(args);
startActivity(intent, options.toBundle());
//v.setTranslationZ(300);
mComeBack = true;
}
private int randomColor() {
int red = mRandom.nextInt(128);
int green = mRandom.nextInt(128);
int blue = mRandom.nextInt(128);
return 0xFF000000 | (red << 16) | (green << 8) | blue;
}
static Bundle getHeroInfo(View view) {
int[] loc = new int[2];
view.getLocationOnScreen(loc);
Bundle bundle = new Bundle();
bundle.putInt(KEY_LEFT_ON_SCREEN, loc[0]);
bundle.putInt(KEY_TOP_ON_SCREEN, loc[1]);
bundle.putInt(KEY_WIDTH, view.getWidth());
bundle.putInt(KEY_HEIGHT, view.getHeight());
bundle.putInt(KEY_ID, view.getId());
return bundle;
}
}

View File

@@ -0,0 +1,240 @@
/*
* Copyright (C) 2014 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.apis.animation;
import com.example.android.apis.R;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.RectEvaluator;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.Log;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.ImageView;
import java.util.Random;
/**
*
*/
public class ActivityTransitionDetails extends Activity {
private static final String TAG = "ActivityTransitionDetails";
private static final String KEY_LEFT_ON_SCREEN = "ViewTransitionValues:left:";
private static final String KEY_TOP_ON_SCREEN = "ViewTransitionValues:top:";
private static final String KEY_WIDTH = "ViewTransitionValues:width:";
private static final String KEY_HEIGHT = "ViewTransitionValues:height:";
private static final String KEY_ID = "ViewTransitionValues:id";
private Random mRandom = new Random();
private boolean mComeBack;
private int mImageResourceId = R.drawable.ducky;
private int mId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setEarlyBackgroundTransition(false);
getWindow().setBackgroundDrawable(new ColorDrawable(randomColor()));
setContentView(R.layout.image_details);
setImageMatrix();
ImageView hero = (ImageView)findViewById(R.id.titleImage);
hero.setImageDrawable(getHeroDrawable());
//hero.setTranslationZ(300);
TransitionManager transitionManager = getContentTransitionManager();
TransitionSet transitions = new TransitionSet();
Fall fall = new Fall();
fall.setDuration(600);
fall.setStartDelay(600);
transitions.addTransition(fall);
transitions.addTransition(new Up());
transitionManager.setTransition("null", getContentScene(), transitions);
transitions = new TransitionSet();
fall = new Fall();
fall.setDuration(600);
transitions.addTransition(fall);
transitions.addTransition(new Up());
transitionManager.setTransition(getContentScene(), "null", transitions);
}
@Override
protected void onResume() {
super.onResume();
if (mComeBack) {
mComeBack = false;
setContentView(R.layout.image_details);
ImageView hero = (ImageView)findViewById(R.id.titleImage);
hero.setImageDrawable(getHeroDrawable());
setImageMatrix();
}
}
private Drawable getHeroDrawable() {
Bundle args = getTransitionArgs();
int id = args == null ? 0 : args.getInt(KEY_ID);
int resourceId;
switch (id) {
case R.id.ducky: resourceId = R.drawable.ducky; break;
case R.id.jellies: resourceId = R.drawable.jellies; break;
case R.id.mug: resourceId = R.drawable.mug; break;
case R.id.pencil: resourceId = R.drawable.pencil; break;
case R.id.scissors: resourceId = R.drawable.scissors; break;
case R.id.woot: resourceId = R.drawable.woot; break;
case R.id.ball: resourceId = R.drawable.ball; break;
case R.id.block: resourceId = R.drawable.block; break;
default:
resourceId = mImageResourceId;
break;
}
mImageResourceId = resourceId;
return getResources().getDrawable(resourceId);
}
private static Property<Drawable, Rect> DRAWABLE_BOUNDS
= new Property<Drawable, Rect>(Rect.class, "bounds") {
@Override
public Rect get(Drawable object) {
return null;
}
@Override
public void set(Drawable object, Rect value) {
object.setBounds(value);
object.invalidateSelf();
}
};
private void setImageMatrix() {
getWindow().getDecorView().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getWindow().getDecorView().getViewTreeObserver().removeOnPreDrawListener(this);
// Use an image matrix such that it aligns with the bottom.
final ImageView hero = (ImageView)findViewById(R.id.titleImage);
hero.setScaleType(ImageView.ScaleType.MATRIX);
Matrix matrix = hero.getImageMatrix();
int height = hero.getHeight();
int width = hero.getWidth();
Drawable image = hero.getDrawable();
int intrinsicHeight = image.getIntrinsicHeight();
int intrinsicWidth = image.getIntrinsicWidth();
int scaledHeight = intrinsicHeight * width / intrinsicWidth;
matrix.postTranslate(0, (height - scaledHeight) / 2);
hero.setImageMatrix(matrix);
return true;
}
});
}
@Override
public void startSharedElementTransition(Bundle transitionArgs) {
mId = transitionArgs.getInt(KEY_ID);
final ImageView hero = (ImageView)findViewById(R.id.titleImage);
int[] loc = new int[2];
hero.getLocationOnScreen(loc);
int endScreenLeft = loc[0];
int endScreenTop = loc[1];
int originalWidth = hero.getWidth();
int originalHeight = hero.getHeight();
hero.setVisibility(View.INVISIBLE);
ViewGroup sceneRoot = getContentScene().getSceneRoot();
sceneRoot.getLocationOnScreen(loc);
int overlayLeft = loc[0];
int overlayTop = loc[1];
final ViewGroupOverlay overlay = sceneRoot.getOverlay();
int endX = endScreenLeft - overlayLeft;
int endY = endScreenTop - overlayTop;
int startX = transitionArgs.getInt(KEY_LEFT_ON_SCREEN) - overlayLeft;
int startY = transitionArgs.getInt(KEY_TOP_ON_SCREEN) - overlayTop;
int startWidth = transitionArgs.getInt(KEY_WIDTH);
int startHeight = transitionArgs.getInt(KEY_HEIGHT);
int endHeight = originalWidth * startHeight / startWidth;
final Drawable image = hero.getDrawable();
Rect startBounds = new Rect(startX, startY, startX + startWidth, startY + startHeight);
endY += originalHeight - endHeight;
Rect endBounds = new Rect(endX, endY, endX + originalWidth, endY + endHeight);
ObjectAnimator boundsAnimator = ObjectAnimator.ofObject(image, DRAWABLE_BOUNDS,
new RectEvaluator(new Rect()), startBounds, endBounds);
hero.setImageDrawable(null);
image.setBounds(startBounds);
overlay.add(image);
boundsAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
overlay.remove(image);
hero.setImageDrawable(image);
hero.setVisibility(View.VISIBLE);
}
});
boundsAnimator.start();
}
public void clicked(View v) {
Intent intent = new Intent(this, ActivityTransition.class);
Bundle args = getHeroInfo((ImageView)v);
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(args);
startActivity(intent, options.toBundle());
mComeBack = true;
}
private int randomColor() {
int red = mRandom.nextInt(128);
int green = mRandom.nextInt(128);
int blue = mRandom.nextInt(128);
return 0xFF000000 | (red << 16) | (green << 8) | blue;
}
private Bundle getHeroInfo(ImageView view) {
int[] loc = new int[2];
view.getLocationOnScreen(loc);
Bundle bundle = new Bundle();
Drawable image = view.getDrawable();
int intrinsicWidth = image.getIntrinsicWidth();
int intrinsicHeight = image.getIntrinsicHeight();
int width = view.getWidth();
int height = intrinsicHeight * width / intrinsicWidth;
int top = loc[1] + view.getHeight() - height;
bundle.putInt(KEY_LEFT_ON_SCREEN, loc[0]);
bundle.putInt(KEY_TOP_ON_SCREEN, top);
bundle.putInt(KEY_WIDTH, width);
bundle.putInt(KEY_HEIGHT, height);
bundle.putInt(KEY_ID, mId);
return bundle;
}
}

View File

@@ -0,0 +1,335 @@
/*
* Copyright (C) 2014 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.apis.animation;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.transition.Transition;
import android.transition.TransitionValues;
import android.transition.Visibility;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
/**
*
*/
public class Fall extends Visibility {
private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
private static final String TAG = "Fall";
private static final String PROPNAME_SCREEN_LOCATION = "android:fade:screen_location";
private View mHero;
public void setHero(View hero) {
mHero = hero;
}
private Animator createAnimation(final View view, long startDelay, final float startY,
float endY, AnimatorListenerAdapter listener, TimeInterpolator interpolator) {
if (startY == endY) {
// run listener if we're noop'ing the animation, to get the end-state results now
if (listener != null) {
listener.onAnimationEnd(null);
}
return null;
}
final ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
if (listener != null) {
anim.addListener(listener);
anim.addPauseListener(listener);
}
anim.setInterpolator(interpolator);
anim.setStartDelay(startDelay);
AnimatorSet wrapper = new AnimatorSet();
wrapper.play(anim);
wrapper.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
view.setTranslationY(startY);
}
});
return wrapper;
}
private void captureValues(TransitionValues transitionValues) {
int[] loc = new int[2];
transitionValues.view.getLocationOnScreen(loc);
transitionValues.values.put(PROPNAME_SCREEN_LOCATION, loc);
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
super.captureStartValues(transitionValues);
captureValues(transitionValues);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
super.captureEndValues(transitionValues);
captureValues(transitionValues);
}
@Override
public Animator onAppear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
if (endValues == null) {
return null;
}
final View endView = endValues.view;
Log.v(TAG, "onAppear: " + endView.getId());
final float endY = endView.getTranslationY();
final float startY = endY + sceneRoot.getHeight();
TransitionListener transitionListener = new TransitionListener() {
boolean mCanceled = false;
float mPausedY;
@Override
public void onTransitionCancel(Transition transition) {
endView.setTranslationY(endY);
mCanceled = true;
}
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
if (!mCanceled) {
endView.setTranslationY(endY);
}
}
@Override
public void onTransitionPause(Transition transition) {
mPausedY = endView.getTranslationY();
endView.setTranslationY(endY);
}
@Override
public void onTransitionResume(Transition transition) {
endView.setTranslationY(mPausedY);
}
};
addListener(transitionListener);
int[] loc = (int[]) endValues.values.get(PROPNAME_SCREEN_LOCATION);
long startDelay = calculateRiseStartDelay(sceneRoot, endView, loc);
return createAnimation(endView, startDelay, startY, endY, null, sDecelerate);
}
@Override
public Animator onDisappear(ViewGroup sceneRoot,
TransitionValues startValues, int startVisibility,
TransitionValues endValues, int endVisibility) {
View view = null;
View startView = (startValues != null) ? startValues.view : null;
View endView = (endValues != null) ? endValues.view : null;
View overlayView = null;
View viewToKeep = null;
if (endView == null || endView.getParent() == null) {
if (endView != null) {
// endView was removed from its parent - add it to the overlay
view = overlayView = endView;
} else if (startView != null) {
// endView does not exist. Use startView only under certain
// conditions, because placing a view in an overlay necessitates
// it being removed from its current parent
if (startView.getParent() == null) {
// no parent - safe to use
view = overlayView = startView;
} else if (startView.getParent() instanceof View &&
startView.getParent().getParent() == null) {
View startParent = (View) startView.getParent();
int id = startParent.getId();
if (id != View.NO_ID && sceneRoot.findViewById(id) != null && canRemoveViews()) {
// no parent, but its parent is unparented but the parent
// hierarchy has been replaced by a new hierarchy with the same id
// and it is safe to un-parent startView
view = overlayView = startView;
}
}
}
} else {
// visibility change
if (endVisibility == View.INVISIBLE) {
view = endView;
viewToKeep = view;
} else {
// Becoming GONE
if (startView == endView) {
view = endView;
viewToKeep = view;
} else {
view = startView;
overlayView = view;
}
}
}
final int finalVisibility = endVisibility;
int[] loc = (int[]) startValues.values.get(PROPNAME_SCREEN_LOCATION);
// TODO: add automatic facility to Visibility superclass for keeping views around
if (overlayView != null) {
// TODO: Need to do this for general case of adding to overlay
long startDelay = calculateFallStartDelay(sceneRoot, overlayView, loc);
int screenX = loc[0];
int screenY = loc[1];
loc = new int[2];
sceneRoot.getLocationOnScreen(loc);
overlayView.offsetLeftAndRight((screenX - loc[0]) - overlayView.getLeft());
overlayView.offsetTopAndBottom((screenY - loc[1]) - overlayView.getTop());
sceneRoot.getOverlay().add(overlayView);
// TODO: add automatic facility to Visibility superclass for keeping views around
final float startY = overlayView.getTranslationY();
float endY = startY + sceneRoot.getHeight();
final View finalView = view;
final View finalOverlayView = overlayView;
final View finalViewToKeep = viewToKeep;
final ViewGroup finalSceneRoot = sceneRoot;
final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
finalView.setTranslationY(startY);
// TODO: restore view offset from overlay repositioning
if (finalViewToKeep != null) {
finalViewToKeep.setVisibility(finalVisibility);
}
if (finalOverlayView != null) {
finalSceneRoot.getOverlay().remove(finalOverlayView);
}
}
@Override
public void onAnimationPause(Animator animation) {
if (finalOverlayView != null) {
finalSceneRoot.getOverlay().remove(finalOverlayView);
}
}
@Override
public void onAnimationResume(Animator animation) {
if (finalOverlayView != null) {
finalSceneRoot.getOverlay().add(finalOverlayView);
}
}
};
return createAnimation(view, startDelay, startY, endY, endListener, sAccelerate);
}
if (viewToKeep != null) {
long startDelay = calculateFallStartDelay(sceneRoot, viewToKeep, loc);
// TODO: find a different way to do this, like just changing the view to be
// VISIBLE for the duration of the transition
viewToKeep.setVisibility((View.VISIBLE));
// TODO: add automatic facility to Visibility superclass for keeping views around
final float startY = viewToKeep.getTranslationY();
float endY = startY + sceneRoot.getHeight();
final View finalView = view;
final View finalOverlayView = overlayView;
final View finalViewToKeep = viewToKeep;
final ViewGroup finalSceneRoot = sceneRoot;
final AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
boolean mCanceled = false;
float mPausedY = -1;
@Override
public void onAnimationPause(Animator animation) {
if (finalViewToKeep != null && !mCanceled) {
finalViewToKeep.setVisibility(finalVisibility);
}
mPausedY = finalView.getTranslationY();
finalView.setTranslationY(startY);
}
@Override
public void onAnimationResume(Animator animation) {
if (finalViewToKeep != null && !mCanceled) {
finalViewToKeep.setVisibility(View.VISIBLE);
}
finalView.setTranslationY(mPausedY);
}
@Override
public void onAnimationCancel(Animator animation) {
mCanceled = true;
if (mPausedY >= 0) {
finalView.setTranslationY(mPausedY);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (!mCanceled) {
finalView.setTranslationY(startY);
}
// TODO: restore view offset from overlay repositioning
if (finalViewToKeep != null && !mCanceled) {
finalViewToKeep.setVisibility(finalVisibility);
}
if (finalOverlayView != null) {
finalSceneRoot.getOverlay().remove(finalOverlayView);
}
}
};
return createAnimation(view, startDelay, startY, endY, endListener, sAccelerate);
}
return null;
}
private long calculateFallStartDelay(View sceneRoot, View view, int[] viewLoc) {
int[] loc = new int[2];
sceneRoot.getLocationOnScreen(loc);
int bottom = loc[1] + sceneRoot.getHeight();
float distance = bottom - viewLoc[1] + view.getTranslationY();
if (mHero != null) {
mHero.getLocationOnScreen(loc);
float heroX = loc[0] + mHero.getTranslationX() + (mHero.getWidth() / 2.0f);
float viewX = viewLoc[0] + view.getTranslationX() + (view.getWidth() / 2.0f);
float distanceX = Math.abs(heroX - viewX);
float distanceXRatio = distanceX / sceneRoot.getWidth();
distance += (1 - distanceXRatio) * mHero.getHeight();
}
float distanceRatio = distance/sceneRoot.getHeight() / 3;
return Math.max(0, Math.round(distanceRatio * getDuration()));
}
private long calculateRiseStartDelay(View sceneRoot, View view, int[] viewLoc) {
int[] loc = new int[2];
sceneRoot.getLocationOnScreen(loc);
int top = loc[1];
float distance = viewLoc[1] + view.getTranslationY() - top;
if (mHero != null) {
mHero.getLocationOnScreen(loc);
float heroX = loc[0] + mHero.getTranslationX() + (mHero.getWidth() / 2.0f);
float viewX = viewLoc[0] + view.getTranslationX() + (view.getWidth() / 2.0f);
float distanceX = Math.abs(heroX - viewX);
float distanceXRatio = distanceX / sceneRoot.getWidth();
distance += distanceXRatio * mHero.getHeight();
}
float distanceRatio = distance/sceneRoot.getHeight() / 3;
return Math.max(0, Math.round(distanceRatio * getDuration()));
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2014 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.apis.animation;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.transition.Transition;
import android.transition.TransitionValues;
import android.view.View;
import android.view.ViewGroup;
/**
*
*/
public class Up extends Transition {
private static final String PROPNAME_Z = "android:z:height";
private static final String[] sTransitionProperties = {
PROPNAME_Z,
};
@Override
public void captureStartValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
captureValues(transitionValues);
}
private void captureValues(TransitionValues transitionValues) {
View view = transitionValues.view;
transitionValues.values.put(PROPNAME_Z, view.getTranslationZ());
}
@Override
public String[] getTransitionProperties() {
return sTransitionProperties;
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
final float startZ = (Float)startValues.values.get(PROPNAME_Z);
final float endZ = (Float)endValues.values.get(PROPNAME_Z);
if (startZ == endZ) {
return null;
}
final View view = endValues.view;
TransitionListener transitionListener = new TransitionListener() {
boolean mCanceled = false;
float mPausedZ;
@Override
public void onTransitionCancel(Transition transition) {
view.setTranslationZ(endZ);
mCanceled = true;
}
@Override
public void onTransitionStart(Transition transition) {
}
@Override
public void onTransitionEnd(Transition transition) {
if (!mCanceled) {
view.setTranslationZ(endZ);
}
}
@Override
public void onTransitionPause(Transition transition) {
mPausedZ = view.getTranslationZ();
view.setTranslationZ(endZ);
}
@Override
public void onTransitionResume(Transition transition) {
view.setTranslationZ(mPausedZ);
}
};
addListener(transitionListener);
return ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
}
}