Merge "Cleaning up scrollbar logic to properly calculate stable extents." into ub-launcher3-calgary
This commit is contained in:
commit
b029e9fd66
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/all_apps_divider_color" />
|
||||
<size android:height="1dp" />
|
||||
</shape>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?android:attr/colorAccent" />
|
||||
<size android:height="1dp" />
|
||||
</shape>
|
|
@ -13,13 +13,14 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/all_apps_divider_height"
|
||||
android:layout_marginBottom="@dimen/all_apps_divider_margin_vertical"
|
||||
android:layout_marginLeft="@dimen/container_fastscroll_thumb_max_width"
|
||||
android:layout_marginRight="@dimen/container_fastscroll_thumb_max_width"
|
||||
android:layout_marginTop="@dimen/all_apps_divider_margin_vertical"
|
||||
android:background="@color/all_apps_divider_color"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/all_apps_divider_margin_vertical"
|
||||
android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
|
||||
android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
|
||||
android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
|
||||
android:src="@drawable/all_apps_divider"
|
||||
android:scaleType="fitXY"
|
||||
android:focusable="false" />
|
|
@ -13,16 +13,13 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.BubbleTextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/Icon.AllApps"
|
||||
android:id="@+id/icon"
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:importantForAccessibility="no"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingTop="@dimen/all_apps_icon_top_bottom_padding"
|
||||
android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding"
|
||||
android:focusable="true"
|
||||
launcher:iconDisplay="all_apps" />
|
||||
|
||||
android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
|
||||
android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
|
||||
android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
|
||||
android:src="@drawable/all_apps_search_divider"
|
||||
android:scaleType="fitXY"
|
||||
android:focusable="false" />
|
|
@ -80,7 +80,6 @@
|
|||
<dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
|
||||
<dimen name="all_apps_header_shadow_height">6dp</dimen>
|
||||
|
||||
<dimen name="all_apps_divider_height">1dp</dimen>
|
||||
<dimen name="all_apps_divider_margin_vertical">8dp</dimen>
|
||||
|
||||
<dimen name="all_apps_bezel_swipe_height">24dp</dimen>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -366,26 +366,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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,41 +373,10 @@ 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsFastScrolling() {
|
||||
// Only allow fast scrolling when the user is not searching, since the results are not
|
||||
|
@ -372,13 +384,63 @@ public class AllAppsRecyclerView extends BaseRecyclerView
|
|||
return !mApps.hasFilter();
|
||||
}
|
||||
|
||||
protected int getTop(int rowIndex) {
|
||||
if (getChildCount() == 0 || rowIndex <= 0) {
|
||||
return 0;
|
||||
@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 || getChildCount() == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The prediction bar icons have more padding, so account for that in the row offset
|
||||
return mPredictionIconHeight + (rowIndex - 1) * mIconHeight;
|
||||
// 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));
|
||||
}
|
||||
|
||||
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 getPaddingTop() + y - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current scroll state.
|
||||
*/
|
||||
protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) {
|
||||
stateOut.rowIndex = -1;
|
||||
stateOut.rowTopOffset = -1;
|
||||
stateOut.itemPos = -1;
|
||||
|
||||
// Skip early if widgets are not bound.
|
||||
if (isModelNotReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
View child = getChildAt(0);
|
||||
int position = getChildPosition(child);
|
||||
|
||||
stateOut.rowIndex = position;
|
||||
stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
|
||||
stateOut.itemPos = position;
|
||||
synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getTop(int rowIndex) {
|
||||
if (getChildCount() == 0) {
|
||||
return 0;
|
||||
protected int getCurrentScrollY() {
|
||||
// Skip early if widgets are not bound.
|
||||
if (isModelNotReady() || getChildCount() == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// All the rows are the same height, return any child height
|
||||
View child = getChildAt(0);
|
||||
return child.getMeasuredHeight() * rowIndex;
|
||||
int rowIndex = getChildPosition(child);
|
||||
int y = (child.getMeasuredHeight() * rowIndex);
|
||||
int offset = getLayoutManager().getDecoratedTop(child);
|
||||
|
||||
return getPaddingTop() + y - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available scroll height:
|
||||
* AvailableScrollHeight = Total height of the all items - last page height
|
||||
*/
|
||||
@Override
|
||||
protected int getAvailableScrollHeight() {
|
||||
View child = getChildAt(0);
|
||||
int height = child.getMeasuredHeight() * mWidgets.getPackageSize();
|
||||
int totalHeight = getPaddingTop() + height + getPaddingBottom();
|
||||
int availableScrollHeight = totalHeight - getVisibleHeight();
|
||||
return availableScrollHeight;
|
||||
}
|
||||
|
||||
private boolean isModelNotReady() {
|
||||
|
|
Loading…
Reference in New Issue