Merge "Make TextViewCompat#setTextAppearance work with fonts" into oc-support-26.0-dev
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
index 148fef0..ebfa813 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
@@ -17,8 +17,10 @@
import android.support.annotation.Nullable;
import android.support.v4.util.CircularIntArray;
import android.support.v7.widget.RecyclerView;
+import android.util.SparseIntArray;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* A grid is representation of single or multiple rows layout data structure and algorithm.
@@ -42,6 +44,8 @@
*/
public static final int START_DEFAULT = -1;
+ Object[] mTmpItem = new Object[1];
+
/**
* When user uses Grid, he should provide count of items and
* the method to create item and remove item.
@@ -70,9 +74,12 @@
* @param append True if new item is after last visible item, false if new item is
* before first visible item.
* @param item item[0] returns created item that will be passed in addItem() call.
+ * @param disappearingItem The item is a disappearing item added by
+ * {@link Grid#fillDisappearingItems(int[], int, SparseIntArray)}.
+ *
* @return length of the item.
*/
- int createItem(int index, boolean append, Object[] item);
+ int createItem(int index, boolean append, Object[] item, boolean disappearingItem);
/**
* add item to given row and given edge. The call is always after createItem().
@@ -399,6 +406,8 @@
/**
* Removes invisible items from end until reaches item at aboveIndex or toLimit.
+ * @param aboveIndex Don't remove items whose index is equals or smaller than aboveIndex
+ * @param toLimit Don't remove items whose left edge is less than toLimit.
*/
public void removeInvisibleItemsAtEnd(int aboveIndex, int toLimit) {
while(mLastVisibleIndex >= mFirstVisibleIndex && mLastVisibleIndex > aboveIndex) {
@@ -416,13 +425,15 @@
/**
* Removes invisible items from front until reaches item at belowIndex or toLimit.
+ * @param belowIndex Don't remove items whose index is equals or larger than belowIndex
+ * @param toLimit Don't remove items whose right edge is equals or greater than toLimit.
*/
public void removeInvisibleItemsAtFront(int belowIndex, int toLimit) {
while(mLastVisibleIndex >= mFirstVisibleIndex && mFirstVisibleIndex < belowIndex) {
- boolean offFront = !mReversedFlow ? mProvider.getEdge(mFirstVisibleIndex)
- + mProvider.getSize(mFirstVisibleIndex) <= toLimit
- : mProvider.getEdge(mFirstVisibleIndex)
- - mProvider.getSize(mFirstVisibleIndex) >= toLimit;
+ final int size = mProvider.getSize(mFirstVisibleIndex);
+ boolean offFront = !mReversedFlow
+ ? mProvider.getEdge(mFirstVisibleIndex) + size <= toLimit
+ : mProvider.getEdge(mFirstVisibleIndex) - size >= toLimit;
if (offFront) {
mProvider.removeItem(mFirstVisibleIndex);
mFirstVisibleIndex++;
@@ -440,6 +451,73 @@
}
/**
+ * Fill disappearing items, i.e. the items are moved out of window, we need give them final
+ * location so recyclerview will run a slide out animation. The positions that was greater than
+ * last visible index will be appended to end, the positions that was smaller than first visible
+ * index will be prepend to beginning.
+ * @param positions Sorted list of positions of disappearing items.
+ * @param positionToRow Which row we want to put the disappearing item.
+ */
+ public void fillDisappearingItems(int[] positions, int positionsLength,
+ SparseIntArray positionToRow) {
+ final int lastPos = getLastVisibleIndex();
+ final int resultSearchLast = lastPos >= 0
+ ? Arrays.binarySearch(positions, 0, positionsLength, lastPos) : 0;
+ if (resultSearchLast < 0) {
+ // we shouldn't find lastPos in disappearing position list.
+ int firstDisappearingIndex = -resultSearchLast - 1;
+ int edge;
+ if (mReversedFlow) {
+ edge = mProvider.getEdge(lastPos) - mProvider.getSize(lastPos) - mSpacing;
+ } else {
+ edge = mProvider.getEdge(lastPos) + mProvider.getSize(lastPos) + mSpacing;
+ }
+ for (int i = firstDisappearingIndex; i < positionsLength; i++) {
+ int disappearingIndex = positions[i];
+ int disappearingRow = positionToRow.get(disappearingIndex);
+ if (disappearingRow < 0) {
+ disappearingRow = 0; // if not found put in row 0
+ }
+ int size = mProvider.createItem(disappearingIndex, true, mTmpItem, true);
+ mProvider.addItem(mTmpItem[0], disappearingIndex, size, disappearingRow, edge);
+ if (mReversedFlow) {
+ edge = edge - size - mSpacing;
+ } else {
+ edge = edge + size + mSpacing;
+ }
+ }
+ }
+
+ final int firstPos = getFirstVisibleIndex();
+ final int resultSearchFirst = firstPos >= 0
+ ? Arrays.binarySearch(positions, 0, positionsLength, firstPos) : 0;
+ if (resultSearchFirst < 0) {
+ // we shouldn't find firstPos in disappearing position list.
+ int firstDisappearingIndex = -resultSearchFirst - 2;
+ int edge;
+ if (mReversedFlow) {
+ edge = mProvider.getEdge(firstPos);
+ } else {
+ edge = mProvider.getEdge(firstPos);
+ }
+ for (int i = firstDisappearingIndex; i >= 0; i--) {
+ int disappearingIndex = positions[i];
+ int disappearingRow = positionToRow.get(disappearingIndex);
+ if (disappearingRow < 0) {
+ disappearingRow = 0; // if not found put in row 0
+ }
+ int size = mProvider.createItem(disappearingIndex, false, mTmpItem, true);
+ if (mReversedFlow) {
+ edge = edge + mSpacing + size;
+ } else {
+ edge = edge - mSpacing - size;
+ }
+ mProvider.addItem(mTmpItem[0], disappearingIndex, size, disappearingRow, edge);
+ }
+ }
+ }
+
+ /**
* Queries items adjacent to the viewport (in the direction of da) into the prefetch registry.
*/
public void collectAdjacentPrefetchPositions(int fromLimit, int da,
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index b9d4967..0fbffca 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -38,6 +38,7 @@
import android.support.v7.widget.RecyclerView.State;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.SparseIntArray;
import android.view.FocusFinder;
import android.view.Gravity;
import android.view.View;
@@ -49,6 +50,8 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
final class GridLayoutManager extends RecyclerView.LayoutManager {
@@ -433,10 +436,10 @@
// appends and prepends due to the fact leanback is doing mario scrolling: removing items to
// the left of focused item might need extra layout on the right.
int mExtraLayoutSpaceInPreLayout;
- // mRetainedFirstPosInPostLayout and mRetainedLastPosInPostLayout are used in post layout pass
- // to add those item even they are out side visible area.
- int mRetainedFirstPosInPostLayout;
- int mRetainedLastPosInPostLayout;
+ // mPositionToRowInPostLayout and mDisappearingPositions are temp variables in post layout.
+ final SparseIntArray mPositionToRowInPostLayout = new SparseIntArray();
+ int[] mDisappearingPositions;
+
RecyclerView.Recycler mRecycler;
private static final Rect sTempRect = new Rect();
@@ -1121,8 +1124,6 @@
mState = state;
mPositionDeltaInPreLayout = 0;
mExtraLayoutSpaceInPreLayout = 0;
- mRetainedFirstPosInPostLayout = Integer.MAX_VALUE;
- mRetainedLastPosInPostLayout = Integer.MIN_VALUE;
}
/**
@@ -1133,8 +1134,6 @@
mState = null;
mPositionDeltaInPreLayout = 0;
mExtraLayoutSpaceInPreLayout = 0;
- mRetainedFirstPosInPostLayout = Integer.MAX_VALUE;
- mRetainedLastPosInPostLayout = Integer.MIN_VALUE;
}
/**
@@ -1522,7 +1521,7 @@
}
@Override
- public int createItem(int index, boolean append, Object[] item) {
+ public int createItem(int index, boolean append, Object[] item, boolean disappearingItem) {
if (TRACE) TraceCompat.beginSection("createItem");
if (TRACE) TraceCompat.beginSection("getview");
View v = getViewForPosition(index - mPositionDeltaInPreLayout);
@@ -1533,10 +1532,18 @@
// See recyclerView docs: we don't need re-add scraped view if it was removed.
if (!lp.isItemRemoved()) {
if (TRACE) TraceCompat.beginSection("addView");
- if (append) {
- addView(v);
+ if (disappearingItem) {
+ if (append) {
+ addDisappearingView(v);
+ } else {
+ addDisappearingView(v, 0);
+ }
} else {
- addView(v, 0);
+ if (append) {
+ addView(v);
+ } else {
+ addView(v, 0);
+ }
}
if (TRACE) TraceCompat.endSection();
if (mChildVisibility != -1) {
@@ -1731,16 +1738,14 @@
private void removeInvisibleViewsAtEnd() {
if (mPruneChild && !mIsSlidingChildViews) {
- int retainedLastPos = Math.max(mRetainedLastPosInPostLayout, mFocusPosition);
- mGrid.removeInvisibleItemsAtEnd(retainedLastPos,
+ mGrid.removeInvisibleItemsAtEnd(mFocusPosition,
mReverseFlowPrimary ? -mExtraLayoutSpace : mSizePrimary + mExtraLayoutSpace);
}
}
private void removeInvisibleViewsAtFront() {
if (mPruneChild && !mIsSlidingChildViews) {
- int retainedFirstPos = Math.min(mRetainedFirstPosInPostLayout, mFocusPosition);
- mGrid.removeInvisibleItemsAtFront(retainedFirstPos,
+ mGrid.removeInvisibleItemsAtFront(mFocusPosition,
mReverseFlowPrimary ? mSizePrimary + mExtraLayoutSpace: -mExtraLayoutSpace);
}
}
@@ -1979,6 +1984,48 @@
return true;
}
+ void updatePositionToRowMapInPostLayout() {
+ mPositionToRowInPostLayout.clear();
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ int position = getAdapterPositionByIndex(i);
+ Grid.Location loc = mGrid.getLocation(position);
+ if (loc != null) {
+ mPositionToRowInPostLayout.put(position, loc.row);
+ }
+ }
+ }
+
+ void fillScrapViewsInPostLayout() {
+ List<RecyclerView.ViewHolder> scrapList = mRecycler.getScrapList();
+ final int scrapSize = scrapList.size();
+ if (scrapSize == 0) {
+ return;
+ }
+ // initialize the int array or re-allocate the array.
+ if (mDisappearingPositions == null || scrapSize > mDisappearingPositions.length) {
+ int length = mDisappearingPositions == null ? 16 : mDisappearingPositions.length;
+ while (length < scrapSize) {
+ length = length << 1;
+ }
+ mDisappearingPositions = new int[length];
+ }
+ int totalItems = 0;
+ for (int i = 0; i < scrapSize; i++) {
+ int pos = scrapList.get(i).getAdapterPosition();
+ if (pos >= 0) {
+ mDisappearingPositions[totalItems++] = pos;
+ }
+ }
+ // totalItems now has the length of disappearing items
+ if (totalItems > 0) {
+ Arrays.sort(mDisappearingPositions, 0, totalItems);
+ mGrid.fillDisappearingItems(mDisappearingPositions, totalItems,
+ mPositionToRowInPostLayout);
+ }
+ mPositionToRowInPostLayout.clear();
+ }
+
// Lays out items based on the current scroll position
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
@@ -2020,6 +2067,10 @@
if (mGrid != null && childCount > 0) {
int minChangedEdge = Integer.MAX_VALUE;
int maxChangeEdge = Integer.MIN_VALUE;
+ int minOldAdapterPosition = mBaseGridView.getChildViewHolder(
+ getChildAt(0)).getOldPosition();
+ int maxOldAdapterPosition = mBaseGridView.getChildViewHolder(
+ getChildAt(childCount - 1)).getOldPosition();
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
LayoutParams lp = (LayoutParams) view.getLayoutParams();
@@ -2029,13 +2080,17 @@
mPositionDeltaInPreLayout = mGrid.getFirstVisibleIndex()
- lp.getViewLayoutPosition();
}
+ int newAdapterPosition = mBaseGridView.getChildAdapterPosition(view);
// if either of following happening
// 1. item itself has changed or layout parameter changed
// 2. item is losing focus
// 3. item is gaining focus
+ // 4. item is moved out of old adapter position range.
if (lp.isItemChanged() || lp.isItemRemoved() || view.isLayoutRequested()
|| (!view.hasFocus() && mFocusPosition == lp.getViewAdapterPosition())
- || (view.hasFocus() && mFocusPosition != lp.getViewAdapterPosition())) {
+ || (view.hasFocus() && mFocusPosition != lp.getViewAdapterPosition())
+ || newAdapterPosition < minOldAdapterPosition
+ || newAdapterPosition > maxOldAdapterPosition) {
minChangedEdge = Math.min(minChangedEdge, getViewMin(view));
maxChangeEdge = Math.max(maxChangeEdge, getViewMax(view));
}
@@ -2053,25 +2108,9 @@
return;
}
- // figure out the child positions that needs to be retained in post layout pass, e.g.
- // When a child is pushed out, they needs to be added in post layout pass.
- if (state.willRunPredictiveAnimations() && getChildCount() > 0) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- int pos = mBaseGridView.getChildViewHolder(child).getAdapterPosition();
- if (pos >= 0) {
- mRetainedFirstPosInPostLayout = pos;
- break;
- }
- }
- for (int i = getChildCount() - 1; i >= 0; i--) {
- View child = getChildAt(i);
- int pos = mBaseGridView.getChildViewHolder(child).getAdapterPosition();
- if (pos >= 0) {
- mRetainedLastPosInPostLayout = pos;
- break;
- }
- }
+ // save all view's row information before detach all views
+ if (state.willRunPredictiveAnimations()) {
+ updatePositionToRowMapInPostLayout();
}
// check if we need align to mFocusPosition, this is usually true unless in smoothScrolling
final boolean scrollToFocus = !isSmoothScrolling()
@@ -2149,11 +2188,8 @@
removeInvisibleViewsAtEnd();
}
}
- while (mGrid.getLastVisibleIndex() < mRetainedLastPosInPostLayout) {
- appendOneColumnVisibleItems();
- }
- while (mGrid.getFirstVisibleIndex() > mRetainedFirstPosInPostLayout) {
- prependOneColumnVisibleItems();
+ if (state.willRunPredictiveAnimations()) {
+ fillScrapViewsInPostLayout();
}
if (DEBUG) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java b/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
index 226bd51..56a3188 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
@@ -25,7 +25,6 @@
class SingleRow extends Grid {
private final Location mTmpLocation = new Location(0);
- private Object[] mTmpItem = new Object[1];
SingleRow() {
setNumRows(1);
@@ -78,7 +77,7 @@
boolean filledOne = false;
int minIndex = mProvider.getMinIndex();
for (int index = getStartIndexForPrepend(); index >= minIndex; index--) {
- int size = mProvider.createItem(index, false, mTmpItem);
+ int size = mProvider.createItem(index, false, mTmpItem, false);
int edge;
if (mFirstVisibleIndex < 0 || mLastVisibleIndex < 0) {
edge = mReversedFlow ? Integer.MIN_VALUE : Integer.MAX_VALUE;
@@ -111,7 +110,7 @@
}
boolean filledOne = false;
for (int index = getStartIndexForAppend(); index < mProvider.getCount(); index++) {
- int size = mProvider.createItem(index, true, mTmpItem);
+ int size = mProvider.createItem(index, true, mTmpItem, false);
int edge;
if (mFirstVisibleIndex < 0 || mLastVisibleIndex< 0) {
edge = mReversedFlow ? Integer.MAX_VALUE : Integer.MIN_VALUE;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
index eb9528e..fb47c5b 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
@@ -67,8 +67,6 @@
// <= mFirstIndex + mLocations.size() - 1
protected int mFirstIndex = -1;
- private Object[] mTmpItem = new Object[1];
-
protected Object mPendingItem;
protected int mPendingItemSize;
@@ -167,7 +165,7 @@
for (; itemIndex >= firstIndex; itemIndex--) {
Location loc = getLocation(itemIndex);
int rowIndex = loc.row;
- int size = mProvider.createItem(itemIndex, false, mTmpItem);
+ int size = mProvider.createItem(itemIndex, false, mTmpItem, false);
if (size != loc.size) {
mLocations.removeFromStart(itemIndex + 1 - mFirstIndex);
mFirstIndex = mFirstVisibleIndex;
@@ -254,7 +252,7 @@
item = mPendingItem;
mPendingItem = null;
} else {
- loc.size = mProvider.createItem(itemIndex, false, mTmpItem);
+ loc.size = mProvider.createItem(itemIndex, false, mTmpItem, false);
item = mTmpItem[0];
}
mFirstIndex = mFirstVisibleIndex = itemIndex;
@@ -324,7 +322,7 @@
edge = edge + loc.offset;
}
int rowIndex = loc.row;
- int size = mProvider.createItem(itemIndex, true, mTmpItem);
+ int size = mProvider.createItem(itemIndex, true, mTmpItem, false);
if (size != loc.size) {
loc.size = size;
mLocations.removeFromEnd(lastIndex - itemIndex);
@@ -388,7 +386,7 @@
item = mPendingItem;
mPendingItem = null;
} else {
- loc.size = mProvider.createItem(itemIndex, true, mTmpItem);
+ loc.size = mProvider.createItem(itemIndex, true, mTmpItem, false);
item = mTmpItem[0];
}
if (mLocations.size() == 1) {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
index c97617d..d9f446f 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridActivity.java
@@ -263,6 +263,17 @@
mGridView.getAdapter().notifyItemMoved(index2 - 1, index1);
}
+ void moveItem(int index1, int index2, boolean notify) {
+ if (index1 == index2) {
+ return;
+ }
+ int[] items = removeItems(index1, 1, false);
+ addItems(index2, items, false);
+ if (notify) {
+ mGridView.getAdapter().notifyItemMoved(index1, index2);
+ }
+ }
+
void changeArraySize(int length) {
mNumItems = length;
mGridView.getAdapter().notifyDataSetChanged();
@@ -299,6 +310,10 @@
}
void addItems(int index, int[] items) {
+ addItems(index, items, true);
+ }
+
+ void addItems(int index, int[] items, boolean notify) {
int length = items.length;
if (mItemLengths.length < mNumItems + length) {
int[] array = new int[mNumItems + length];
@@ -308,7 +323,7 @@
System.arraycopy(mItemLengths, index, mItemLengths, index + length, mNumItems - index);
System.arraycopy(items, 0, mItemLengths, index, length);
mNumItems += length;
- if (mGridView.getAdapter() != null) {
+ if (notify && mGridView.getAdapter() != null) {
mGridView.getAdapter().notifyItemRangeInserted(index, length);
}
}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
index 605adfa..43793f9 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridTest.java
@@ -43,7 +43,7 @@
}
@Override
- public int createItem(int index, boolean append, Object[] item) {
+ public int createItem(int index, boolean append, Object[] item, boolean disappearingItem) {
return mItems[index];
}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
index 8b38db0..e3af480 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/GridWidgetTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
@@ -930,6 +931,194 @@
verifyBeginAligned();
}
+ void waitOneUiCycle() throws Throwable {
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ }
+ });
+ }
+
+ @Test
+ public void testDontPruneMovingItem() throws Throwable {
+ Intent intent = new Intent();
+ intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+ intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+ intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000);
+ initActivity(intent);
+ mOrientation = BaseGridView.HORIZONTAL;
+ mNumRows = 1;
+
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.getItemAnimator().setMoveDuration(2000);
+ mGridView.setSelectedPosition(50);
+ }
+ });
+ waitForScrollIdle();
+ final ArrayList<RecyclerView.ViewHolder> moveViewHolders = new ArrayList();
+ for (int i = 51;; i++) {
+ RecyclerView.ViewHolder vh = mGridView.findViewHolderForAdapterPosition(i);
+ if (vh == null) {
+ break;
+ }
+ moveViewHolders.add(vh);
+ }
+
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // add a lot of items, so we will push everything to right of 51 out side window
+ int[] lots_items = new int[1000];
+ for (int i = 0; i < lots_items.length; i++) {
+ lots_items[i] = 300;
+ }
+ mActivity.addItems(51, lots_items);
+ }
+ });
+ waitOneUiCycle();
+ // run a scroll pass, the scroll pass should not remove the animating views even they are
+ // outside visible areas.
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.scrollBy(-3, 0);
+ }
+ });
+ waitOneUiCycle();
+ for (int i = 0; i < moveViewHolders.size(); i++) {
+ assertSame(mGridView, moveViewHolders.get(i).itemView.getParent());
+ }
+ }
+
+ @Test
+ public void testMoveItemToTheRight() throws Throwable {
+ Intent intent = new Intent();
+ intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+ intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+ intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000);
+ initActivity(intent);
+ mOrientation = BaseGridView.HORIZONTAL;
+ mNumRows = 1;
+
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.getItemAnimator().setAddDuration(2000);
+ mGridView.getItemAnimator().setMoveDuration(2000);
+ mGridView.setSelectedPosition(50);
+ }
+ });
+ waitForScrollIdle();
+ RecyclerView.ViewHolder moveViewHolder = mGridView.findViewHolderForAdapterPosition(51);
+
+ int lastPos = mGridView.getChildAdapterPosition(mGridView.getChildAt(
+ mGridView.getChildCount() - 1));
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mActivity.moveItem(51, 1000, true);
+ }
+ });
+ final ArrayList<View> moveInViewHolders = new ArrayList();
+ waitForItemAnimationStart();
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < mGridView.getLayoutManager().getChildCount(); i++) {
+ View v = mGridView.getLayoutManager().getChildAt(i);
+ if (Math.abs(v.getTranslationX()) > 5) {
+ moveInViewHolders.add(v);
+ }
+ }
+ }
+ });
+ waitOneUiCycle();
+ assertTrue("prelayout should layout extra items to slide in",
+ moveInViewHolders.size() > lastPos - 51);
+ // run a scroll pass, the scroll pass should not remove the animating views even they are
+ // outside visible areas.
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.scrollBy(-3, 0);
+ }
+ });
+ waitOneUiCycle();
+ for (int i = 0; i < moveInViewHolders.size(); i++) {
+ assertSame(mGridView, moveInViewHolders.get(i).getParent());
+ }
+ assertSame(mGridView, moveViewHolder.itemView.getParent());
+ assertFalse(moveViewHolder.isRecyclable());
+ waitForItemAnimation();
+ assertNull(moveViewHolder.itemView.getParent());
+ assertTrue(moveViewHolder.isRecyclable());
+ }
+
+ @Test
+ public void testMoveItemToTheLeft() throws Throwable {
+ Intent intent = new Intent();
+ intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.horizontal_linear);
+ intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+ intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, 2000);
+ initActivity(intent);
+ mOrientation = BaseGridView.HORIZONTAL;
+ mNumRows = 1;
+
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.getItemAnimator().setAddDuration(2000);
+ mGridView.getItemAnimator().setMoveDuration(2000);
+ mGridView.setSelectedPosition(1500);
+ }
+ });
+ waitForScrollIdle();
+ RecyclerView.ViewHolder moveViewHolder = mGridView.findViewHolderForAdapterPosition(1499);
+
+ int firstPos = mGridView.getChildAdapterPosition(mGridView.getChildAt(0));
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mActivity.moveItem(1499, 1, true);
+ }
+ });
+ final ArrayList<View> moveInViewHolders = new ArrayList();
+ waitForItemAnimationStart();
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < mGridView.getLayoutManager().getChildCount(); i++) {
+ View v = mGridView.getLayoutManager().getChildAt(i);
+ if (Math.abs(v.getTranslationX()) > 5) {
+ moveInViewHolders.add(v);
+ }
+ }
+ }
+ });
+ waitOneUiCycle();
+ assertTrue("prelayout should layout extra items to slide in ",
+ moveInViewHolders.size() > 1499 - firstPos);
+ // run a scroll pass, the scroll pass should not remove the animating views even they are
+ // outside visible areas.
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.scrollBy(3, 0);
+ }
+ });
+ waitOneUiCycle();
+ for (int i = 0; i < moveInViewHolders.size(); i++) {
+ assertSame(mGridView, moveInViewHolders.get(i).getParent());
+ }
+ assertSame(mGridView, moveViewHolder.itemView.getParent());
+ assertFalse(moveViewHolder.isRecyclable());
+ waitForItemAnimation();
+ assertNull(moveViewHolder.itemView.getParent());
+ assertTrue(moveViewHolder.isRecyclable());
+ }
+
@Test
public void testContinuousSwapForward() throws Throwable {
Intent intent = new Intent();