diff options
-rw-r--r-- | res/layout/app_drawer_item.xml | 11 | ||||
-rw-r--r-- | src/com/android/launcher3/AppDrawerListAdapter.java | 122 | ||||
-rw-r--r-- | src/com/android/launcher3/AppDrawerScrubber.java | 39 |
3 files changed, 143 insertions, 29 deletions
diff --git a/res/layout/app_drawer_item.xml b/res/layout/app_drawer_item.xml index cc2ed8f9b..5d3b94655 100644 --- a/res/layout/app_drawer_item.xml +++ b/res/layout/app_drawer_item.xml @@ -19,6 +19,15 @@ android:splitMotionEvents="false" android:layout_width="match_parent" android:layout_height="wrap_content"> + <View + android:id="@+id/fading_background_back" + android:alpha="0" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layout_alignTop="@+id/drawer_item_flow" + android:layout_alignBottom="@+id/drawer_item_flow" + android:background="@color/app_drawer_drag_background" /> + <!-- Layout in back to front render order --> <LinearLayout android:id="@+id/drawer_item_flow" @@ -30,7 +39,7 @@ android:orientation="horizontal" /> <View - android:id="@+id/fading_background" + android:id="@+id/fading_background_front" android:alpha="0" android:layout_width="fill_parent" android:layout_height="fill_parent" diff --git a/src/com/android/launcher3/AppDrawerListAdapter.java b/src/com/android/launcher3/AppDrawerListAdapter.java index 5a5e6cb9f..5bbc2ad03 100644 --- a/src/com/android/launcher3/AppDrawerListAdapter.java +++ b/src/com/android/launcher3/AppDrawerListAdapter.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.ComponentName; import android.content.Context; @@ -51,12 +53,27 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap private static final String NUMERIC_OR_SPECIAL_HEADER = "#"; + /** + * Tracks both the section index and the positional item index for the sections + * section: 0 0 0 1 1 2 3 4 4 + * itemIndex: 0 1 2 3 4 5 6 7 8 + * Sections: A A A B B C D E E + */ + private static class SectionIndices { + public int mSectionIndex; + public int mItemIndex; + public SectionIndices(int sectionIndex, int itemIndex) { + mSectionIndex = sectionIndex; + mItemIndex = itemIndex; + } + } + private ArrayList<AppItemIndexedInfo> mHeaderList; private LayoutInflater mLayoutInflater; private Launcher mLauncher; private DeviceProfile mDeviceProfile; - private LinkedHashMap<String, Integer> mSectionHeaders; + private LinkedHashMap<String, SectionIndices> mSectionHeaders; private LinearLayout.LayoutParams mIconParams; private Rect mIconRect; private LocaleSetManager mLocaleSetManager; @@ -93,12 +110,14 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap public static class ViewHolder extends RecyclerView.ViewHolder { public AutoFitTextView mTextView; public ViewGroup mLayout; - public View mFadingBackground; + public View mFadingBackgroundFront; + public View mFadingBackgroundBack; 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); + mFadingBackgroundFront = itemView.findViewById(R.id.fading_background_front); + mFadingBackgroundBack = itemView.findViewById(R.id.fading_background_back); } } @@ -111,6 +130,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap private static final float MAX_SCALE = 2f; private static final float MIN_SCALE = 1f; private static final float FAST_SCROLL = 0.3f; + private static final int NO_SECTION_TARGET = -1; private final float YDPI; private final HashSet<ViewHolder> mViewHolderSet; @@ -125,11 +145,16 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap private float mFastScrollSpeed; private float mLastScrollSpeed; + // If the user is scrubbing, we want to highlight the target section differently, + // so we use this to track where the user is currently scrubbing to + private int mSectionTarget; + public ItemAnimatorSet(Context ctx) { mDragging = false; mExpanding = false; mPendingShrink = false; mScrollState = RecyclerView.SCROLL_STATE_IDLE; + mSectionTarget = NO_SECTION_TARGET; mViewHolderSet = new HashSet<>(); mInterpolator = new DecelerateInterpolator(); YDPI = ctx.getResources().getDisplayMetrics().ydpi; @@ -161,6 +186,12 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap mScrollState = newState; mFastScrollSpeed = 0; checkAnimationState(); + + // If the user is dragging, clear the section target + if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING + && mSectionTarget != NO_SECTION_TARGET) { + setSectionTarget(NO_SECTION_TARGET); + } } } @@ -211,18 +242,46 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap } } - public void createAnimationHook(ViewHolder holder) { + public void createAnimationHook(final ViewHolder holder) { holder.mTextView.animate().cancel(); holder.mTextView.animate() .setUpdateListener(new ItemAnimator(holder, mItemAnimatorSet)) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(final Animator animation) { + animateEnd(holder, animation); + } + }) .setDuration(ANIMATION_DURATION) .start(); } - public void animate(ViewHolder holder, ValueAnimator animation) { + public void animateEnd(ViewHolder holder, Animator animation) { + animate(holder, animation, 1f); + } + + public void animate(ViewHolder holder, Animator animation) { long diffTime = System.currentTimeMillis() - mStartTime; float percentage = Math.min(diffTime / (float) ANIMATION_DURATION, 1f); + + animate(holder, animation, percentage); + + if (diffTime >= ANIMATION_DURATION) { + if (animation != null) { + animation.cancel(); + } + + if (mPendingShrink) { + mPendingShrink = false; + mLastScrollSpeed = 0; + checkAnimationState(); + } + + } + } + + public void animate(ViewHolder holder, Animator animation, float percentage) { percentage = mInterpolator.getInterpolation(percentage); if (!mExpanding) { @@ -233,17 +292,24 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap 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(); - } + if (getSectionForPosition(holder.getPosition()) == mSectionTarget) { + holder.mFadingBackgroundFront.setVisibility(View.INVISIBLE); + holder.mFadingBackgroundBack.setAlpha(percentage); + holder.mFadingBackgroundBack.setVisibility(View.VISIBLE); + } else { + holder.mFadingBackgroundFront.setAlpha(percentage); + holder.mFadingBackgroundFront.setVisibility(View.VISIBLE); + holder.mFadingBackgroundBack.setVisibility(View.INVISIBLE); + } + } + /** + * Sets the section index to highlight different from the rest when scrubbing + */ + public void setSectionTarget(int sectionIndex) { + mSectionTarget = sectionIndex; + for (ViewHolder holder : mViewHolderSet) { + animate(holder, null); } } } @@ -289,6 +355,13 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap mItemAnimatorSet.setDragging(dragging); } + /** + * Sets the section index to highlight different from the rest when scrubbing + */ + public void setSectionTarget(int sectionIndex) { + mItemAnimatorSet.setSectionTarget(sectionIndex); + } + private void initParams() { mDeviceProfile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); @@ -380,18 +453,15 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap private void populateSectionHeaders() { if (mSectionHeaders == null || mSectionHeaders.size() != mHeaderList.size()) { - mSectionHeaders = new LinkedHashMap<String, Integer>(); + mSectionHeaders = new LinkedHashMap<>(); } - int count = 0; + + int sectionIndex = 0; for (int i = 0; i < mHeaderList.size(); i++) { - AppItemIndexedInfo info = mHeaderList.get(i); if (!mHeaderList.get(i).isChild) { - mSectionHeaders.put(String.valueOf(mHeaderList.get(i).mStartString), count); - } - if (info.mInfo.size() < mDeviceProfile.numColumnsBase) { - count++; - } else { - count += info.mInfo.size() / mDeviceProfile.numColumnsBase; + mSectionHeaders.put(String.valueOf(mHeaderList.get(i).mStartString), + new SectionIndices(sectionIndex, i)); + sectionIndex++; } } } @@ -741,12 +811,12 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap @Override public int getPositionForSection(int sectionIndex) { - return mSectionHeaders.get(getSections()[sectionIndex]); + return mSectionHeaders.get(getSections()[sectionIndex]).mItemIndex; } @Override public int getSectionForPosition(int position) { - return mSectionHeaders.get(mHeaderList.get(position).mStartString); + return mSectionHeaders.get(mHeaderList.get(position).mStartString).mSectionIndex; } private void filterProtectedApps(ArrayList<AppInfo> list) { diff --git a/src/com/android/launcher3/AppDrawerScrubber.java b/src/com/android/launcher3/AppDrawerScrubber.java index 706ddfe96..c65fd373d 100644 --- a/src/com/android/launcher3/AppDrawerScrubber.java +++ b/src/com/android/launcher3/AppDrawerScrubber.java @@ -20,9 +20,11 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.graphics.Color; +import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.LinearSmoothScroller; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; @@ -261,8 +263,41 @@ public class AppDrawerScrubber extends LinearLayout { // get the index of the underlying list int adapterIndex = mSectionContainer.getAdapterIndex(mLastIndex, index); - mLayoutManager.smoothScrollToPosition(mListView, null, - mAdapter.getPositionForSection(adapterIndex)); + int itemIndex = mAdapter.getPositionForSection(adapterIndex); + + // get any child's height since all children are the same height + int itemHeight = 0; + View child = mLayoutManager.getChildAt(0); + if (child != null) { + itemHeight = child.getMeasuredHeight(); + } + + if (itemHeight != 0) { + // scroll to the item such that there are 2 rows beneath it from the bottom + final int itemDiff = 2 * itemHeight; + LinearSmoothScroller scroller = new LinearSmoothScroller(mListView.getContext()) { + @Override + protected int getVerticalSnapPreference() { + // position the item against the end of the list view + return SNAP_TO_END; + } + + @Override + public PointF computeScrollVectorForPosition(int targetPosition) { + return mLayoutManager.computeScrollVectorForPosition(targetPosition); + } + + @Override + public int calculateDyToMakeVisible(View view, int snapPreference) { + int dy = super.calculateDyToMakeVisible(view, snapPreference); + return dy - itemDiff; + } + }; + scroller.setTargetPosition(itemIndex); + mLayoutManager.startSmoothScroll(scroller); + } + + mAdapter.setSectionTarget(adapterIndex); mLastIndex = index; } |