diff options
Diffstat (limited to 'src/com/android/launcher3/AppDrawerListAdapter.java')
-rw-r--r-- | src/com/android/launcher3/AppDrawerListAdapter.java | 215 |
1 files changed, 213 insertions, 2 deletions
diff --git a/src/com/android/launcher3/AppDrawerListAdapter.java b/src/com/android/launcher3/AppDrawerListAdapter.java index a16937308..ea3243534 100644 --- a/src/com/android/launcher3/AppDrawerListAdapter.java +++ b/src/com/android/launcher3/AppDrawerListAdapter.java @@ -16,25 +16,29 @@ package com.android.launcher3; +import android.animation.ValueAnimator; import android.content.ComponentName; import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.provider.Settings; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.support.v7.widget.RecyclerView; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; import android.widget.LinearLayout; import android.widget.SectionIndexer; import com.android.launcher3.locale.LocaleSetManager; import com.android.launcher3.locale.LocaleUtils; import com.android.launcher3.settings.SettingsProvider; -import java.text.Collator; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -61,6 +65,8 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap private boolean mHideIconLabels; + private ItemAnimatorSet mItemAnimatorSet; + public enum DrawerType { Drawer(0), Pager(1); @@ -87,10 +93,173 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap public static class ViewHolder extends RecyclerView.ViewHolder { public AutoFitTextView mTextView; public ViewGroup mLayout; + public View mFadingBackground; public ViewHolder(View itemView) { super(itemView); mTextView = (AutoFitTextView) itemView.findViewById(R.id.drawer_item_title); mLayout = (ViewGroup) itemView.findViewById(R.id.drawer_item_flow); + mFadingBackground = itemView.findViewById(R.id.fading_background); + } + } + + /** + * This class handles animating the different items when the user scrolls through the drawer + * quickly + */ + private class ItemAnimatorSet { + private static final long ANIMATION_DURATION = 200; + private static final float MAX_SCALE = 2f; + private static final float MIN_SCALE = 1f; + private static final float FAST_SCROLL = 0.3f; + + private final float YDPI; + private final HashSet<ViewHolder> mViewHolderSet; + private final Interpolator mInterpolator; + private final View.OnLayoutChangeListener mLayoutChangeListener; + + private boolean mDragging; + private boolean mExpanding; + private boolean mPendingShrink; + private long mStartTime; + private int mScrollState; + private float mFastScrollSpeed; + private float mLastScrollSpeed; + + public ItemAnimatorSet(Context ctx) { + mDragging = false; + mExpanding = false; + mPendingShrink = false; + mScrollState = RecyclerView.SCROLL_STATE_IDLE; + mViewHolderSet = new HashSet<>(); + mInterpolator = new DecelerateInterpolator(); + YDPI = ctx.getResources().getDisplayMetrics().ydpi; + mLayoutChangeListener = new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + // set the pivot of the text view + v.setPivotX(0); + v.setPivotY(v.getMeasuredHeight() / 2); + } + }; + } + + public void add(ViewHolder holder) { + mViewHolderSet.add(holder); + holder.mTextView.addOnLayoutChangeListener(mLayoutChangeListener); + + createAnimationHook(holder); + } + + public void remove(ViewHolder holder) { + mViewHolderSet.remove(holder); + holder.mTextView.removeOnLayoutChangeListener(mLayoutChangeListener); + } + + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (newState != mScrollState) { + mScrollState = newState; + mFastScrollSpeed = 0; + checkAnimationState(); + } + } + + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (mScrollState == RecyclerView.SCROLL_STATE_SETTLING) { + mLastScrollSpeed = Math.abs(dy / YDPI); + // get the max of the current scroll speed and the previous fastest scroll speed + mFastScrollSpeed = Math.max(mFastScrollSpeed, mLastScrollSpeed); + checkAnimationState(); + } + } + + public void setDragging(boolean dragging) { + mDragging = dragging; + checkAnimationState(); + } + + private void checkAnimationState() { + // if the user is dragging or if we're settling at a fast speed, then show animation + showAnimation(mDragging || + (mScrollState == RecyclerView.SCROLL_STATE_SETTLING && + mFastScrollSpeed >= FAST_SCROLL)); + } + + private void showAnimation(boolean expanding) { + if (mExpanding != expanding) { + // if near the top or bottom and flick to that side of the list, the scroll speed + // will hit 0 and the animation will cut straight to shrinking. This code + // is here to allow the expand animation to complete in that specific scenario + // before shrinking + // if the user isn't dragging, the scroll state is idle, the last scroll is fast and + // the expand animation is still playing, then mark pending shrink as true + if (!mDragging + && mScrollState == RecyclerView.SCROLL_STATE_IDLE + && mLastScrollSpeed > FAST_SCROLL + && System.currentTimeMillis() - mStartTime < ANIMATION_DURATION) { + mPendingShrink = true; + return; + } + + mExpanding = expanding; + mPendingShrink = false; + mStartTime = System.currentTimeMillis(); + + for (ViewHolder holder : mViewHolderSet) { + createAnimationHook(holder); + } + } + } + + public void createAnimationHook(ViewHolder holder) { + holder.mTextView.animate().cancel(); + holder.mTextView.animate() + .setUpdateListener(new ItemAnimator(holder, mItemAnimatorSet)) + .setDuration(ANIMATION_DURATION) + .start(); + } + + public void animate(ViewHolder holder, ValueAnimator animation) { + long diffTime = System.currentTimeMillis() - mStartTime; + + float percentage = Math.min(diffTime / (float) ANIMATION_DURATION, 1f); + percentage = mInterpolator.getInterpolation(percentage); + + if (!mExpanding) { + percentage = 1 - percentage; + } + + final float targetScale = (MAX_SCALE - MIN_SCALE) * percentage + MIN_SCALE; + holder.mTextView.setScaleX(targetScale); + holder.mTextView.setScaleY(targetScale); + + holder.mFadingBackground.setAlpha(percentage); + + if (diffTime >= ANIMATION_DURATION) { + animation.cancel(); + + if (mPendingShrink) { + mPendingShrink = false; + mLastScrollSpeed = 0; + checkAnimationState(); + } + + } + } + } + + private static class ItemAnimator implements ValueAnimator.AnimatorUpdateListener { + private ViewHolder mViewHolder; + private ItemAnimatorSet mAnimatorSet; + + public ItemAnimator(final ViewHolder holder, final ItemAnimatorSet animatorSet) { + mViewHolder = holder; + mAnimatorSet = animatorSet; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mAnimatorSet.animate(mViewHolder, animation); } } @@ -101,11 +270,25 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap mLocaleSetManager = new LocaleSetManager(mLauncher); mLocaleSetManager.updateLocaleSet(mLocaleSetManager.getSystemLocaleSet()); + mItemAnimatorSet = new ItemAnimatorSet(launcher); initParams(); updateProtectedAppsList(mLauncher); } + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + mItemAnimatorSet.onScrollStateChanged(recyclerView, newState); + } + + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + mItemAnimatorSet.onScrolled(recyclerView, dx, dy); + } + + + public void setDragging(boolean dragging) { + mItemAnimatorSet.setDragging(dragging); + } + private void initParams() { mDeviceProfile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); @@ -347,7 +530,17 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap View v = LayoutInflater.from(parent.getContext()). inflate(R.layout.app_drawer_item, parent, false); ViewHolder holder = new ViewHolder(v); - holder.mTextView.setPadding(0, 0, 0, mDeviceProfile.iconTextSizePx + 10); + ViewGroup.LayoutParams params = holder.mTextView.getLayoutParams(); + + // set the margin parameter to account for the text size of the icons so that the text view + // is based on the icon size only + if (params instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) params; + marginParams.setMargins(marginParams.leftMargin, marginParams.topMargin, + marginParams.rightMargin, mDeviceProfile.iconTextSizePx); + holder.mTextView.setLayoutParams(marginParams); + } + for (int i = 0; i < mDeviceProfile.numColumnsBase; i++) { AppDrawerIconView icon = (AppDrawerIconView) mLayoutInflater.inflate( R.layout.drawer_icon, holder.mLayout, false); @@ -359,6 +552,20 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap } @Override + public void onViewAttachedToWindow(ViewHolder holder) { + super.onViewAttachedToWindow(holder); + + mItemAnimatorSet.add(holder); + } + + @Override + public void onViewDetachedFromWindow(ViewHolder holder) { + super.onViewDetachedFromWindow(holder); + + mItemAnimatorSet.remove(holder); + } + + @Override public int getItemCount() { return mHeaderList.size(); } @@ -380,6 +587,10 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap holder.mTextView.setText(String.valueOf(indexedInfo.mStartString)); } } + + holder.mTextView.setPivotX(0); + holder.mTextView.setPivotY(holder.mTextView.getHeight() / 2); + final int size = indexedInfo.mInfo.size(); for (int i = 0; i < holder.mLayout.getChildCount(); i++) { AppDrawerIconView icon = (AppDrawerIconView) holder.mLayout.getChildAt(i); |