From f533ad90223718f9b5f6d35adc180618620313a6 Mon Sep 17 00:00:00 2001 From: Martin Brabham Date: Mon, 13 Apr 2015 12:56:07 -0700 Subject: Clean up scrolling - Eliminate creation of garbage - Square up interface - Post animation signals to handler so they can be cancelled and redispatched with new data for the updated signal. Change-Id: I9824b7eb762a8d565e22e118bf3f07a8a4791ce8 (cherry picked from commit 22c34f768d1cf58f7b340350ef0a64b2c1ae9485) --- res/layout/app_drawer_container.xml | 2 +- res/layout/app_drawer_item.xml | 2 +- res/values/colors.xml | 1 + res/values/dimens.xml | 8 + .../android/launcher3/AppDrawerListAdapter.java | 77 +++++-- src/com/android/launcher3/AppDrawerScrubber.java | 249 +++++++++++++++++---- 6 files changed, 282 insertions(+), 57 deletions(-) diff --git a/res/layout/app_drawer_container.xml b/res/layout/app_drawer_container.xml index 30a4ceeb0..146906637 100644 --- a/res/layout/app_drawer_container.xml +++ b/res/layout/app_drawer_container.xml @@ -82,4 +82,4 @@ android:visibility="invisible" android:layout_height="100dp" /> - \ No newline at end of file + diff --git a/res/layout/app_drawer_item.xml b/res/layout/app_drawer_item.xml index 5d3b94655..46a965226 100644 --- a/res/layout/app_drawer_item.xml +++ b/res/layout/app_drawer_item.xml @@ -54,7 +54,7 @@ android:layout_marginTop="10dp" android:layout_marginLeft="6dp" android:layout_centerVertical="true" - android:includeFontPadding="false" + android:includeFontPadding="true" android:gravity="center" android:singleLine="true" autofit:minTextSize="8sp" diff --git a/res/values/colors.xml b/res/values/colors.xml index 082489d53..7f2f07ad7 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -56,4 +56,5 @@ @android:color/darker_gray #CC14191E + #b0000000 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 5394ea761..124bcb7db 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -126,4 +126,12 @@ 5dp 20dp + + + 6dp + 6dp + 32dp + 10dp + 8sp + 24sp diff --git a/src/com/android/launcher3/AppDrawerListAdapter.java b/src/com/android/launcher3/AppDrawerListAdapter.java index 21c1b28e9..25c6bb793 100644 --- a/src/com/android/launcher3/AppDrawerListAdapter.java +++ b/src/com/android/launcher3/AppDrawerListAdapter.java @@ -24,14 +24,16 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.provider.Settings; +import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.Log; +import android.view.Gravity; 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.FrameLayout; import android.widget.LinearLayout; import android.widget.SectionIndexer; import com.android.launcher3.locale.LocaleSetManager; @@ -51,6 +53,7 @@ import java.util.List; public class AppDrawerListAdapter extends RecyclerView.Adapter implements View.OnLongClickListener, DragSource, SectionIndexer { + private static final String TAG = AppDrawerListAdapter.class.getSimpleName(); private static final String NUMERIC_OR_SPECIAL_HEADER = "#"; /** @@ -110,14 +113,17 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter(); 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, @@ -288,19 +295,22 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter infos = getAllApps(); mLauncher.mAppDrawer.getLayoutManager().removeAllViews(); + setApps(infos); } @@ -662,6 +689,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter mHeaderList.size()) ? mHeaderList.size() : position; + + int index = 0; + AppItemIndexedInfo info = mHeaderList.get(position); + if (info != null) { + SectionIndices indices = mSectionHeaders.get(info.mStartString); + if (indices != null) { + index = indices.mSectionIndex; + } else { + Log.w(TAG, "SectionIndices are null"); + } + } else { + Log.w(TAG, "AppItemIndexedInfo is null"); + } + return index; } private void filterProtectedApps(ArrayList list) { diff --git a/src/com/android/launcher3/AppDrawerScrubber.java b/src/com/android/launcher3/AppDrawerScrubber.java index b69530d47..de63955b4 100644 --- a/src/com/android/launcher3/AppDrawerScrubber.java +++ b/src/com/android/launcher3/AppDrawerScrubber.java @@ -23,6 +23,8 @@ import android.graphics.Color; import android.graphics.PointF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearSmoothScroller; import android.support.v7.widget.RecyclerView; @@ -34,8 +36,18 @@ import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; +import java.lang.IllegalArgumentException; import java.util.ArrayList; +/** + * AppDrawerScrubber + *
+ *     This is the scrubber at the bottom of the app drawer layout for navigating the application
+ *     list
+ * 
+ * + * @see {@link android.widget.LinearLayout} + */ public class AppDrawerScrubber extends LinearLayout { private AppDrawerListAdapter mAdapter; private RecyclerView mListView; @@ -45,17 +57,197 @@ public class AppDrawerScrubber extends LinearLayout { private SectionContainer mSectionContainer; private LinearLayoutManager mLayoutManager; private ScrubberAnimationState mScrubberAnimationState; + private Drawable mTransparentDrawable; + private AppDrawerSmoothScroller mLinearSmoothScroller; + + private static final int MSG_SET_TARGET = 1000; + private static final int MSG_SMOOTH_SCROLL = MSG_SET_TARGET + 1; + private static final int MSG_ANIMATE_PICK = MSG_SMOOTH_SCROLL + 1; + + /** + * UiHandler + *
+     *     Using a handler for sending signals to perform certain actions.  The reason for
+     *     using this is to be able to remove and replace a signal if signals are being
+     *     sent too fast (e.g. user scrubbing like crazy). This allows the touch loop to
+     *     complete then later run the animations in their own loops.
+     * 
+ */ + private class UiHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SET_TARGET: + int adapterIndex = msg.arg1; + performSetTarget(adapterIndex); + break; + case MSG_ANIMATE_PICK: + int index = msg.arg1; + int width = msg.arg2; + int lastIndex = (Integer)msg.obj; + performAnimatePickMessage(index, width, lastIndex); + break; + case MSG_SMOOTH_SCROLL: + int itemDiff = msg.arg1; + int itemIndex = msg.arg2; + performSmoothScroll(itemDiff, itemIndex); + break; + default: + super.handleMessage(msg); + } + } + + /** + * Overidden to remove identical calls if they are called subsequently fast enough. + * + * This is the final point that is public in the call chain. Other calls to sendMessageXXX + * will eventually call this function which calls "enqueueMessage" which is private. + * + * @param msg {@link android.os.Message} + * @param uptimeMillis {@link java.lang.Long} + * + * @throws IllegalArgumentException {@link java.lang.IllegalArgumentException} + */ + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) throws + IllegalArgumentException { + if (msg == null) { + throw new IllegalArgumentException("'msg' cannot be null!"); + } + if (hasMessages(msg.what)) { + removeMessages(msg.what); + } + return super.sendMessageAtTime(msg, uptimeMillis); + } + + } + private Handler mUiHandler = new UiHandler(); + private void sendSetTargetMessage(int adapterIndex) { + Message msg = mUiHandler.obtainMessage(MSG_SET_TARGET); + msg.what = MSG_SET_TARGET; + msg.arg1 = adapterIndex; + mUiHandler.sendMessage(msg); + } + private void performSetTarget(int adapterIndex) { + if (mAdapter != null) { + mAdapter.setSectionTarget(adapterIndex); + } + } + private void sendAnimatePickMessage(int index, int width, int lastIndex) { + Message msg = mUiHandler.obtainMessage(MSG_ANIMATE_PICK); + msg.what = MSG_ANIMATE_PICK; + msg.arg1 = index; + msg.arg2 = width; + msg.obj = lastIndex; + mUiHandler.sendMessage(msg); + } + private void performAnimatePickMessage(int index, int width, int lastIndex) { + if (mScrubberIndicator != null) { + // get the index based on the direction the user is scrolling + int directionalIndex = mSectionContainer.getDirectionalIndex(lastIndex, index); + String sectionText = mSectionContainer.getHeader(directionalIndex); + float translateX = (index * width) / (float) mSectionContainer.size(); + // if we are showing letters, grab the position based on the text view + if (mSectionContainer.showLetters()) { + translateX = mScrubberText.getPositionOfSection(index); + } + // center the x position + translateX -= mScrubberIndicator.getMeasuredWidth() / 2; + mScrubberIndicator.setTranslationX(translateX); + mScrubberIndicator.setText(sectionText); + } + } + private void sendSmoothScrollMessage(int itemDiff, int itemIndex) { + Message msg = mUiHandler.obtainMessage(MSG_SMOOTH_SCROLL); + msg.what = MSG_SMOOTH_SCROLL; + msg.arg1 = itemDiff; + msg.arg2 = itemIndex; + mUiHandler.sendMessage(msg); + } + private void performSmoothScroll(int itemDiff, int itemIndex) { + if (mLinearSmoothScroller == null) { + mLinearSmoothScroller = new AppDrawerSmoothScroller(mContext); + } + mLinearSmoothScroller.setItemDiff(itemDiff); + mLinearSmoothScroller.setTargetPosition(itemIndex); + mLayoutManager.startSmoothScroll(mLinearSmoothScroller); + } + /** + * Constructor + * + * @param context {@link android.content.Context} + * @param attrs {@link android.util.AttributeSet} + */ public AppDrawerScrubber(Context context, AttributeSet attrs) { super(context, attrs); init(context); } + /** + * Constructor + * + * @param context {@link android.content.Context} + */ public AppDrawerScrubber(Context context) { super(context); init(context); } + /** + * AppDrawerSmoothScroller + *
+     *     This is a smooth scroller with the ability to set an item diff
+     * 
+ * + * @see {@link android.support.v7.widget.LinearSmoothScroller} + */ + private class AppDrawerSmoothScroller extends LinearSmoothScroller { + + // Members + private int mItemDiff = 0; + + public AppDrawerSmoothScroller(Context context) { + super(context); + } + + @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 - mItemDiff; + } + + /** + * Set the item difference + * + * @param itemDiff + */ + public void setItemDiff(int itemDiff) { + mItemDiff = itemDiff; + } + + /** + * Get the item difference + * + * @return {@link java.lang.Integer} + */ + public int getItemDiff() { + return mItemDiff; + } + + } + /** * Simple container class that tries to abstract out the knowledge of complex sections vs * simple string sections @@ -131,7 +323,7 @@ public class AppDrawerScrubber extends LinearLayout { mSeekBar.setMax(mSectionContainer.size() - 1); // show a white line if there are no letters, otherwise show transparent - Drawable d = mSectionContainer.showLetters() ? new ColorDrawable(Color.TRANSPARENT) + Drawable d = mSectionContainer.showLetters() ? mTransparentDrawable : getContext().getResources().getDrawable(R.drawable.seek_back); ((ViewGroup)mSeekBar.getParent()).setBackground(d); @@ -155,6 +347,7 @@ public class AppDrawerScrubber extends LinearLayout { private void init(Context context) { LayoutInflater.from(context).inflate(R.layout.scrub_layout, this); + mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT); mScrubberAnimationState = new ScrubberAnimationState(); mSeekBar = (SeekBar) findViewById(R.id.scrubber); mScrubberText = (AutoExpandTextView) findViewById(R.id.scrubberText); @@ -193,6 +386,9 @@ public class AppDrawerScrubber extends LinearLayout { } private void animateIn() { + if (mScrubberIndicator == null) { + return; + } // start from a scratch position when animating in mScrubberIndicator.animate().cancel(); mScrubberIndicator.setPivotX(mScrubberIndicator.getMeasuredWidth() / 2); @@ -218,11 +414,13 @@ public class AppDrawerScrubber extends LinearLayout { animateOut(); } } - }) - .start(); + }).start(); } private void animateOut() { + if (mScrubberIndicator == null) { + return; + } mScrubberIndicator.animate() .alpha(SCRUBBER_ALPHA_START) .scaleX(SCRUBBER_SCALE_START) @@ -242,24 +440,12 @@ public class AppDrawerScrubber extends LinearLayout { if (!isReady()) { return; } + progressChanged(seekBar, index, fromUser); + } - if (mScrubberIndicator != null) { - // get the index based on the direction the user is scrolling - int directionalIndex = mSectionContainer.getDirectionalIndex(mLastIndex, index); - String sectionText = mSectionContainer.getHeader(directionalIndex); - - float translateX = (index * seekBar.getWidth()) / (float)mSectionContainer.size(); - // if we are showing letters, grab the position based on the text view - if (mSectionContainer.showLetters()) { - translateX = mScrubberText.getPositionOfSection(index); - } - - // center the x position - translateX -= mScrubberIndicator.getMeasuredWidth() / 2; + private void progressChanged(SeekBar seekBar, int index, boolean fromUser) { - mScrubberIndicator.setTranslationX(translateX); - mScrubberIndicator.setText(sectionText); - } + sendAnimatePickMessage(index, seekBar.getWidth(), mLastIndex); // get the index of the underlying list int adapterIndex = mSectionContainer.getAdapterIndex(mLastIndex, index); @@ -272,32 +458,15 @@ public class AppDrawerScrubber extends LinearLayout { itemHeight = child.getMeasuredHeight(); } + // Start smooth scroll from this Looper loop 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); + sendSmoothScrollMessage(itemDiff, itemIndex); } - mAdapter.setSectionTarget(adapterIndex); + // Post set target index on queue to get processed by Looper later + sendSetTargetMessage(adapterIndex); mLastIndex = index; } -- cgit v1.2.3