diff options
author | Winson <winsonc@google.com> | 2016-07-11 18:59:18 -0700 |
---|---|---|
committer | Peter Schiller <peterschiller@google.com> | 2016-07-12 12:16:26 -0700 |
commit | b655b8850064cc4f8e59652b4e3bffc09090769a (patch) | |
tree | 35903c193119dbea9f704ad0449f45398ffd1d3e /src/com/android | |
parent | 7ed42af3398c1764424ebbf2dd6e2a51e05eaf5f (diff) | |
download | android_packages_apps_Trebuchet-b655b8850064cc4f8e59652b4e3bffc09090769a.tar.gz android_packages_apps_Trebuchet-b655b8850064cc4f8e59652b4e3bffc09090769a.tar.bz2 android_packages_apps_Trebuchet-b655b8850064cc4f8e59652b4e3bffc09090769a.zip |
Cleaning up scrollbar logic to properly calculate stable extents.
- Removing old logic which assumed that views were the same size,
especially now we can have variable dividers, etc.
- Simplifying old scroll position logic.
- Removing unnecessary prediction icon layout (same as normal icon)
Bug: 30023608
Change-Id: I39e1126fa586a76a9bdd3ff38cd6e360ac3021e6
Diffstat (limited to 'src/com/android')
6 files changed, 151 insertions, 155 deletions
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 4cb050e24..8bd5eba00 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -41,21 +41,6 @@ public abstract class BaseRecyclerView extends RecyclerView @Thunk int mDy = 0; private float mDeltaThreshold; - /** - * The current scroll state of the recycler view. We use this in onUpdateScrollbar() - * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so - * that we can calculate what the scroll bar looks like, and where to jump to from the fast - * scroller. - */ - public static class ScrollPositionState { - // The index of the first visible row - public int rowIndex; - // The offset of the first visible row - public int rowTopOffset; - // The adapter position of the first visible item - public int itemPos; - } - protected BaseRecyclerViewFastScrollBar mScrollbar; private int mDownX; @@ -199,11 +184,7 @@ public abstract class BaseRecyclerView extends RecyclerView * Returns the available scroll height: * AvailableScrollHeight = Total height of the all items - last page height */ - protected int getAvailableScrollHeight(int rowCount) { - int totalHeight = getPaddingTop() + getTop(rowCount) + getPaddingBottom(); - int availableScrollHeight = totalHeight - getVisibleHeight(); - return availableScrollHeight; - } + protected abstract int getAvailableScrollHeight(); /** * Returns the available scroll bar height: @@ -247,15 +228,12 @@ public abstract class BaseRecyclerView extends RecyclerView * this by mapping the available scroll area of the recycler view to the available space for the * scroll bar. * - * @param scrollPosState the current scroll position - * @param rowCount the number of rows, used to calculate the total scroll height (assumes that - * all rows are the same height) + * @param scrollY the current scroll y */ - protected void synchronizeScrollBarThumbOffsetToViewScroll(ScrollPositionState scrollPosState, - int rowCount) { + protected void synchronizeScrollBarThumbOffsetToViewScroll(int scrollY, + int availableScrollHeight) { // Only show the scrollbar if there is height to be scrolled int availableScrollBarHeight = getAvailableScrollBarHeight(); - int availableScrollHeight = getAvailableScrollHeight(rowCount); if (availableScrollHeight <= 0) { mScrollbar.setThumbOffset(-1, -1); return; @@ -264,7 +242,6 @@ public abstract class BaseRecyclerView extends RecyclerView // Calculate the current scroll position, the scrollY of the recycler view accounts for the // view padding, while the scrollBarY is drawn right up to the background padding (ignoring // padding) - int scrollY = Math.max(0, getScrollTop(scrollPosState)); int scrollBarY = mBackgroundPadding.top + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); @@ -291,20 +268,7 @@ public abstract class BaseRecyclerView extends RecyclerView * * @return the scroll top of this recycler view. */ - protected int getScrollTop(ScrollPositionState scrollPosState) { - return getPaddingTop() + getTop(scrollPosState.rowIndex) - - scrollPosState.rowTopOffset; - } - - /** - * Returns information about the item that the recycler view is currently scrolled to. - */ - protected abstract void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask); - - /** - * Returns the top (or y position) of the row at the specified index. - */ - protected abstract int getTop(int rowIndex); + protected abstract int getCurrentScrollY(); /** * Maps the touch (from 0..1) to the adapter position that should be visible. diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 717ce74c7..a9cc8f379 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -364,26 +364,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView); mAppsRecyclerView.addItemDecoration(focusedItemDecorator); + mAppsRecyclerView.preMeasureViews(mAdapter); mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener()); - // Precalculate the prediction icon and normal icon sizes - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - final int widthMeasureSpec = MeasureSpec.makeMeasureSpec( - getResources().getDisplayMetrics().widthPixels, MeasureSpec.AT_MOST); - final int heightMeasureSpec = MeasureSpec.makeMeasureSpec( - getResources().getDisplayMetrics().heightPixels, MeasureSpec.AT_MOST); - - BubbleTextView icon = (BubbleTextView) layoutInflater.inflate( - R.layout.all_apps_icon, this, false); - icon.applyDummyInfo(); - icon.measure(widthMeasureSpec, heightMeasureSpec); - BubbleTextView predIcon = (BubbleTextView) layoutInflater.inflate( - R.layout.all_apps_prediction_bar_icon, this, false); - predIcon.applyDummyInfo(); - predIcon.measure(widthMeasureSpec, heightMeasureSpec); - mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(), - icon.getMeasuredHeight()); - // TODO(hyunyoungs): clean up setting the content and the reveal view. if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { getContentView().setBackground(null); diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java index 73de45e39..6d9094f78 100644 --- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java +++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java @@ -18,7 +18,6 @@ package com.android.launcher3.allapps; import android.support.v7.widget.RecyclerView; import android.view.View; -import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.BaseRecyclerViewFastScrollBar; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.util.Thunk; @@ -144,8 +143,8 @@ public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallb // Calculate the full animation from the current scroll position to the final scroll // position, and then run the animation for the duration. - int newScrollY = Math.min(availableScrollHeight, - mRv.getPaddingTop() + mRv.getTop(info.fastScrollToItem.rowIndex)); + int newPosition = info.fastScrollToItem.position; + int newScrollY = Math.min(availableScrollHeight, mRv.getCurrentScrollY(newPosition, 0)); int numFrames = mFastScrollFrames.length; for (int i = 0; i < numFrames; i++) { // TODO(winsonc): We can interpolate this as well. diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 7d856c04f..268019772 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -334,7 +334,6 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. private final int mSectionNamesMargin; private final int mSectionHeaderOffset; private final Paint mSectionTextPaint; - private int mAccentColor; private int mAppsPerRow; @@ -364,12 +363,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.all_apps_grid_section_y_offset); mIsRtl = Utilities.isRtl(res); - mAccentColor = Utilities.getColorAccent(launcher); - mSectionTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mSectionTextPaint.setTextSize(res.getDimensionPixelSize( R.dimen.all_apps_grid_section_text_size)); - mSectionTextPaint.setColor(mAccentColor); + mSectionTextPaint.setColor(Utilities.getColorAccent(launcher)); } public static boolean isDividerViewType(int viewType) { @@ -380,6 +377,10 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. return isViewType(viewType, VIEW_TYPE_MASK_ICON); } + public static boolean isPredictionIconViewType(int viewType) { + return isViewType(viewType, VIEW_TYPE_PREDICTION_ICON); + } + public static boolean isViewType(int viewType, int viewTypeMask) { return (viewType & viewTypeMask) != 0; } @@ -449,8 +450,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. /* falls through */ case VIEW_TYPE_PREDICTION_ICON: { BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate( - viewType == VIEW_TYPE_ICON ? R.layout.all_apps_icon : - R.layout.all_apps_prediction_bar_icon, parent, false); + R.layout.all_apps_icon, parent, false); icon.setOnClickListener(mIconClickListener); icon.setOnLongClickListener(mIconLongClickListener); icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext()) @@ -472,14 +472,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter. }); return new ViewHolder(searchMarketView); case VIEW_TYPE_SEARCH_DIVIDER: - final View searchDivider = - mLayoutInflater.inflate(R.layout.all_apps_divider, parent, false); - searchDivider.setBackgroundColor(mAccentColor); - final GridLayoutManager.LayoutParams searchDividerParams = - (GridLayoutManager.LayoutParams) searchDivider.getLayoutParams(); - searchDividerParams.topMargin = 0; - searchDivider.setLayoutParams(searchDividerParams); - return new ViewHolder(searchDivider); + return new ViewHolder(mLayoutInflater.inflate( + R.layout.all_apps_search_divider, parent, false)); case VIEW_TYPE_PREDICTION_DIVIDER: /* falls through */ case VIEW_TYPE_SEARCH_MARKET_DIVIDER: diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index ac881138d..3a4485320 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -21,6 +21,7 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; +import android.util.SparseIntArray; import android.view.View; import com.android.launcher3.BaseRecyclerView; @@ -42,13 +43,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView private AlphabeticalAppsList mApps; private AllAppsFastScrollHelper mFastScrollHelper; - private BaseRecyclerView.ScrollPositionState mScrollPosState = - new BaseRecyclerView.ScrollPositionState(); private int mNumAppsPerRow; - // The specific icon heights that we use to calculate scroll - private int mPredictionIconHeight; - private int mIconHeight; + // The specific view heights that we use to calculate scroll + private SparseIntArray mViewHeights = new SparseIntArray(); + private SparseIntArray mCachedScrollPositions = new SparseIntArray(); // The empty-search result background private AllAppsBackgroundDrawable mEmptySearchBackground; @@ -109,11 +108,52 @@ public class AllAppsRecyclerView extends BaseRecyclerView } /** - * Sets the heights of the icons in this view (for scroll calculations). + * Ensures that we can present a stable scrollbar for views of varying types by pre-measuring + * all the different view types. */ - public void setPremeasuredIconHeights(int predictionIconHeight, int iconHeight) { - mPredictionIconHeight = predictionIconHeight; - mIconHeight = iconHeight; + public void preMeasureViews(AllAppsGridAdapter adapter) { + final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec( + getResources().getDisplayMetrics().widthPixels, View.MeasureSpec.AT_MOST); + final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec( + getResources().getDisplayMetrics().heightPixels, View.MeasureSpec.AT_MOST); + + // Icons + BubbleTextView icon = (BubbleTextView) adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_ICON).mContent; + icon.applyDummyInfo(); + icon.measure(widthMeasureSpec, heightMeasureSpec); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, icon.getMeasuredHeight()); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, icon.getMeasuredHeight()); + + // Search divider + View searchDivider = adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER).mContent; + searchDivider.measure(widthMeasureSpec, heightMeasureSpec); + int searchDividerHeight = searchDivider.getMeasuredHeight(); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, searchDividerHeight); + + // Generic dividers + View divider = adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER).mContent; + divider.measure(widthMeasureSpec, heightMeasureSpec); + int dividerHeight = divider.getMeasuredHeight(); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, dividerHeight); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, dividerHeight); + + // Search views + View emptySearch = adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH).mContent; + emptySearch.measure(widthMeasureSpec, heightMeasureSpec); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, + emptySearch.getMeasuredHeight()); + View searchMarket = adapter.onCreateViewHolder(this, + AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET).mContent; + searchMarket.measure(widthMeasureSpec, heightMeasureSpec); + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, + searchMarket.getMeasuredHeight()); + + // Section breaks + mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SECTION_BREAK, 0); } /** @@ -234,8 +274,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView } // Update the fast scroll - int scrollY = getScrollTop(mScrollPosState); - int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows()); + int scrollY = getCurrentScrollY(); + int availableScrollHeight = getAvailableScrollHeight(); mFastScrollHelper.smoothScrollToSection(scrollY, availableScrollHeight, lastInfo); return lastInfo.sectionName; } @@ -249,6 +289,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView @Override public void setAdapter(Adapter adapter) { super.setAdapter(adapter); + adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + public void onChanged() { + mCachedScrollPositions.clear(); + } + }); mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter); } @@ -265,17 +310,16 @@ public class AllAppsRecyclerView extends BaseRecyclerView return; } - // Find the index and height of the first visible row (all rows have the same height) - int rowCount = mApps.getNumAppRows(); - getCurScrollState(mScrollPosState, AllAppsGridAdapter.VIEW_TYPE_MASK_ICON); - if (mScrollPosState.rowIndex < 0) { + // Skip early if, there no child laid out in the container. + int scrollY = getCurrentScrollY(); + if (scrollY < 0) { mScrollbar.setThumbOffset(-1, -1); return; } // Only show the scrollbar if there is height to be scrolled int availableScrollBarHeight = getAvailableScrollBarHeight(); - int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows()); + int availableScrollHeight = getAvailableScrollHeight(); if (availableScrollHeight <= 0) { mScrollbar.setThumbOffset(-1, -1); return; @@ -284,7 +328,6 @@ public class AllAppsRecyclerView extends BaseRecyclerView // Calculate the current scroll position, the scrollY of the recycler view accounts for the // view padding, while the scrollBarY is drawn right up to the background padding (ignoring // padding) - int scrollY = getScrollTop(mScrollPosState); int scrollBarY = mBackgroundPadding.top + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); @@ -330,55 +373,74 @@ public class AllAppsRecyclerView extends BaseRecyclerView } } } else { - synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount); + synchronizeScrollBarThumbOffsetToViewScroll(scrollY, availableScrollHeight); } } - /** - * Returns the current scroll state of the apps rows. - */ - protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) { - stateOut.rowIndex = -1; - stateOut.rowTopOffset = -1; - stateOut.itemPos = -1; + @Override + protected boolean supportsFastScrolling() { + // Only allow fast scrolling when the user is not searching, since the results are not + // grouped in a meaningful order + return !mApps.hasFilter(); + } + @Override + protected int getCurrentScrollY() { // Return early if there are no items or we haven't been measured List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); - if (items.isEmpty() || mNumAppsPerRow == 0) { - return; + if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) { + return -1; + } + + // Calculate the y and offset for the item + View child = getChildAt(0); + int position = getChildPosition(child); + if (position == NO_POSITION) { + return -1; } + return getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child)); + } - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - int position = getChildPosition(child); - if (position != NO_POSITION) { - AlphabeticalAppsList.AdapterItem item = items.get(position); - if (AllAppsGridAdapter.isViewType(item.viewType, viewTypeMask)) { - stateOut.rowIndex = item.rowIndex; - stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); - stateOut.itemPos = position; - return; + public int getCurrentScrollY(int position, int offset) { + List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems(); + AlphabeticalAppsList.AdapterItem posItem = position < items.size() ? + items.get(position) : null; + int y = mCachedScrollPositions.get(position, -1); + if (y < 0) { + y = 0; + for (int i = 0; i < position; i++) { + AlphabeticalAppsList.AdapterItem item = items.get(i); + if (AllAppsGridAdapter.isIconViewType(item.viewType)) { + // Break once we reach the desired row + if (posItem != null && posItem.viewType == item.viewType && + posItem.rowIndex == item.rowIndex) { + break; + } + // Otherwise, only account for the first icon in the row since they are the same + // size within a row + if (item.rowAppIndex == 0) { + y += mViewHeights.get(item.viewType, 0); + } + } else { + // Rest of the views span the full width + y += mViewHeights.get(item.viewType, 0); } } + mCachedScrollPositions.put(position, y); } - return; - } - @Override - protected boolean supportsFastScrolling() { - // Only allow fast scrolling when the user is not searching, since the results are not - // grouped in a meaningful order - return !mApps.hasFilter(); + return getPaddingTop() + y - offset; } - protected int getTop(int rowIndex) { - if (getChildCount() == 0 || rowIndex <= 0) { - return 0; - } - - // The prediction bar icons have more padding, so account for that in the row offset - return mPredictionIconHeight + (rowIndex - 1) * mIconHeight; + /** + * Returns the available scroll height: + * AvailableScrollHeight = Total height of the all items - last page height + */ + @Override + protected int getAvailableScrollHeight() { + int paddedHeight = getCurrentScrollY(mApps.getAdapterItems().size(), 0); + int totalHeight = paddedHeight + getPaddingBottom(); + return totalHeight - getVisibleHeight(); } /** diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 2e3cc1aa1..097520605 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -33,7 +33,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView { private static final String TAG = "WidgetsRecyclerView"; private WidgetsModel mWidgets; - private ScrollPositionState mScrollPosState = new ScrollPositionState(); public WidgetsRecyclerView(Context context) { this(context, null); @@ -99,9 +98,8 @@ public class WidgetsRecyclerView extends BaseRecyclerView { stopScroll(); int rowCount = mWidgets.getPackageSize(); - getCurScrollState(mScrollPosState, -1); float pos = rowCount * touchFraction; - int availableScrollHeight = getAvailableScrollHeight(rowCount); + int availableScrollHeight = getAvailableScrollHeight(); LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); @@ -121,45 +119,41 @@ public class WidgetsRecyclerView extends BaseRecyclerView { } // Skip early if, there no child laid out in the container. - getCurScrollState(mScrollPosState, -1); - if (mScrollPosState.rowIndex < 0) { + int scrollY = getCurrentScrollY(); + if (scrollY < 0) { mScrollbar.setThumbOffset(-1, -1); return; } - synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, mWidgets.getPackageSize()); + synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight()); } - /** - * Returns the current scroll state. - */ - protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) { - stateOut.rowIndex = -1; - stateOut.rowTopOffset = -1; - stateOut.itemPos = -1; - + @Override + protected int getCurrentScrollY() { // Skip early if widgets are not bound. - if (isModelNotReady()) { - return; + if (isModelNotReady() || getChildCount() == 0) { + return -1; } View child = getChildAt(0); - int position = getChildPosition(child); + int rowIndex = getChildPosition(child); + int y = (child.getMeasuredHeight() * rowIndex); + int offset = getLayoutManager().getDecoratedTop(child); - stateOut.rowIndex = position; - stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child); - stateOut.itemPos = position; + return getPaddingTop() + y - offset; } + /** + * Returns the available scroll height: + * AvailableScrollHeight = Total height of the all items - last page height + */ @Override - protected int getTop(int rowIndex) { - if (getChildCount() == 0) { - return 0; - } - - // All the rows are the same height, return any child height + protected int getAvailableScrollHeight() { View child = getChildAt(0); - return child.getMeasuredHeight() * rowIndex; + int height = child.getMeasuredHeight() * mWidgets.getPackageSize(); + int totalHeight = getPaddingTop() + height + getPaddingBottom(); + int availableScrollHeight = totalHeight - getVisibleHeight(); + return availableScrollHeight; } private boolean isModelNotReady() { |