From ce34a9768b01115def95f000a6a8f35870f10d3a Mon Sep 17 00:00:00 2001 From: Patrick Dubroy Date: Tue, 19 Oct 2010 10:34:32 -0700 Subject: Animate items into place when dropping on home screen --- res/values/config.xml | 8 +++ src/com/android/launcher2/CellLayout.java | 72 +++++++++++++++++++-- src/com/android/launcher2/Workspace.java | 103 +++++++++++++++++------------- 3 files changed, 133 insertions(+), 50 deletions(-) diff --git a/res/values/config.xml b/res/values/config.xml index 9dee154c0..9350ad115 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -24,4 +24,12 @@ 128 + + + + 400 + + + 800 diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java index 29ff679ba..c9be887d3 100644 --- a/src/com/android/launcher2/CellLayout.java +++ b/src/com/android/launcher2/CellLayout.java @@ -119,6 +119,9 @@ public class CellLayout extends ViewGroup implements Dimmable { private boolean mDragging = false; + private ObjectAnimator mDropAnim; + private TimeInterpolator mEaseOutInterpolator; + public CellLayout(Context context) { this(context, null); } @@ -177,7 +180,7 @@ public class CellLayout extends ViewGroup implements Dimmable { // Initialize the data structures used for the drag visualization. mCrosshairsDrawable = res.getDrawable(R.drawable.gardening_crosshairs); - TimeInterpolator interp = new DecelerateInterpolator(2.5f); // Quint ease out + mEaseOutInterpolator = new DecelerateInterpolator(2.5f); // Quint ease out // Set up the animation for fading the crosshairs in and out int animDuration = res.getInteger(R.integer.config_crosshairsFadeInTime); @@ -188,7 +191,7 @@ public class CellLayout extends ViewGroup implements Dimmable { CellLayout.this.invalidate(); } }); - mCrosshairsAnimator.getAnimator().setInterpolator(interp); + mCrosshairsAnimator.getAnimator().setInterpolator(mEaseOutInterpolator); for (int i = 0; i < mDragOutlines.length; i++) { mDragOutlines[i] = new Point(-1, -1); @@ -209,7 +212,7 @@ public class CellLayout extends ViewGroup implements Dimmable { for (int i = 0; i < mDragOutlineAnims.length; i++) { final InterruptibleInOutAnimator anim = new InterruptibleInOutAnimator(duration, fromAlphaValue, toAlphaValue); - anim.getAnimator().setInterpolator(interp); + anim.getAnimator().setInterpolator(mEaseOutInterpolator); final int thisIndex = i; anim.getAnimator().addUpdateListener(new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { @@ -245,6 +248,10 @@ public class CellLayout extends ViewGroup implements Dimmable { }); mDragOutlineAnims[i] = anim; } + + mDropAnim = new ObjectAnimator(); + mDropAnim.setInterpolator(mEaseOutInterpolator); + mBackgroundRect = new Rect(); mHoverRect = new Rect(); setHoverScale(1.0f); @@ -752,12 +759,46 @@ public class CellLayout extends ViewGroup implements Dimmable { } } + /** + * Animate a child of this CellLayout into its current layout position. + * The position to animate from is given by the oldX and oldY values in its LayoutParams. + */ + private void animateChildIntoPosition(final View child) { + final Resources res = getResources(); + final ObjectAnimator anim = mDropAnim; + final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); + final float startX = lp.oldX - lp.x; + final float startY = lp.oldY - lp.y; + + // Calculate the duration of the animation based on the object's distance + final float dist = (float) Math.sqrt(startX*startX + startY*startY); + final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist); + final int duration = (int) (res.getInteger(R.integer.config_dropAnimMaxDuration) + * mEaseOutInterpolator.getInterpolation(dist / maxDist)); + + anim.cancel(); // Make sure it's not already running + anim.setDuration(duration); + anim.setTarget(child); + anim.setPropertyName("translationX"); + anim.setFloatValues(startX, 0); + + anim.removeAllUpdateListeners(); + anim.addUpdateListener(new AnimatorUpdateListener() { + public void onAnimationUpdate(ValueAnimator animation) { + // Set the value of translationY based on the current x value + final float translationX = (Float) anim.getAnimatedValue(); + child.setTranslationY((startY / startX) * translationX); + } + }); + anim.start(); + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int count = getChildCount(); for (int i = 0; i < count; i++) { - View child = getChildAt(i); + final View child = getChildAt(i); if (child.getVisibility() != GONE) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); @@ -774,6 +815,8 @@ public class CellLayout extends ViewGroup implements Dimmable { mWallpaperManager.sendWallpaperCommand(getWindowToken(), "android.home.drop", cellXY[0] + childLeft + lp.width / 2, cellXY[1] + childTop + lp.height / 2, 0, null); + + animateChildIntoPosition(child); } } } @@ -1176,7 +1219,7 @@ public class CellLayout extends ViewGroup implements Dimmable { /** * Mark a child as having been dropped. * At the beginning of the drag operation, the child may have been on another - * screen, but it is reparented before this method is called. + * screen, but it is re-parented before this method is called. * * @param child The child that is being dropped */ @@ -1185,13 +1228,17 @@ public class CellLayout extends ViewGroup implements Dimmable { LayoutParams lp = (LayoutParams) child.getLayoutParams(); lp.isDragging = false; lp.dropped = true; + child.setVisibility(View.VISIBLE); child.requestLayout(); } } void onDropAborted(View child) { if (child != null) { - ((LayoutParams) child.getLayoutParams()).isDragging = false; + LayoutParams lp = (LayoutParams) child.getLayoutParams(); + lp.isDragging = false; + child.setVisibility(View.VISIBLE); + animateChildIntoPosition(child); } } @@ -1203,6 +1250,7 @@ public class CellLayout extends ViewGroup implements Dimmable { void onDragChild(View child) { LayoutParams lp = (LayoutParams) child.getLayoutParams(); lp.isDragging = true; + child.setVisibility(View.GONE); } /** @@ -1431,6 +1479,18 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) { @ViewDebug.ExportedProperty int y; + /** + * The old X coordinate of this item, relative to its current parent. + * Used to animate the item into its new position. + */ + int oldX; + + /** + * The old Y coordinate of this item, relative to its current parent. + * Used to animate the item into its new position. + */ + int oldY; + boolean dropped; public LayoutParams(Context c, AttributeSet attrs) { diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index a82cb7f00..dd622a6aa 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -19,11 +19,11 @@ package com.android.launcher2; import com.android.launcher.R; import android.animation.Animator; +import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; -import android.animation.Animator.AnimatorListener; import android.app.WallpaperManager; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; @@ -1031,8 +1031,6 @@ public class Workspace extends SmoothPagedView mDragController.startDrag(b, screenX, screenY, 0, 0, bmpWidth, bmpHeight, this, child.getTag(), DragController.DRAG_ACTION_MOVE, null); b.recycle(); - - child.setVisibility(View.GONE); } void addApplicationShortcut(ShortcutInfo info, int screen, int cellX, int cellY, @@ -1048,16 +1046,38 @@ public class Workspace extends SmoothPagedView cellXY[0], cellXY[1]); } + private void setPositionForDropAnimation( + View dragView, int dragViewX, int dragViewY, View parent, View child) { + final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); + + // Based on the position of the drag view, find the top left of the original view + int viewX = dragViewX + (dragView.getWidth() - child.getWidth()) / 2; + int viewY = dragViewY + (dragView.getHeight() - child.getHeight()) / 2; + viewX -= getResources().getInteger(R.integer.config_dragViewOffsetX); + viewY -= getResources().getInteger(R.integer.config_dragViewOffsetY); + + // Set its old pos (in the new parent's coordinates); the CellLayout will + // animate it from this position during the next layout pass + lp.oldX = viewX - (parent.getLeft() - mScrollX); + lp.oldY = viewY - (parent.getTop() - mScrollY); + } public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, DragView dragView, Object dragInfo) { + + int originX = x - xOffset; + int originY = y - yOffset; + if (mDragTargetLayout == null) { - // cancel the drag if we're not over a screen at time of drop - // TODO: maybe add a nice fade here? + // Cancel the drag if we're not over a screen at time of drop + if (mDragInfo != null) { + // Set its position so the parent can animate it back + final View parent = getChildAt(mDragInfo.screen); + setPositionForDropAnimation(dragView, originX, originY, parent, mDragInfo.cell); + } return; } - int originX = x - xOffset; - int originY = y - yOffset; + if (mIsSmall || mIsInUnshrinkAnimation) { // get originX and originY in the local coordinate system of the screen mTempOriginXY[0] = originX; @@ -1069,37 +1089,38 @@ public class Workspace extends SmoothPagedView if (source != this) { onDropExternal(originX, originY, dragInfo, mDragTargetLayout); - } else { + } else if (mDragInfo != null) { // Move internally - if (mDragInfo != null) { - final View cell = mDragInfo.cell; - - mTargetCell = findNearestVacantArea(originX, originY, - mDragInfo.spanX, mDragInfo.spanY, cell, mDragTargetLayout, - mTargetCell); - - int screen = indexOfChild(mDragTargetLayout); - if (screen != mDragInfo.screen) { - final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); - originalCellLayout.removeView(cell); - addInScreen(cell, screen, mTargetCell[0], mTargetCell[1], - mDragInfo.spanX, mDragInfo.spanY); - } - mDragTargetLayout.onDropChild(cell); - - // update the item's position after drop - final ItemInfo info = (ItemInfo) cell.getTag(); - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); - mDragTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); - lp.cellX = mTargetCell[0]; - lp.cellY = mTargetCell[1]; - cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen, - mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); - - LauncherModel.moveItemInDatabase(mLauncher, info, - LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, - lp.cellX, lp.cellY); + final View cell = mDragInfo.cell; + mTargetCell = findNearestVacantArea(originX, originY, + mDragInfo.spanX, mDragInfo.spanY, cell, mDragTargetLayout, + mTargetCell); + + int screen = indexOfChild(mDragTargetLayout); + if (screen != mDragInfo.screen) { + final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen); + originalCellLayout.removeView(cell); + addInScreen(cell, screen, mTargetCell[0], mTargetCell[1], + mDragInfo.spanX, mDragInfo.spanY); } + mDragTargetLayout.onDropChild(cell); + + // update the item's position after drop + final ItemInfo info = (ItemInfo) cell.getTag(); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams(); + mDragTargetLayout.onMove(cell, mTargetCell[0], mTargetCell[1]); + lp.cellX = mTargetCell[0]; + lp.cellY = mTargetCell[1]; + cell.setId(LauncherModel.getCellLayoutChildId(-1, mDragInfo.screen, + mTargetCell[0], mTargetCell[1], mDragInfo.spanX, mDragInfo.spanY)); + + LauncherModel.moveItemInDatabase(mLauncher, info, + LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, + lp.cellX, lp.cellY); + + // Prepare it to be animated into its new position + // This must be called after the view has been re-parented + setPositionForDropAnimation(dragView, originX, originY, mDragTargetLayout, cell); } } @@ -1562,16 +1583,10 @@ public class Workspace extends SmoothPagedView } // final Object tag = mDragInfo.cell.getTag(); } - } else { - if (mDragInfo != null) { - final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen); - cellLayout.onDropAborted(mDragInfo.cell); - } + } else if (mDragInfo != null) { + ((CellLayout) getChildAt(mDragInfo.screen)).onDropAborted(mDragInfo.cell); } - if (mDragInfo != null) { - mDragInfo.cell.setVisibility(View.VISIBLE); - } mDragOutline = null; mDragInfo = null; } -- cgit v1.2.3