diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index 8dde4d318..26fcbabe7 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -1293,6 +1293,25 @@ + + + + + + + + + + + + + diff --git a/samples/ApiDemos/res/drawable-nodpi/ball.jpg b/samples/ApiDemos/res/drawable-nodpi/ball.jpg new file mode 100644 index 000000000..2960b7309 Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/ball.jpg differ diff --git a/samples/ApiDemos/res/drawable-nodpi/block.jpg b/samples/ApiDemos/res/drawable-nodpi/block.jpg new file mode 100644 index 000000000..04c22a0cf Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/block.jpg differ diff --git a/samples/ApiDemos/res/drawable-nodpi/ducky.jpg b/samples/ApiDemos/res/drawable-nodpi/ducky.jpg new file mode 100644 index 000000000..830bbe347 Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/ducky.jpg differ diff --git a/samples/ApiDemos/res/drawable-nodpi/jellies.jpg b/samples/ApiDemos/res/drawable-nodpi/jellies.jpg new file mode 100644 index 000000000..ee2b5c68c Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/jellies.jpg differ diff --git a/samples/ApiDemos/res/drawable-nodpi/mug.jpg b/samples/ApiDemos/res/drawable-nodpi/mug.jpg new file mode 100644 index 000000000..e149e198a Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/mug.jpg differ diff --git a/samples/ApiDemos/res/drawable-nodpi/pencil.jpg b/samples/ApiDemos/res/drawable-nodpi/pencil.jpg new file mode 100644 index 000000000..e348311bf Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/pencil.jpg differ diff --git a/samples/ApiDemos/res/drawable-nodpi/scissors.jpg b/samples/ApiDemos/res/drawable-nodpi/scissors.jpg new file mode 100644 index 000000000..caf0ce8e2 Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/scissors.jpg differ diff --git a/samples/ApiDemos/res/drawable-nodpi/woot.jpg b/samples/ApiDemos/res/drawable-nodpi/woot.jpg new file mode 100644 index 000000000..ccaef674b Binary files /dev/null and b/samples/ApiDemos/res/drawable-nodpi/woot.jpg differ diff --git a/samples/ApiDemos/res/layout/image_block.xml b/samples/ApiDemos/res/layout/image_block.xml new file mode 100644 index 000000000..50d7d049e --- /dev/null +++ b/samples/ApiDemos/res/layout/image_block.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/ApiDemos/res/layout/image_details.xml b/samples/ApiDemos/res/layout/image_details.xml new file mode 100644 index 000000000..a1a745921 --- /dev/null +++ b/samples/ApiDemos/res/layout/image_details.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/ApiDemos/res/layout/transition_scene1.xml b/samples/ApiDemos/res/layout/transition_scene1.xml index a98da60b9..19fbe890a 100644 --- a/samples/ApiDemos/res/layout/transition_scene1.xml +++ b/samples/ApiDemos/res/layout/transition_scene1.xml @@ -18,36 +18,40 @@ android:layout_height="match_parent" android:id="@+id/container"> - - - - diff --git a/samples/ApiDemos/res/layout/transition_scene2.xml b/samples/ApiDemos/res/layout/transition_scene2.xml index fda227651..2d7e546f7 100644 --- a/samples/ApiDemos/res/layout/transition_scene2.xml +++ b/samples/ApiDemos/res/layout/transition_scene2.xml @@ -18,36 +18,40 @@ android:layout_height="match_parent" android:id="@+id/container"> - - - - diff --git a/samples/ApiDemos/res/layout/transition_scene3.xml b/samples/ApiDemos/res/layout/transition_scene3.xml index 39c4da770..43ffc386e 100644 --- a/samples/ApiDemos/res/layout/transition_scene3.xml +++ b/samples/ApiDemos/res/layout/transition_scene3.xml @@ -18,36 +18,40 @@ android:layout_height="match_parent" android:id="@+id/container"> - - - - + + + diff --git a/samples/ApiDemos/res/values/colors.xml b/samples/ApiDemos/res/values/colors.xml index f2534d17e..fd68af39e 100644 --- a/samples/ApiDemos/res/values/colors.xml +++ b/samples/ApiDemos/res/values/colors.xml @@ -28,5 +28,6 @@ #0000ff #f0f0 #ffffff00 + #ff884488 diff --git a/samples/ApiDemos/res/values/styles.xml b/samples/ApiDemos/res/values/styles.xml index 49a1c25a5..4d7894f1e 100644 --- a/samples/ApiDemos/res/values/styles.xml +++ b/samples/ApiDemos/res/values/styles.xml @@ -121,4 +121,13 @@ wrap_content wrap_content + + + diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/ActivityTransition.java b/samples/ApiDemos/src/com/example/android/apis/animation/ActivityTransition.java new file mode 100644 index 000000000..c3171c43e --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/animation/ActivityTransition.java @@ -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_BOUNDS + = new Property(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; + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/ActivityTransitionDetails.java b/samples/ApiDemos/src/com/example/android/apis/animation/ActivityTransitionDetails.java new file mode 100644 index 000000000..d0b2c4f5a --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/animation/ActivityTransitionDetails.java @@ -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_BOUNDS + = new Property(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; + } + +} diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/Fall.java b/samples/ApiDemos/src/com/example/android/apis/animation/Fall.java new file mode 100644 index 000000000..e7281b383 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/animation/Fall.java @@ -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())); + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/animation/Up.java b/samples/ApiDemos/src/com/example/android/apis/animation/Up.java new file mode 100644 index 000000000..4d3bba5b8 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/animation/Up.java @@ -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); + } +}