diff options
| author | George Mount <mount@google.com> | 2014-03-10 16:51:16 -0700 |
|---|---|---|
| committer | George Mount <mount@google.com> | 2014-04-07 09:14:09 -0700 |
| commit | d6107a3170df61d9e776fcd5666acfc9135c6f16 (patch) | |
| tree | 8eebc42794fe87d3974a3e9bd21ab47b0244ae23 /core/java/android/transition/MoveImage.java | |
| parent | cb4b7d999e7bcba608726188421772e313e67163 (diff) | |
| download | frameworks_base-d6107a3170df61d9e776fcd5666acfc9135c6f16.zip frameworks_base-d6107a3170df61d9e776fcd5666acfc9135c6f16.tar.gz frameworks_base-d6107a3170df61d9e776fcd5666acfc9135c6f16.tar.bz2 | |
Add Transitions useful for Activity transitions.
Slide: transition in and out of the edge of the scene.
Explode: transition to the scene borders
Moved capability from Fade to Visibility.
Change-Id: Ibeb0d8f751c990edc467570d9665fbe251af2703
Diffstat (limited to 'core/java/android/transition/MoveImage.java')
| -rw-r--r-- | core/java/android/transition/MoveImage.java | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/core/java/android/transition/MoveImage.java b/core/java/android/transition/MoveImage.java new file mode 100644 index 0000000..d68e971 --- /dev/null +++ b/core/java/android/transition/MoveImage.java @@ -0,0 +1,326 @@ +/* + * 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 android.transition; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.RectEvaluator; +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.util.FloatMath; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroupOverlay; +import android.view.ViewParent; +import android.widget.ImageView; + +import java.util.ArrayList; +import java.util.Map; + +/** + * Transitions ImageViews, including size, scaleType, and matrix. The ImageView drawable + * must remain the same between both start and end states, but the + * {@link ImageView#setScaleType(android.widget.ImageView.ScaleType)} may + * differ. + */ +public class MoveImage extends Transition { + private static final String TAG = "MoveImage"; + private static final String PROPNAME_MATRIX = "android:moveImage:matrix"; + private static final String PROPNAME_BOUNDS = "android:moveImage:bounds"; + private static final String PROPNAME_CLIP = "android:moveImage:clip"; + private static final String PROPNAME_DRAWABLE = "android:moveImage:drawable"; + + private int[] mTempLoc = new int[2]; + + private static final String[] sTransitionProperties = { + PROPNAME_MATRIX, + PROPNAME_BOUNDS, + PROPNAME_CLIP, + PROPNAME_DRAWABLE, + }; + + private void captureValues(TransitionValues transitionValues) { + View view = transitionValues.view; + if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) { + return; + } + Map<String, Object> values = transitionValues.values; + + ViewGroup parent = (ViewGroup) view.getParent(); + parent.getLocationInWindow(mTempLoc); + int paddingLeft = view.getPaddingLeft(); + int paddingTop = view.getPaddingTop(); + int paddingRight = view.getPaddingRight(); + int paddingBottom = view.getPaddingBottom(); + int left = mTempLoc[0] + paddingLeft + view.getLeft() + Math.round(view.getTranslationX()); + int top = mTempLoc[1] + paddingTop + view.getTop() + Math.round(view.getTranslationY()); + int right = left + view.getWidth() - paddingRight - paddingLeft; + int bottom = top + view.getHeight() - paddingTop - paddingBottom; + + Rect bounds = new Rect(left, top, right, bottom); + values.put(PROPNAME_BOUNDS, bounds); + ImageView imageView = (ImageView) view; + Matrix matrix = getMatrix(imageView); + values.put(PROPNAME_MATRIX, matrix); + values.put(PROPNAME_CLIP, findClip(imageView)); + values.put(PROPNAME_DRAWABLE, imageView.getDrawable()); + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + captureValues(transitionValues); + } + + @Override + public String[] getTransitionProperties() { + return sTransitionProperties; + } + + /** + * Creates an Animator for ImageViews moving, changing dimensions, and/or changing + * {@link android.widget.ImageView.ScaleType}. + * @param sceneRoot The root of the transition hierarchy. + * @param startValues The values for a specific target in the start scene. + * @param endValues The values for the target in the end scene. + * @return An Animator to move an ImageView or null if the View is not an ImageView, + * the Drawable changed, the View is not VISIBLE, or there was no change. + */ + @Override + public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, + TransitionValues endValues) { + if (startValues == null || endValues == null + || startValues.values.get(PROPNAME_BOUNDS) == null + || endValues.values.get(PROPNAME_BOUNDS) == null + || startValues.values.get(PROPNAME_DRAWABLE) + != endValues.values.get(PROPNAME_DRAWABLE)) { + return null; + } + ArrayList<PropertyValuesHolder> changes = new ArrayList<PropertyValuesHolder>(); + + Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX); + Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX); + + if (!startMatrix.equals(endMatrix)) { + changes.add(PropertyValuesHolder.ofObject(MatrixClippedDrawable.MATRIX_PROPERTY, + new MatrixEvaluator(), startMatrix, endMatrix)); + } + + sceneRoot.getLocationInWindow(mTempLoc); + int rootX = mTempLoc[0]; + int rootY = mTempLoc[1]; + final ImageView imageView = (ImageView) endValues.view; + + Drawable drawable = imageView.getDrawable(); + + Rect startBounds = new Rect((Rect) startValues.values.get(PROPNAME_BOUNDS)); + Rect endBounds = new Rect((Rect) endValues.values.get(PROPNAME_BOUNDS)); + startBounds.offset(-rootX, -rootY); + endBounds.offset(-rootX, -rootY); + + if (!startBounds.equals(endBounds)) { + changes.add(PropertyValuesHolder.ofObject("bounds", new RectEvaluator(new Rect()), + startBounds, endBounds)); + } + + Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP); + Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP); + if (startClip != null || endClip != null) { + startClip = nonNullClip(startClip, sceneRoot, rootX, rootY); + endClip = nonNullClip(endClip, sceneRoot, rootX, rootY); + + expandClip(startBounds, startMatrix, startClip, endClip); + expandClip(endBounds, endMatrix, endClip, startClip); + boolean clipped = !startClip.contains(startBounds) || !endClip.contains(endBounds); + if (!clipped) { + startClip = null; + } else if (!startClip.equals(endClip)) { + changes.add(PropertyValuesHolder.ofObject(MatrixClippedDrawable.CLIP_PROPERTY, + new RectEvaluator(), startClip, endClip)); + } + } + + if (changes.isEmpty()) { + return null; + } + + drawable = drawable.getConstantState().newDrawable(); + final MatrixClippedDrawable matrixClippedDrawable = new MatrixClippedDrawable(drawable); + matrixClippedDrawable.setMatrix(startMatrix); + matrixClippedDrawable.setBounds(startBounds); + matrixClippedDrawable.setClipRect(startClip); + + imageView.setVisibility(View.INVISIBLE); + final ViewGroupOverlay overlay = sceneRoot.getOverlay(); + overlay.add(matrixClippedDrawable); + ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(matrixClippedDrawable, + changes.toArray(new PropertyValuesHolder[changes.size()])); + + AnimatorListenerAdapter listener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + imageView.setVisibility(View.VISIBLE); + overlay.remove(matrixClippedDrawable); + } + + @Override + public void onAnimationPause(Animator animation) { + imageView.setVisibility(View.VISIBLE); + overlay.remove(matrixClippedDrawable); + } + + @Override + public void onAnimationResume(Animator animation) { + imageView.setVisibility(View.INVISIBLE); + overlay.add(matrixClippedDrawable); + } + }; + + animator.addListener(listener); + animator.addPauseListener(listener); + + return animator; + } + + private static Rect nonNullClip(Rect clip, ViewGroup sceneRoot, int rootX, int rootY) { + if (clip != null) { + clip = new Rect(clip); + clip.offset(-rootX, -rootY); + } else { + clip = new Rect(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight()); + } + return clip; + } + + private static void expandClip(Rect bounds, Matrix matrix, Rect clip, Rect otherClip) { + RectF boundsF = new RectF(bounds); + matrix.mapRect(boundsF); + clip.left = expandMinDimension(boundsF.left, clip.left, otherClip.left); + clip.top = expandMinDimension(boundsF.top, clip.top, otherClip.top); + clip.right = expandMaxDimension(boundsF.right, clip.right, otherClip.right); + clip.bottom = expandMaxDimension(boundsF.bottom, clip.bottom, otherClip.bottom); + } + + private static int expandMinDimension(float boundsDimension, int clipDimension, + int otherClipDimension) { + if (clipDimension > boundsDimension) { + // Already clipped in that dimension, return the clipped value + return clipDimension; + } + return Math.min(clipDimension, otherClipDimension); + } + + private static int expandMaxDimension(float boundsDimension, int clipDimension, + int otherClipDimension) { + return -expandMinDimension(-boundsDimension, -clipDimension, -otherClipDimension); + } + + private static Matrix getMatrix(ImageView imageView) { + Drawable drawable = imageView.getDrawable(); + int drawableWidth = drawable.getIntrinsicWidth(); + int drawableHeight = drawable.getIntrinsicHeight(); + ImageView.ScaleType scaleType = imageView.getScaleType(); + if (drawableWidth <= 0 || drawableHeight <= 0 || scaleType == ImageView.ScaleType.FIT_XY) { + return null; + } + return new Matrix(imageView.getImageMatrix()); + } + + private Rect findClip(ImageView imageView) { + if (imageView.getCropToPadding()) { + Rect clip = getClip(imageView); + clip.left += imageView.getPaddingLeft(); + clip.right -= imageView.getPaddingRight(); + clip.top += imageView.getPaddingTop(); + clip.bottom -= imageView.getPaddingBottom(); + return clip; + } else { + View view = imageView; + ViewParent viewParent; + while ((viewParent = view.getParent()) instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) viewParent; + if (viewGroup.getClipChildren()) { + Rect clip = getClip(view); + return clip; + } + view = viewGroup; + } + } + return null; + } + + private Rect getClip(View clipView) { + Rect clipBounds = clipView.getClipBounds(); + if (clipBounds == null) { + clipBounds = new Rect(clipView.getLeft(), clipView.getTop(), + clipView.getRight(), clipView.getBottom()); + } + + ViewParent parent = clipView.getParent(); + if (parent instanceof ViewGroup) { + ViewGroup parentViewGroup = (ViewGroup) parent; + parentViewGroup.getLocationInWindow(mTempLoc); + clipBounds.offset(mTempLoc[0], mTempLoc[1]); + } + + return clipBounds; + } + + @Override + public Transition clone() { + MoveImage clone = (MoveImage) super.clone(); + clone.mTempLoc = new int[2]; + return clone; + } + + private static class MatrixEvaluator implements TypeEvaluator<Matrix> { + static final Matrix sIdentity = new Matrix(); + float[] mTempStartValues = new float[9]; + float[] mTempEndValues = new float[9]; + Matrix mTempMatrix = new Matrix(); + + @Override + public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) { + if (startValue == null && endValue == null) { + return null; + } + if (startValue == null) { + startValue = sIdentity; + } else if (endValue == null) { + endValue = sIdentity; + } + startValue.getValues(mTempStartValues); + endValue.getValues(mTempEndValues); + for (int i = 0; i < 9; i++) { + float diff = mTempEndValues[i] - mTempStartValues[i]; + mTempEndValues[i] = mTempStartValues[i] + (fraction * diff); + } + mTempMatrix.setValues(mTempEndValues); + return mTempMatrix; + } + } +} |
