diff --git a/app/build.gradle b/app/build.gradle
index f19c7b4804..29a0b7b28a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -97,13 +97,11 @@ android {
}
dependencies {
-
testCompile 'junit:junit:4.12'
-
- compile fileTree(dir: 'libs', include: '*.jar')
-
+ compile fileTree(include: '*.jar', dir: 'libs')
+ compile 'com.android.support:recyclerview-v7:23.2.1'
compile 'com.android.support:appcompat-v7:23.2.1'
-// compile 'com.android.support:cardview-v7:21.0.0'
+ // compile 'com.android.support:cardview-v7:21.0.0'
// fresco图片框架
compile 'com.facebook.fresco:fresco:0.12.0'
compile 'com.facebook.fresco:animated-gif:0.12.0'
@@ -132,14 +130,12 @@ dependencies {
compile 'com.jakewharton.rxbinding:rxbinding:0.3.0'
// compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.3.0'
// compile 'com.jakewharton.rxbinding:rxbinding-design:0.3.0'
-
// zxing 二维码扫描以及生成
compile 'com.google.zxing:core:3.2.1'
compile 'com.google.zxing:android-core:3.2.1'
//tinker
-// compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
-// compile "com.android.support:multidex:1.0.1"
-
+ // compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
+ // compile "com.android.support:multidex:1.0.1"
compile project(':libraries:EventBus')
compile project(':libraries:MiPush')
compile project(':libraries:MTA')
diff --git a/app/src/main/java/android/support/v7/widget/AdapterHelper.java b/app/src/main/java/android/support/v7/widget/AdapterHelper.java
deleted file mode 100644
index ecf0c294d4..0000000000
--- a/app/src/main/java/android/support/v7/widget/AdapterHelper.java
+++ /dev/null
@@ -1,699 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-package android.support.v7.widget;
-
-import android.support.v4.util.Pools;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static android.support.v7.widget.RecyclerView.ViewHolder;
-
-/**
- * Helper class that can enqueue and process adapter update operations.
- *
- * To support animations, RecyclerView presents an older version the Adapter to best represent
- * previous state of the layout. Sometimes, this is not trivial when items are removed that were
- * not laid out, in which case, RecyclerView has no way of providing that item's view for
- * animations.
- *
- * AdapterHelper creates an UpdateOp for each adapter data change then pre-processes them. During
- * pre processing, AdapterHelper finds out which UpdateOps can be deferred to second layout pass
- * and which cannot. For the UpdateOps that cannot be deferred, AdapterHelper will change them
- * according to previously deferred operation and dispatch them before the first layout pass. It
- * also takes care of updating deferred UpdateOps since order of operations is changed by this
- * process.
- *
- * Although operations may be forwarded to LayoutManager in different orders, resulting data set
- * is guaranteed to be the consistent.
- */
-class AdapterHelper implements OpReorderer.Callback {
-
- final static int POSITION_TYPE_INVISIBLE = 0;
-
- final static int POSITION_TYPE_NEW_OR_LAID_OUT = 1;
-
- private static final boolean DEBUG = false;
-
- private static final String TAG = "AHT";
-
- private Pools.Pool mUpdateOpPool = new Pools.SimplePool(UpdateOp.POOL_SIZE);
-
- final ArrayList mPendingUpdates = new ArrayList();
-
- final ArrayList mPostponedList = new ArrayList();
-
- final Callback mCallback;
-
- Runnable mOnItemProcessedCallback;
-
- final boolean mDisableRecycler;
-
- final OpReorderer mOpReorderer;
-
- AdapterHelper(Callback callback) {
- this(callback, false);
- }
-
- AdapterHelper(Callback callback, boolean disableRecycler) {
- mCallback = callback;
- mDisableRecycler = disableRecycler;
- mOpReorderer = new OpReorderer(this);
- }
-
- AdapterHelper addUpdateOp(UpdateOp... ops) {
- Collections.addAll(mPendingUpdates, ops);
- return this;
- }
-
- void reset() {
- recycleUpdateOpsAndClearList(mPendingUpdates);
- recycleUpdateOpsAndClearList(mPostponedList);
- }
-
- void preProcess() {
- mOpReorderer.reorderOps(mPendingUpdates);
- final int count = mPendingUpdates.size();
- for (int i = 0; i < count; i++) {
- UpdateOp op = mPendingUpdates.get(i);
- switch (op.cmd) {
- case UpdateOp.ADD:
- applyAdd(op);
- break;
- case UpdateOp.REMOVE:
- applyRemove(op);
- break;
- case UpdateOp.UPDATE:
- applyUpdate(op);
- break;
- case UpdateOp.MOVE:
- applyMove(op);
- break;
- }
- if (mOnItemProcessedCallback != null) {
- mOnItemProcessedCallback.run();
- }
- }
- mPendingUpdates.clear();
- }
-
- void consumePostponedUpdates() {
- final int count = mPostponedList.size();
- for (int i = 0; i < count; i++) {
- mCallback.onDispatchSecondPass(mPostponedList.get(i));
- }
- recycleUpdateOpsAndClearList(mPostponedList);
- }
-
- private void applyMove(UpdateOp op) {
- // MOVE ops are pre-processed so at this point, we know that item is still in the adapter.
- // otherwise, it would be converted into a REMOVE operation
- postponeAndUpdateViewHolders(op);
- }
-
- private void applyRemove(UpdateOp op) {
- int tmpStart = op.positionStart;
- int tmpCount = 0;
- int tmpEnd = op.positionStart + op.itemCount;
- int type = -1;
- for (int position = op.positionStart; position < tmpEnd; position++) {
- boolean typeChanged = false;
- ViewHolder vh = mCallback.findViewHolder(position);
- if (vh != null || canFindInPreLayout(position)) {
- // If a ViewHolder exists or this is a newly added item, we can defer this update
- // to post layout stage.
- // * For existing ViewHolders, we'll fake its existence in the pre-layout phase.
- // * For items that are added and removed in the same process cycle, they won't
- // have any effect in pre-layout since their add ops are already deferred to
- // post-layout pass.
- if (type == POSITION_TYPE_INVISIBLE) {
- // Looks like we have other updates that we cannot merge with this one.
- // Create an UpdateOp and dispatch it to LayoutManager.
- UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount);
- dispatchAndUpdateViewHolders(newOp);
- typeChanged = true;
- }
- type = POSITION_TYPE_NEW_OR_LAID_OUT;
- } else {
- // This update cannot be recovered because we don't have a ViewHolder representing
- // this position. Instead, post it to LayoutManager immediately
- if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
- // Looks like we have other updates that we cannot merge with this one.
- // Create UpdateOp op and dispatch it to LayoutManager.
- UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount);
- postponeAndUpdateViewHolders(newOp);
- typeChanged = true;
- }
- type = POSITION_TYPE_INVISIBLE;
- }
- if (typeChanged) {
- position -= tmpCount; // also equal to tmpStart
- tmpEnd -= tmpCount;
- tmpCount = 1;
- } else {
- tmpCount++;
- }
- }
- if (tmpCount != op.itemCount) { // all 1 effect
- recycleUpdateOp(op);
- op = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount);
- }
- if (type == POSITION_TYPE_INVISIBLE) {
- dispatchAndUpdateViewHolders(op);
- } else {
- postponeAndUpdateViewHolders(op);
- }
- }
-
- private void applyUpdate(UpdateOp op) {
- int tmpStart = op.positionStart;
- int tmpCount = 0;
- int tmpEnd = op.positionStart + op.itemCount;
- int type = -1;
- for (int position = op.positionStart; position < tmpEnd; position++) {
- ViewHolder vh = mCallback.findViewHolder(position);
- if (vh != null || canFindInPreLayout(position)) { // deferred
- if (type == POSITION_TYPE_INVISIBLE) {
- UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount);
- dispatchAndUpdateViewHolders(newOp);
- tmpCount = 0;
- tmpStart = position;
- }
- type = POSITION_TYPE_NEW_OR_LAID_OUT;
- } else { // applied
- if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
- UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount);
- postponeAndUpdateViewHolders(newOp);
- tmpCount = 0;
- tmpStart = position;
- }
- type = POSITION_TYPE_INVISIBLE;
- }
- tmpCount++;
- }
- if (tmpCount != op.itemCount) { // all 1 effect
- recycleUpdateOp(op);
- op = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount);
- }
- if (type == POSITION_TYPE_INVISIBLE) {
- dispatchAndUpdateViewHolders(op);
- } else {
- postponeAndUpdateViewHolders(op);
- }
- }
-
- private void dispatchAndUpdateViewHolders(UpdateOp op) {
- // tricky part.
- // traverse all postpones and revert their changes on this op if necessary, apply updated
- // dispatch to them since now they are after this op.
- if (op.cmd == UpdateOp.ADD || op.cmd == UpdateOp.MOVE) {
- throw new IllegalArgumentException("should not dispatch add or move for pre layout");
- }
- if (DEBUG) {
- Log.d(TAG, "dispatch (pre)" + op);
- Log.d(TAG, "postponed state before:");
- for (UpdateOp updateOp : mPostponedList) {
- Log.d(TAG, updateOp.toString());
- }
- Log.d(TAG, "----");
- }
-
- // handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial
- // TODO Since move ops are pushed to end, we should not need this anymore
- int tmpStart = updatePositionWithPostponed(op.positionStart, op.cmd);
- if (DEBUG) {
- Log.d(TAG, "pos:" + op.positionStart + ",updatedPos:" + tmpStart);
- }
- int tmpCnt = 1;
- int offsetPositionForPartial = op.positionStart;
- final int positionMultiplier;
- switch (op.cmd) {
- case UpdateOp.UPDATE:
- positionMultiplier = 1;
- break;
- case UpdateOp.REMOVE:
- positionMultiplier = 0;
- break;
- default:
- throw new IllegalArgumentException("op should be remove or update." + op);
- }
- for (int p = 1; p < op.itemCount; p++) {
- final int pos = op.positionStart + (positionMultiplier * p);
- int updatedPos = updatePositionWithPostponed(pos, op.cmd);
- if (DEBUG) {
- Log.d(TAG, "pos:" + pos + ",updatedPos:" + updatedPos);
- }
- boolean continuous = false;
- switch (op.cmd) {
- case UpdateOp.UPDATE:
- continuous = updatedPos == tmpStart + 1;
- break;
- case UpdateOp.REMOVE:
- continuous = updatedPos == tmpStart;
- break;
- }
- if (continuous) {
- tmpCnt++;
- } else {
- // need to dispatch this separately
- UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt);
- if (DEBUG) {
- Log.d(TAG, "need to dispatch separately " + tmp);
- }
- dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
- recycleUpdateOp(tmp);
- if (op.cmd == UpdateOp.UPDATE) {
- offsetPositionForPartial += tmpCnt;
- }
- tmpStart = updatedPos;// need to remove previously dispatched
- tmpCnt = 1;
- }
- }
- recycleUpdateOp(op);
- if (tmpCnt > 0) {
- UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt);
- if (DEBUG) {
- Log.d(TAG, "dispatching:" + tmp);
- }
- dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
- recycleUpdateOp(tmp);
- }
- if (DEBUG) {
- Log.d(TAG, "post dispatch");
- Log.d(TAG, "postponed state after:");
- for (UpdateOp updateOp : mPostponedList) {
- Log.d(TAG, updateOp.toString());
- }
- Log.d(TAG, "----");
- }
- }
-
- void dispatchFirstPassAndUpdateViewHolders(UpdateOp op, int offsetStart) {
- mCallback.onDispatchFirstPass(op);
- switch (op.cmd) {
- case UpdateOp.REMOVE:
- mCallback.offsetPositionsForRemovingInvisible(offsetStart, op.itemCount);
- break;
- case UpdateOp.UPDATE:
- mCallback.markViewHoldersUpdated(offsetStart, op.itemCount);
- break;
- default:
- throw new IllegalArgumentException("only remove and update ops can be dispatched"
- + " in first pass");
- }
- }
-
- private int updatePositionWithPostponed(int pos, int cmd) {
- final int count = mPostponedList.size();
- for (int i = count - 1; i >= 0; i--) {
- UpdateOp postponed = mPostponedList.get(i);
- if (postponed.cmd == UpdateOp.MOVE) {
- int start, end;
- if (postponed.positionStart < postponed.itemCount) {
- start = postponed.positionStart;
- end = postponed.itemCount;
- } else {
- start = postponed.itemCount;
- end = postponed.positionStart;
- }
- if (pos >= start && pos <= end) {
- //i'm affected
- if (start == postponed.positionStart) {
- if (cmd == UpdateOp.ADD) {
- postponed.itemCount++;
- } else if (cmd == UpdateOp.REMOVE) {
- postponed.itemCount--;
- }
- // op moved to left, move it right to revert
- pos++;
- } else {
- if (cmd == UpdateOp.ADD) {
- postponed.positionStart++;
- } else if (cmd == UpdateOp.REMOVE) {
- postponed.positionStart--;
- }
- // op was moved right, move left to revert
- pos--;
- }
- } else if (pos < postponed.positionStart) {
- // postponed MV is outside the dispatched OP. if it is before, offset
- if (cmd == UpdateOp.ADD) {
- postponed.positionStart++;
- postponed.itemCount++;
- } else if (cmd == UpdateOp.REMOVE) {
- postponed.positionStart--;
- postponed.itemCount--;
- }
- }
- } else {
- if (postponed.positionStart <= pos) {
- if (postponed.cmd == UpdateOp.ADD) {
- pos -= postponed.itemCount;
- } else if (postponed.cmd == UpdateOp.REMOVE) {
- pos += postponed.itemCount;
- }
- } else {
- if (cmd == UpdateOp.ADD) {
- postponed.positionStart++;
- } else if (cmd == UpdateOp.REMOVE) {
- postponed.positionStart--;
- }
- }
- }
- if (DEBUG) {
- Log.d(TAG, "dispath (step" + i + ")");
- Log.d(TAG, "postponed state:" + i + ", pos:" + pos);
- for (UpdateOp updateOp : mPostponedList) {
- Log.d(TAG, updateOp.toString());
- }
- Log.d(TAG, "----");
- }
- }
- for (int i = mPostponedList.size() - 1; i >= 0; i--) {
- UpdateOp op = mPostponedList.get(i);
- if (op.cmd == UpdateOp.MOVE) {
- if (op.itemCount == op.positionStart || op.itemCount < 0) {
- mPostponedList.remove(i);
- recycleUpdateOp(op);
- }
- } else if (op.itemCount <= 0) {
- mPostponedList.remove(i);
- recycleUpdateOp(op);
- }
- }
- return pos;
- }
-
- private boolean canFindInPreLayout(int position) {
- final int count = mPostponedList.size();
- for (int i = 0; i < count; i++) {
- UpdateOp op = mPostponedList.get(i);
- if (op.cmd == UpdateOp.MOVE) {
- if (findPositionOffset(op.itemCount, i + 1) == position) {
- return true;
- }
- } else if (op.cmd == UpdateOp.ADD) {
- // TODO optimize.
- final int end = op.positionStart + op.itemCount;
- for (int pos = op.positionStart; pos < end; pos++) {
- if (findPositionOffset(pos, i + 1) == position) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- private void applyAdd(UpdateOp op) {
- postponeAndUpdateViewHolders(op);
- }
-
- private void postponeAndUpdateViewHolders(UpdateOp op) {
- if (DEBUG) {
- Log.d(TAG, "postponing " + op);
- }
-// Utils.log("add UpdateOp to PostponedList");
- mPostponedList.add(op);
-// Utils.log("op" + op.positionStart + "=" + op.itemCount);
- switch (op.cmd) {
- case UpdateOp.ADD:
- mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
- break;
- case UpdateOp.MOVE:
- mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
- break;
- case UpdateOp.REMOVE:
- mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,
- op.itemCount);
- break;
- case UpdateOp.UPDATE:
- mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount);
- break;
- default:
- throw new IllegalArgumentException("Unknown update op type for " + op);
- }
- }
-
- boolean hasPendingUpdates() {
- return mPendingUpdates.size() > 0;
- }
-
- int findPositionOffset(int position) {
- return findPositionOffset(position, 0);
- }
-
- int findPositionOffset(int position, int firstPostponedItem) {
- int count = mPostponedList.size();
- for (int i = firstPostponedItem; i < count; ++i) {
- UpdateOp op = mPostponedList.get(i);
- if (op.cmd == UpdateOp.MOVE) {
- if (op.positionStart == position) {
- position = op.itemCount;
- } else {
- if (op.positionStart < position) {
- position--; // like a remove
- }
- if (op.itemCount <= position) {
- position++; // like an add
- }
- }
- } else if (op.positionStart <= position) {
- if (op.cmd == UpdateOp.REMOVE) {
- if (position < op.positionStart + op.itemCount) {
- return -1;
- }
- position -= op.itemCount;
- } else if (op.cmd == UpdateOp.ADD) {
- position += op.itemCount;
- }
- }
- }
- return position;
- }
-
- /**
- * @return True if updates should be processed.
- */
- boolean onItemRangeChanged(int positionStart, int itemCount) {
- mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount));
- return mPendingUpdates.size() == 1;
- }
-
- /**
- * @return True if updates should be processed.
- */
- boolean onItemRangeInserted(int positionStart, int itemCount) {
- mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount));
- return mPendingUpdates.size() == 1;
- }
-
- /**
- * @return True if updates should be processed.
- */
- boolean onItemRangeRemoved(int positionStart, int itemCount) {
- mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount));
- return mPendingUpdates.size() == 1;
- }
-
- /**
- * @return True if updates should be processed.
- */
- boolean onItemRangeMoved(int from, int to, int itemCount) {
- if (from == to) {
- return false;//no-op
- }
- if (itemCount != 1) {
- throw new IllegalArgumentException("Moving more than 1 item is not supported yet");
- }
- mPendingUpdates.add(obtainUpdateOp(UpdateOp.MOVE, from, to));
- return mPendingUpdates.size() == 1;
- }
-
- /**
- * Skips pre-processing and applies all updates in one pass.
- */
- void consumeUpdatesInOnePass() {
- // we still consume postponed updates (if there is) in case there was a pre-process call
- // w/o a matching consumePostponedUpdates.
- consumePostponedUpdates();
- final int count = mPendingUpdates.size();
- for (int i = 0; i < count; i++) {
- UpdateOp op = mPendingUpdates.get(i);
- switch (op.cmd) {
- case UpdateOp.ADD:
- mCallback.onDispatchSecondPass(op);
- mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
- break;
- case UpdateOp.REMOVE:
- mCallback.onDispatchSecondPass(op);
- mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount);
- break;
- case UpdateOp.UPDATE:
- mCallback.onDispatchSecondPass(op);
- mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount);
- break;
- case UpdateOp.MOVE:
- mCallback.onDispatchSecondPass(op);
- mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
- break;
- }
- if (mOnItemProcessedCallback != null) {
- mOnItemProcessedCallback.run();
- }
- }
- recycleUpdateOpsAndClearList(mPendingUpdates);
- }
-
- /**
- * Queued operation to happen when child views are updated.
- */
- static class UpdateOp {
-
- static final int ADD = 0;
-
- static final int REMOVE = 1;
-
- static final int UPDATE = 2;
-
- static final int MOVE = 3;
-
- static final int POOL_SIZE = 30;
-
- int cmd;
-
- int positionStart;
-
- // holds the target position if this is a MOVE
- int itemCount;
-
- UpdateOp(int cmd, int positionStart, int itemCount) {
- this.cmd = cmd;
- this.positionStart = positionStart;
- this.itemCount = itemCount;
- }
-
- String cmdToString() {
- switch (cmd) {
- case ADD:
- return "add";
- case REMOVE:
- return "rm";
- case UPDATE:
- return "up";
- case MOVE:
- return "mv";
- }
- return "??";
- }
-
- @Override
- public String toString() {
- return "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount + "]";
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- UpdateOp op = (UpdateOp) o;
-
- if (cmd != op.cmd) {
- return false;
- }
- if (cmd == MOVE && Math.abs(itemCount - positionStart) == 1) {
- // reverse of this is also true
- if (itemCount == op.positionStart && positionStart == op.itemCount) {
- return true;
- }
- }
- if (itemCount != op.itemCount) {
- return false;
- }
- if (positionStart != op.positionStart) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = cmd;
- result = 31 * result + positionStart;
- result = 31 * result + itemCount;
- return result;
- }
- }
-
- @Override
- public UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount) {
- UpdateOp op = mUpdateOpPool.acquire();
- if (op == null) {
- op = new UpdateOp(cmd, positionStart, itemCount);
- } else {
- op.cmd = cmd;
- op.positionStart = positionStart;
- op.itemCount = itemCount;
- }
- return op;
- }
-
- @Override
- public void recycleUpdateOp(UpdateOp op) {
- if (!mDisableRecycler) {
- mUpdateOpPool.release(op);
- }
- }
-
- void recycleUpdateOpsAndClearList(List ops) {
- final int count = ops.size();
- for (int i = 0; i < count; i++) {
- recycleUpdateOp(ops.get(i));
- }
- ops.clear();
- }
-
- /**
- * Contract between AdapterHelper and RecyclerView.
- */
- static interface Callback {
-
- ViewHolder findViewHolder(int position);
-
- void offsetPositionsForRemovingInvisible(int positionStart, int itemCount);
-
- void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount);
-
- void markViewHoldersUpdated(int positionStart, int itemCount);
-
- void onDispatchFirstPass(UpdateOp updateOp);
-
- void onDispatchSecondPass(UpdateOp updateOp);
-
- void offsetPositionsForAdd(int positionStart, int itemCount);
-
- void offsetPositionsForMove(int from, int to);
- }
-}
diff --git a/app/src/main/java/android/support/v7/widget/ChildHelper.java b/app/src/main/java/android/support/v7/widget/ChildHelper.java
deleted file mode 100644
index e5556796f1..0000000000
--- a/app/src/main/java/android/support/v7/widget/ChildHelper.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-package android.support.v7.widget;
-
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Helper class to manage children.
- *
- * It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods
- * provided by this class. Regular methods are the ones that replicate ViewGroup methods
- * like getChildAt, getChildCount etc. These methods ignore hidden children.
- *
- * When RecyclerView needs direct access to the view group children, it can call unfiltered
- * methods like get getUnfilteredChildCount or getUnfilteredChildAt.
- */
-class ChildHelper {
-
- private static final boolean DEBUG = false;
-
- private static final String TAG = "ChildrenHelper";
-
- final Callback mCallback;
-
- final Bucket mBucket;
-
- final List mHiddenViews;
-
- ChildHelper(Callback callback) {
- mCallback = callback;
- mBucket = new Bucket();
- mHiddenViews = new ArrayList();
- }
-
- /**
- * Adds a view to the ViewGroup
- *
- * @param child View to add.
- * @param hidden If set to true, this item will be invisible from regular methods.
- */
- void addView(View child, boolean hidden) {
- addView(child, -1, hidden);
- }
-
- /**
- * Add a view to the ViewGroup at an index
- *
- * @param child View to add.
- * @param index Index of the child from the regular perspective (excluding hidden views).
- * ChildHelper offsets this index to actual ViewGroup index.
- * @param hidden If set to true, this item will be invisible from regular methods.
- */
- void addView(View child, int index, boolean hidden) {
- final int offset;
- if (index < 0) {
- offset = mCallback.getChildCount();
- } else {
- offset = getOffset(index);
- }
- mCallback.addView(child, offset);
- mBucket.insert(offset, hidden);
- if (hidden) {
- mHiddenViews.add(child);
- }
- if (DEBUG) {
- Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this);
- }
- }
-
- private int getOffset(int index) {
- if (index < 0) {
- return -1; //anything below 0 won't work as diff will be undefined.
- }
- final int limit = mCallback.getChildCount();
- int offset = index;
- while (offset < limit) {
- final int removedBefore = mBucket.countOnesBefore(offset);
- final int diff = index - (offset - removedBefore);
- if (diff == 0) {
- while (mBucket.get(offset)) { // ensure this offset is not hidden
- offset ++;
- }
- return offset;
- } else {
- offset += diff;
- }
- }
- return -1;
- }
-
- /**
- * Removes the provided View from underlying RecyclerView.
- *
- * @param view The view to remove.
- */
- void removeView(View view) {
- int index = mCallback.indexOfChild(view);
- if (index < 0) {
- return;
- }
- mCallback.removeViewAt(index);
- if (mBucket.remove(index)) {
- mHiddenViews.remove(view);
- }
- if (DEBUG) {
- Log.d(TAG, "remove View off:" + index + "," + this);
- }
- }
-
- /**
- * Removes the view at the provided index from RecyclerView.
- *
- * @param index Index of the child from the regular perspective (excluding hidden views).
- * ChildHelper offsets this index to actual ViewGroup index.
- */
- void removeViewAt(int index) {
- final int offset = getOffset(index);
- final View view = mCallback.getChildAt(offset);
- if (view == null) {
- return;
- }
- mCallback.removeViewAt(offset);
- if (mBucket.remove(offset)) {
- mHiddenViews.remove(view);
- }
- if (DEBUG) {
- Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this);
- }
- }
-
- /**
- * Returns the child at provided index.
- *
- * @param index Index of the child to return in regular perspective.
- */
- View getChildAt(int index) {
- final int offset = getOffset(index);
- return mCallback.getChildAt(offset);
- }
-
- /**
- * Removes all views from the ViewGroup including the hidden ones.
- */
- void removeAllViewsUnfiltered() {
- mCallback.removeAllViews();
- mBucket.reset();
- mHiddenViews.clear();
- if (DEBUG) {
- Log.d(TAG, "removeAllViewsUnfiltered");
- }
- }
-
- /**
- * This can be used to find a disappearing view by position.
- *
- * @param position The adapter position of the item.
- * @param type View type, can be {@link RecyclerView#INVALID_TYPE}.
- * @return A hidden view with a valid ViewHolder that matches the position and type.
- */
- View findHiddenNonRemovedView(int position, int type) {
- final int count = mHiddenViews.size();
- for (int i = 0; i < count; i++) {
- final View view = mHiddenViews.get(i);
- RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view);
- if (holder.getPosition() == position && !holder.isInvalid() &&
- (type == RecyclerView.INVALID_TYPE || holder.getItemViewType() == type)) {
- return view;
- }
- }
- return null;
- }
-
- /**
- * Attaches the provided view to the underlying ViewGroup.
- *
- * @param child Child to attach.
- * @param index Index of the child to attach in regular perspective.
- * @param layoutParams LayoutParams for the child.
- * @param hidden If set to true, this item will be invisible to the regular methods.
- */
- void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams,
- boolean hidden) {
- final int offset;
- if (index < 0) {
- offset = mCallback.getChildCount();
- } else {
- offset = getOffset(index);
- }
- mCallback.attachViewToParent(child, offset, layoutParams);
- mBucket.insert(offset, hidden);
- if (DEBUG) {
- Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + "," +
- "h:" + hidden + ", " + this);
- }
- }
-
- /**
- * Returns the number of children that are not hidden.
- *
- * @return Number of children that are not hidden.
- * @see #getChildAt(int)
- */
- int getChildCount() {
- return mCallback.getChildCount() - mHiddenViews.size();
- }
-
- /**
- * Returns the total number of children.
- *
- * @return The total number of children including the hidden views.
- * @see #getUnfilteredChildAt(int)
- */
- int getUnfilteredChildCount() {
- return mCallback.getChildCount();
- }
-
- /**
- * Returns a child by ViewGroup offset. ChildHelper won't offset this index.
- *
- * @param index ViewGroup index of the child to return.
- * @return The view in the provided index.
- */
- View getUnfilteredChildAt(int index) {
- return mCallback.getChildAt(index);
- }
-
- /**
- * Detaches the view at the provided index.
- *
- * @param index Index of the child to return in regular perspective.
- */
- void detachViewFromParent(int index) {
- final int offset = getOffset(index);
- mCallback.detachViewFromParent(offset);
- mBucket.remove(offset);
- if (DEBUG) {
- Log.d(TAG, "detach view from parent " + index + ", off:" + offset);
- }
- }
-
- /**
- * Returns the index of the child in regular perspective.
- *
- * @param child The child whose index will be returned.
- * @return The regular perspective index of the child or -1 if it does not exists.
- */
- int indexOfChild(View child) {
- final int index = mCallback.indexOfChild(child);
- if (index == -1) {
- return -1;
- }
- if (mBucket.get(index)) {
- if (DEBUG) {
- throw new IllegalArgumentException("cannot get index of a hidden child");
- } else {
- return -1;
- }
- }
- // reverse the index
- return index - mBucket.countOnesBefore(index);
- }
-
- /**
- * Returns whether a View is visible to LayoutManager or not.
- *
- * @param view The child view to check. Should be a child of the Callback.
- * @return True if the View is not visible to LayoutManager
- */
- boolean isHidden(View view) {
- return mHiddenViews.contains(view);
- }
-
- /**
- * Marks a child view as hidden.
- *
- * @param view The view to hide.
- */
- void hide(View view) {
- final int offset = mCallback.indexOfChild(view);
- if (offset < 0) {
- throw new IllegalArgumentException("view is not a child, cannot hide " + view);
- }
- if (DEBUG && mBucket.get(offset)) {
- throw new RuntimeException("trying to hide same view twice, how come ? " + view);
- }
- mBucket.set(offset);
- mHiddenViews.add(view);
- if (DEBUG) {
- Log.d(TAG, "hiding child " + view + " at offset " + offset+ ", " + this);
- }
- }
-
- @Override
- public String toString() {
- return mBucket.toString();
- }
-
- /**
- * Removes a view from the ViewGroup if it is hidden.
- *
- * @param view The view to remove.
- * @return True if the View is found and it is hidden. False otherwise.
- */
- boolean removeViewIfHidden(View view) {
- final int index = mCallback.indexOfChild(view);
- if (index == -1) {
- if (mHiddenViews.remove(view) && DEBUG) {
- throw new IllegalStateException("view is in hidden list but not in view group");
- }
- return true;
- }
- if (mBucket.get(index)) {
- mBucket.remove(index);
- mCallback.removeViewAt(index);
- if (!mHiddenViews.remove(view) && DEBUG) {
- throw new IllegalStateException(
- "removed a hidden view but it is not in hidden views list");
- }
- return true;
- }
- return false;
- }
-
- /**
- * Bitset implementation that provides methods to offset indices.
- */
- static class Bucket {
-
- final static int BITS_PER_WORD = Long.SIZE;
-
- final static long LAST_BIT = 1L << (Long.SIZE - 1);
-
- long mData = 0;
-
- Bucket next;
-
- void set(int index) {
- if (index >= BITS_PER_WORD) {
- ensureNext();
- next.set(index - BITS_PER_WORD);
- } else {
- mData |= 1L << index;
- }
- }
-
- private void ensureNext() {
- if (next == null) {
- next = new Bucket();
- }
- }
-
- void clear(int index) {
- if (index >= BITS_PER_WORD) {
- if (next != null) {
- next.clear(index - BITS_PER_WORD);
- }
- } else {
- mData &= ~(1L << index);
- }
-
- }
-
- boolean get(int index) {
- if (index >= BITS_PER_WORD) {
- ensureNext();
- return next.get(index - BITS_PER_WORD);
- } else {
- return (mData & (1L << index)) != 0;
- }
- }
-
- void reset() {
- mData = 0;
- if (next != null) {
- next.reset();
- }
- }
-
- void insert(int index, boolean value) {
- if (index >= BITS_PER_WORD) {
- ensureNext();
- next.insert(index - BITS_PER_WORD, value);
- } else {
- final boolean lastBit = (mData & LAST_BIT) != 0;
- long mask = (1L << index) - 1;
- final long before = mData & mask;
- final long after = ((mData & ~mask)) << 1;
- mData = before | after;
- if (value) {
- set(index);
- } else {
- clear(index);
- }
- if (lastBit || next != null) {
- ensureNext();
- next.insert(0, lastBit);
- }
- }
- }
-
- boolean remove(int index) {
- if (index >= BITS_PER_WORD) {
- ensureNext();
- return next.remove(index - BITS_PER_WORD);
- } else {
- long mask = (1L << index);
- final boolean value = (mData & mask) != 0;
- mData &= ~mask;
- mask = mask - 1;
- final long before = mData & mask;
- // cannot use >> because it adds one.
- final long after = Long.rotateRight(mData & ~mask, 1);
- mData = before | after;
- if (next != null) {
- if (next.get(0)) {
- set(BITS_PER_WORD - 1);
- }
- next.remove(0);
- }
- return value;
- }
- }
-
- int countOnesBefore(int index) {
- if (next == null) {
- if (index >= BITS_PER_WORD) {
- return Long.bitCount(mData);
- }
- return Long.bitCount(mData & ((1L << index) - 1));
- }
- if (index < BITS_PER_WORD) {
- return Long.bitCount(mData & ((1L << index) - 1));
- } else {
- return next.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
- }
- }
-
- @Override
- public String toString() {
- return next == null ? Long.toBinaryString(mData)
- : next.toString() + "xx" + Long.toBinaryString(mData);
- }
- }
-
- static interface Callback {
-
- int getChildCount();
-
- void addView(View child, int index);
-
- int indexOfChild(View view);
-
- void removeViewAt(int index);
-
- View getChildAt(int offset);
-
- void removeAllViews();
-
- RecyclerView.ViewHolder getChildViewHolder(View view);
-
- void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams);
-
- void detachViewFromParent(int offset);
- }
-}
diff --git a/app/src/main/java/android/support/v7/widget/DefaultItemAnimator.java b/app/src/main/java/android/support/v7/widget/DefaultItemAnimator.java
deleted file mode 100644
index 2a27d65ab8..0000000000
--- a/app/src/main/java/android/support/v7/widget/DefaultItemAnimator.java
+++ /dev/null
@@ -1,628 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-package android.support.v7.widget;
-
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
-import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This implementation of {@link RecyclerView.ItemAnimator} provides basic
- * animations on remove, add, and move events that happen to the items in
- * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.
- *
- * @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)
- */
-public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
- private static final boolean DEBUG = false;
-
- private ArrayList mPendingRemovals = new ArrayList();
- private ArrayList mPendingAdditions = new ArrayList();
- private ArrayList mPendingMoves = new ArrayList();
- private ArrayList mPendingChanges = new ArrayList();
-
- private ArrayList> mAdditionsList =
- new ArrayList>();
- private ArrayList> mMovesList = new ArrayList>();
- private ArrayList> mChangesList = new ArrayList>();
-
- private ArrayList mAddAnimations = new ArrayList();
- private ArrayList mMoveAnimations = new ArrayList();
- private ArrayList mRemoveAnimations = new ArrayList();
- private ArrayList mChangeAnimations = new ArrayList();
-
- private static class MoveInfo {
- public ViewHolder holder;
- public int fromX, fromY, toX, toY;
-
- private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
- this.holder = holder;
- this.fromX = fromX;
- this.fromY = fromY;
- this.toX = toX;
- this.toY = toY;
- }
- }
-
- private static class ChangeInfo {
- public ViewHolder oldHolder, newHolder;
- public int fromX, fromY, toX, toY;
- private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
- this.oldHolder = oldHolder;
- this.newHolder = newHolder;
- }
-
- private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
- int fromX, int fromY, int toX, int toY) {
- this(oldHolder, newHolder);
- this.fromX = fromX;
- this.fromY = fromY;
- this.toX = toX;
- this.toY = toY;
- }
-
- @Override
- public String toString() {
- return "ChangeInfo{" +
- "oldHolder=" + oldHolder +
- ", newHolder=" + newHolder +
- ", fromX=" + fromX +
- ", fromY=" + fromY +
- ", toX=" + toX +
- ", toY=" + toY +
- '}';
- }
- }
-
- @Override
- public void runPendingAnimations() {
- boolean removalsPending = !mPendingRemovals.isEmpty();
- boolean movesPending = !mPendingMoves.isEmpty();
- boolean changesPending = !mPendingChanges.isEmpty();
- boolean additionsPending = !mPendingAdditions.isEmpty();
- if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
- // nothing to animate
- return;
- }
- // First, remove stuff
- for (ViewHolder holder : mPendingRemovals) {
- animateRemoveImpl(holder);
- }
- mPendingRemovals.clear();
- // Next, move stuff
- if (movesPending) {
- final ArrayList moves = new ArrayList();
- moves.addAll(mPendingMoves);
- mMovesList.add(moves);
- mPendingMoves.clear();
- Runnable mover = new Runnable() {
- @Override
- public void run() {
- for (MoveInfo moveInfo : moves) {
- animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
- moveInfo.toX, moveInfo.toY);
- }
- moves.clear();
- mMovesList.remove(moves);
- }
- };
- if (removalsPending) {
- View view = moves.get(0).holder.itemView;
- ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
- } else {
- mover.run();
- }
- }
- // Next, change stuff, to run in parallel with move animations
- if (changesPending) {
- final ArrayList changes = new ArrayList();
- changes.addAll(mPendingChanges);
- mChangesList.add(changes);
- mPendingChanges.clear();
- Runnable changer = new Runnable() {
- @Override
- public void run() {
- for (ChangeInfo change : changes) {
- animateChangeImpl(change);
- }
- changes.clear();
- mChangesList.remove(changes);
- }
- };
- if (removalsPending) {
- ViewHolder holder = changes.get(0).oldHolder;
- ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
- } else {
- changer.run();
- }
- }
- // Next, add stuff
- if (additionsPending) {
- final ArrayList additions = new ArrayList();
- additions.addAll(mPendingAdditions);
- mAdditionsList.add(additions);
- mPendingAdditions.clear();
- Runnable adder = new Runnable() {
- public void run() {
- for (ViewHolder holder : additions) {
- animateAddImpl(holder);
- }
- additions.clear();
- mAdditionsList.remove(additions);
- }
- };
- if (removalsPending || movesPending || changesPending) {
- long removeDuration = removalsPending ? getRemoveDuration() : 0;
- long moveDuration = movesPending ? getMoveDuration() : 0;
- long changeDuration = changesPending ? getChangeDuration() : 0;
- long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
- View view = additions.get(0).itemView;
- ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
- } else {
- adder.run();
- }
- }
- }
-
- @Override
- public boolean animateRemove(final ViewHolder holder) {
- endAnimation(holder);
- mPendingRemovals.add(holder);
- return true;
- }
-
- private void animateRemoveImpl(final ViewHolder holder) {
- final View view = holder.itemView;
- final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
- animation.setDuration(getRemoveDuration())
- .alpha(0).setListener(new VpaListenerAdapter() {
- @Override
- public void onAnimationStart(View view) {
- dispatchRemoveStarting(holder);
- }
- @Override
- public void onAnimationEnd(View view) {
- animation.setListener(null);
- ViewCompat.setAlpha(view, 1);
- dispatchRemoveFinished(holder);
- mRemoveAnimations.remove(holder);
- dispatchFinishedWhenDone();
- }
- }).start();
- mRemoveAnimations.add(holder);
- }
-
- @Override
- public boolean animateAdd(final ViewHolder holder) {
- endAnimation(holder);
- ViewCompat.setAlpha(holder.itemView, 0);
- mPendingAdditions.add(holder);
- return true;
- }
-
- private void animateAddImpl(final ViewHolder holder) {
- final View view = holder.itemView;
- mAddAnimations.add(holder);
- final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
- animation.alpha(1).setDuration(getAddDuration()).
- setListener(new VpaListenerAdapter() {
- @Override
- public void onAnimationStart(View view) {
- dispatchAddStarting(holder);
- }
- @Override
- public void onAnimationCancel(View view) {
- ViewCompat.setAlpha(view, 1);
- }
-
- @Override
- public void onAnimationEnd(View view) {
- animation.setListener(null);
- dispatchAddFinished(holder);
- mAddAnimations.remove(holder);
- dispatchFinishedWhenDone();
- }
- }).start();
- }
-
- @Override
- public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
- int toX, int toY) {
- final View view = holder.itemView;
- fromX += ViewCompat.getTranslationX(holder.itemView);
- fromY += ViewCompat.getTranslationY(holder.itemView);
- endAnimation(holder);
- int deltaX = toX - fromX;
- int deltaY = toY - fromY;
- if (deltaX == 0 && deltaY == 0) {
- dispatchMoveFinished(holder);
- return false;
- }
- if (deltaX != 0) {
- ViewCompat.setTranslationX(view, -deltaX);
- }
- if (deltaY != 0) {
- ViewCompat.setTranslationY(view, -deltaY);
- }
- mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
- return true;
- }
-
- private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
- final View view = holder.itemView;
- final int deltaX = toX - fromX;
- final int deltaY = toY - fromY;
- if (deltaX != 0) {
- ViewCompat.animate(view).translationX(0);
- }
- if (deltaY != 0) {
- ViewCompat.animate(view).translationY(0);
- }
- // TODO: make EndActions end listeners instead, since end actions aren't called when
- // vpas are canceled (and can't end them. why?)
- // need listener functionality in VPACompat for this. Ick.
- mMoveAnimations.add(holder);
- final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
- animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
- @Override
- public void onAnimationStart(View view) {
- dispatchMoveStarting(holder);
- }
- @Override
- public void onAnimationCancel(View view) {
- if (deltaX != 0) {
- ViewCompat.setTranslationX(view, 0);
- }
- if (deltaY != 0) {
- ViewCompat.setTranslationY(view, 0);
- }
- }
- @Override
- public void onAnimationEnd(View view) {
- animation.setListener(null);
- dispatchMoveFinished(holder);
- mMoveAnimations.remove(holder);
- dispatchFinishedWhenDone();
- }
- }).start();
- }
-
- @Override
- public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
- int fromX, int fromY, int toX, int toY) {
- final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
- final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
- final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
- endAnimation(oldHolder);
- int deltaX = (int) (toX - fromX - prevTranslationX);
- int deltaY = (int) (toY - fromY - prevTranslationY);
- // recover prev translation state after ending animation
- ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
- ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
- ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
- if (newHolder != null && newHolder.itemView != null) {
- // carry over translation values
- endAnimation(newHolder);
- ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
- ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
- ViewCompat.setAlpha(newHolder.itemView, 0);
- }
- mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
- return true;
- }
-
- private void animateChangeImpl(final ChangeInfo changeInfo) {
- final ViewHolder holder = changeInfo.oldHolder;
- final View view = holder.itemView;
- final ViewHolder newHolder = changeInfo.newHolder;
- final View newView = newHolder != null ? newHolder.itemView : null;
- mChangeAnimations.add(changeInfo.oldHolder);
-
- final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
- getChangeDuration());
- oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
- oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
- oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
- @Override
- public void onAnimationStart(View view) {
- dispatchChangeStarting(changeInfo.oldHolder, true);
- }
- @Override
- public void onAnimationEnd(View view) {
- oldViewAnim.setListener(null);
- ViewCompat.setAlpha(view, 1);
- ViewCompat.setTranslationX(view, 0);
- ViewCompat.setTranslationY(view, 0);
- dispatchChangeFinished(changeInfo.oldHolder, true);
- mChangeAnimations.remove(changeInfo.oldHolder);
- dispatchFinishedWhenDone();
- }
- }).start();
- if (newView != null) {
- mChangeAnimations.add(changeInfo.newHolder);
- final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
- newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).
- alpha(1).setListener(new VpaListenerAdapter() {
- @Override
- public void onAnimationStart(View view) {
- dispatchChangeStarting(changeInfo.newHolder, false);
- }
- @Override
- public void onAnimationEnd(View view) {
- newViewAnimation.setListener(null);
- ViewCompat.setAlpha(newView, 1);
- ViewCompat.setTranslationX(newView, 0);
- ViewCompat.setTranslationY(newView, 0);
- dispatchChangeFinished(changeInfo.newHolder, false);
- mChangeAnimations.remove(changeInfo.newHolder);
- dispatchFinishedWhenDone();
- }
- }).start();
- }
- }
-
- private void endChangeAnimation(List infoList, ViewHolder item) {
- for (int i = infoList.size() - 1; i >= 0; i--) {
- ChangeInfo changeInfo = infoList.get(i);
- if (endChangeAnimationIfNecessary(changeInfo, item)) {
- if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
- infoList.remove(changeInfo);
- }
- }
- }
- }
-
- private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
- if (changeInfo.oldHolder != null) {
- endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
- }
- if (changeInfo.newHolder != null) {
- endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
- }
- }
- private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
- boolean oldItem = false;
- if (changeInfo.newHolder == item) {
- changeInfo.newHolder = null;
- } else if (changeInfo.oldHolder == item) {
- changeInfo.oldHolder = null;
- oldItem = true;
- } else {
- return false;
- }
- ViewCompat.setAlpha(item.itemView, 1);
- ViewCompat.setTranslationX(item.itemView, 0);
- ViewCompat.setTranslationY(item.itemView, 0);
- dispatchChangeFinished(item, oldItem);
- return true;
- }
-
- @Override
- public void endAnimation(ViewHolder item) {
- final View view = item.itemView;
- // this will trigger end callback which should set properties to their target values.
- ViewCompat.animate(view).cancel();
- // TODO if some other animations are chained to end, how do we cancel them as well?
- for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
- MoveInfo moveInfo = mPendingMoves.get(i);
- if (moveInfo.holder == item) {
- ViewCompat.setTranslationY(view, 0);
- ViewCompat.setTranslationX(view, 0);
- dispatchMoveFinished(item);
- mPendingMoves.remove(item);
- }
- }
- endChangeAnimation(mPendingChanges, item);
- if (mPendingRemovals.remove(item)) {
- ViewCompat.setAlpha(view, 1);
- dispatchRemoveFinished(item);
- }
- if (mPendingAdditions.remove(item)) {
- ViewCompat.setAlpha(view, 1);
- dispatchAddFinished(item);
- }
-
- for (int i = mChangesList.size() - 1; i >= 0; i--) {
- ArrayList changes = mChangesList.get(i);
- endChangeAnimation(changes, item);
- if (changes.isEmpty()) {
- mChangesList.remove(changes);
- }
- }
- for (int i = mMovesList.size() - 1; i >= 0; i--) {
- ArrayList moves = mMovesList.get(i);
- for (int j = moves.size() - 1; j >= 0; j--) {
- MoveInfo moveInfo = moves.get(j);
- if (moveInfo.holder == item) {
- ViewCompat.setTranslationY(view, 0);
- ViewCompat.setTranslationX(view, 0);
- dispatchMoveFinished(item);
- moves.remove(j);
- if (moves.isEmpty()) {
- mMovesList.remove(moves);
- }
- break;
- }
- }
- }
- for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
- ArrayList additions = mAdditionsList.get(i);
- if (additions.remove(item)) {
- ViewCompat.setAlpha(view, 1);
- dispatchAddFinished(item);
- if (additions.isEmpty()) {
- mAdditionsList.remove(additions);
- }
- }
- }
-
- // animations should be ended by the cancel above.
- if (mRemoveAnimations.remove(item) && DEBUG) {
- throw new IllegalStateException("after animation is cancelled, item should not be in "
- + "mRemoveAnimations list");
- }
-
- if (mAddAnimations.remove(item) && DEBUG) {
- throw new IllegalStateException("after animation is cancelled, item should not be in "
- + "mAddAnimations list");
- }
-
- if (mChangeAnimations.remove(item) && DEBUG) {
- throw new IllegalStateException("after animation is cancelled, item should not be in "
- + "mChangeAnimations list");
- }
-
- if (mMoveAnimations.remove(item) && DEBUG) {
- throw new IllegalStateException("after animation is cancelled, item should not be in "
- + "mMoveAnimations list");
- }
- dispatchFinishedWhenDone();
- }
-
- @Override
- public boolean isRunning() {
- return (!mPendingAdditions.isEmpty() ||
- !mPendingChanges.isEmpty() ||
- !mPendingMoves.isEmpty() ||
- !mPendingRemovals.isEmpty() ||
- !mMoveAnimations.isEmpty() ||
- !mRemoveAnimations.isEmpty() ||
- !mAddAnimations.isEmpty() ||
- !mChangeAnimations.isEmpty() ||
- !mMovesList.isEmpty() ||
- !mAdditionsList.isEmpty() ||
- !mChangesList.isEmpty());
- }
-
- /**
- * Check the state of currently pending and running animations. If there are none
- * pending/running, call {@link #dispatchAnimationsFinished()} to notify any
- * listeners.
- */
- private void dispatchFinishedWhenDone() {
- if (!isRunning()) {
- dispatchAnimationsFinished();
- }
- }
-
- @Override
- public void endAnimations() {
- int count = mPendingMoves.size();
- for (int i = count - 1; i >= 0; i--) {
- MoveInfo item = mPendingMoves.get(i);
- View view = item.holder.itemView;
- ViewCompat.setTranslationY(view, 0);
- ViewCompat.setTranslationX(view, 0);
- dispatchMoveFinished(item.holder);
- mPendingMoves.remove(i);
- }
- count = mPendingRemovals.size();
- for (int i = count - 1; i >= 0; i--) {
- ViewHolder item = mPendingRemovals.get(i);
- dispatchRemoveFinished(item);
- mPendingRemovals.remove(i);
- }
- count = mPendingAdditions.size();
- for (int i = count - 1; i >= 0; i--) {
- ViewHolder item = mPendingAdditions.get(i);
- View view = item.itemView;
- ViewCompat.setAlpha(view, 1);
- dispatchAddFinished(item);
- mPendingAdditions.remove(i);
- }
- count = mPendingChanges.size();
- for (int i = count - 1; i >= 0; i--) {
- endChangeAnimationIfNecessary(mPendingChanges.get(i));
- }
- mPendingChanges.clear();
- if (!isRunning()) {
- return;
- }
-
- int listCount = mMovesList.size();
- for (int i = listCount - 1; i >= 0; i--) {
- ArrayList moves = mMovesList.get(i);
- count = moves.size();
- for (int j = count - 1; j >= 0; j--) {
- MoveInfo moveInfo = moves.get(j);
- ViewHolder item = moveInfo.holder;
- View view = item.itemView;
- ViewCompat.setTranslationY(view, 0);
- ViewCompat.setTranslationX(view, 0);
- dispatchMoveFinished(moveInfo.holder);
- moves.remove(j);
- if (moves.isEmpty()) {
- mMovesList.remove(moves);
- }
- }
- }
- listCount = mAdditionsList.size();
- for (int i = listCount - 1; i >= 0; i--) {
- ArrayList additions = mAdditionsList.get(i);
- count = additions.size();
- for (int j = count - 1; j >= 0; j--) {
- ViewHolder item = additions.get(j);
- View view = item.itemView;
- ViewCompat.setAlpha(view, 1);
- dispatchAddFinished(item);
- additions.remove(j);
- if (additions.isEmpty()) {
- mAdditionsList.remove(additions);
- }
- }
- }
- listCount = mChangesList.size();
- for (int i = listCount - 1; i >= 0; i--) {
- ArrayList changes = mChangesList.get(i);
- count = changes.size();
- for (int j = count - 1; j >= 0; j--) {
- endChangeAnimationIfNecessary(changes.get(j));
- if (changes.isEmpty()) {
- mChangesList.remove(changes);
- }
- }
- }
-
- cancelAll(mRemoveAnimations);
- cancelAll(mMoveAnimations);
- cancelAll(mAddAnimations);
- cancelAll(mChangeAnimations);
-
- dispatchAnimationsFinished();
- }
-
- void cancelAll(List viewHolders) {
- for (int i = viewHolders.size() - 1; i >= 0; i--) {
- ViewCompat.animate(viewHolders.get(i).itemView).cancel();
- }
- }
-
- private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
- @Override
- public void onAnimationStart(View view) {}
-
- @Override
- public void onAnimationEnd(View view) {}
-
- @Override
- public void onAnimationCancel(View view) {}
- };
-}
diff --git a/app/src/main/java/android/support/v7/widget/GridLayoutManager.java b/app/src/main/java/android/support/v7/widget/GridLayoutManager.java
deleted file mode 100644
index a05ac473e4..0000000000
--- a/app/src/main/java/android/support/v7/widget/GridLayoutManager.java
+++ /dev/null
@@ -1,816 +0,0 @@
-/*
- * Copyright (C) 2014 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 languag`e governing permissions and
- * limitations under the License.
- */
-package android.support.v7.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseIntArray;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.Arrays;
-
-/**
- * A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid.
- *
- * By default, each item occupies 1 span. You can change it by providing a custom
- * {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}.
- */
-public class GridLayoutManager extends LinearLayoutManager {
-
- private static final boolean DEBUG = false;
- private static final String TAG = "GridLayoutManager";
- public static final int DEFAULT_SPAN_COUNT = -1;
- /**
- * The measure spec for the scroll direction.
- */
- static final int MAIN_DIR_SPEC =
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
-
- int mSpanCount = DEFAULT_SPAN_COUNT;
- /**
- * The size of each span
- */
- int mSizePerSpan;
- /**
- * Temporary array to keep views in layoutChunk method
- */
- View[] mSet;
- final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray();
- final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray();
- SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup();
- // re-used variable to acquire decor insets from RecyclerView
- final Rect mDecorInsets = new Rect();
-
- /**
- * Creates a vertical GridLayoutManager
- *
- * @param context Current context, will be used to access resources.
- * @param spanCount The number of columns in the grid
- */
- public GridLayoutManager(Context context, int spanCount) {
- super(context);
- setSpanCount(spanCount);
- }
-
- /**
- * @param context Current context, will be used to access resources.
- * @param spanCount The number of columns or rows in the grid
- * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
- * #VERTICAL}.
- * @param reverseLayout When set to true, layouts from end to start.
- */
- public GridLayoutManager(Context context, int spanCount, int orientation,
- boolean reverseLayout) {
- super(context, orientation, reverseLayout);
- setSpanCount(spanCount);
- }
-
- /**
- * stackFromEnd is not supported by GridLayoutManager. Consider using
- * {@link #setReverseLayout(boolean)}.
- */
- @Override
- public void setStackFromEnd(boolean stackFromEnd) {
- if (stackFromEnd) {
- throw new UnsupportedOperationException(
- "GridLayoutManager does not support stack from end."
- + " Consider using reverse layout");
- }
- super.setStackFromEnd(false);
- }
-
- @Override
- public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
- RecyclerView.State state) {
- if (mOrientation == HORIZONTAL) {
- return mSpanCount;
- }
- if (state.getItemCount() < 1) {
- return 0;
- }
- return getSpanGroupIndex(recycler, state, state.getItemCount() - 1);
- }
-
- @Override
- public int getColumnCountForAccessibility(RecyclerView.Recycler recycler,
- RecyclerView.State state) {
- if (mOrientation == VERTICAL) {
- return mSpanCount;
- }
- if (state.getItemCount() < 1) {
- return 0;
- }
- return getSpanGroupIndex(recycler, state, state.getItemCount() - 1);
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
- RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
- ViewGroup.LayoutParams lp = host.getLayoutParams();
- if (!(lp instanceof LayoutParams)) {
- super.onInitializeAccessibilityNodeInfoForItem(host, info);
- return;
- }
- LayoutParams glp = (LayoutParams) lp;
- int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewPosition());
- if (mOrientation == HORIZONTAL) {
- info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
- glp.getSpanIndex(), glp.getSpanSize(),
- spanGroupIndex, 1,
- mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
- } else { // VERTICAL
- info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
- spanGroupIndex , 1,
- glp.getSpanIndex(), glp.getSpanSize(),
- mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
- }
- }
-
- @Override
- public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
- if (state.isPreLayout()) {
- cachePreLayoutSpanMapping();
- }
- super.onLayoutChildren(recycler, state);
- if (DEBUG) {
- validateChildOrder();
- }
- clearPreLayoutSpanMappingCache();
- }
-
- private void clearPreLayoutSpanMappingCache() {
- mPreLayoutSpanSizeCache.clear();
- mPreLayoutSpanIndexCache.clear();
- }
-
- private void cachePreLayoutSpanMapping() {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
- final int viewPosition = lp.getViewPosition();
- mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize());
- mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex());
- }
- }
-
- @Override
- public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
- mSpanSizeLookup.invalidateSpanIndexCache();
- }
-
- @Override
- public void onItemsChanged(RecyclerView recyclerView) {
- mSpanSizeLookup.invalidateSpanIndexCache();
- }
-
- @Override
- public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
- mSpanSizeLookup.invalidateSpanIndexCache();
- }
-
- @Override
- public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
- mSpanSizeLookup.invalidateSpanIndexCache();
- }
-
- @Override
- public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
- mSpanSizeLookup.invalidateSpanIndexCache();
- }
-
- @Override
- public RecyclerView.LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- }
-
- @Override
- public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
- return new LayoutParams(c, attrs);
- }
-
- @Override
- public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
- if (lp instanceof ViewGroup.MarginLayoutParams) {
- return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
- } else {
- return new LayoutParams(lp);
- }
- }
-
- @Override
- public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
- return lp instanceof LayoutParams;
- }
-
- /**
- * Sets the source to get the number of spans occupied by each item in the adapter.
- *
- * @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans
- * occupied by each item
- */
- public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
- mSpanSizeLookup = spanSizeLookup;
- }
-
- /**
- * Returns the current {@link SpanSizeLookup} used by the GridLayoutManager.
- *
- * @return The current {@link SpanSizeLookup} used by the GridLayoutManager.
- */
- public SpanSizeLookup getSpanSizeLookup() {
- return mSpanSizeLookup;
- }
-
- private void updateMeasurements() {
- int totalSpace;
- if (getOrientation() == VERTICAL) {
- totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
- } else {
- totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();
- }
- mSizePerSpan = totalSpace / mSpanCount;
- }
-
- @Override
- void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) {
- super.onAnchorReady(state, anchorInfo);
- updateMeasurements();
- if (state.getItemCount() > 0 && !state.isPreLayout()) {
- ensureAnchorIsInFirstSpan(anchorInfo);
- }
- if (mSet == null || mSet.length != mSpanCount) {
- mSet = new View[mSpanCount];
- }
- }
-
- private void ensureAnchorIsInFirstSpan(AnchorInfo anchorInfo) {
- int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
- while (span > 0 && anchorInfo.mPosition > 0) {
- anchorInfo.mPosition--;
- span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
- }
- }
-
- private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state,
- int viewPosition) {
- if (!state.isPreLayout()) {
- return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount);
- }
- final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition);
- if (adapterPosition == -1) {
- if (DEBUG) {
- throw new RuntimeException("Cannot find span group index for position "
- + viewPosition);
- }
- Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition);
- return 0;
- }
- return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount);
- }
-
- private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
- if (!state.isPreLayout()) {
- return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount);
- }
- final int cached = mPreLayoutSpanIndexCache.get(pos, -1);
- if (cached != -1) {
- return cached;
- }
- final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
- if (adapterPosition == -1) {
- if (DEBUG) {
- throw new RuntimeException("Cannot find span index for pre layout position. It is"
- + " not cached, not in the adapter. Pos:" + pos);
- }
- Log.w(TAG, "Cannot find span size for pre layout position. It is"
- + " not cached, not in the adapter. Pos:" + pos);
- return 0;
- }
- return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount);
- }
-
- private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
- if (!state.isPreLayout()) {
- return mSpanSizeLookup.getSpanSize(pos);
- }
- final int cached = mPreLayoutSpanSizeCache.get(pos, -1);
- if (cached != -1) {
- return cached;
- }
- final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
- if (adapterPosition == -1) {
- if (DEBUG) {
- throw new RuntimeException("Cannot find span size for pre layout position. It is"
- + " not cached, not in the adapter. Pos:" + pos);
- }
- Log.w(TAG, "Cannot find span size for pre layout position. It is"
- + " not cached, not in the adapter. Pos:" + pos);
- return 1;
- }
- return mSpanSizeLookup.getSpanSize(adapterPosition);
- }
-
- @Override
- void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
- LayoutState layoutState, LayoutChunkResult result) {
- final boolean layingOutInPrimaryDirection =
- layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL;
- int count = 0;
- int consumedSpanCount = 0;
- int remainingSpan = mSpanCount;
- if (!layingOutInPrimaryDirection) {
- int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition);
- int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition);
- remainingSpan = itemSpanIndex + itemSpanSize;
- }
- while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
- int pos = layoutState.mCurrentPosition;
- final int spanSize = getSpanSize(recycler, state, pos);
- if (spanSize > mSpanCount) {
- throw new IllegalArgumentException("Item at position " + pos + " requires " +
- spanSize + " spans but GridLayoutManager has only " + mSpanCount
- + " spans.");
- }
- remainingSpan -= spanSize;
- if (remainingSpan < 0) {
- break; // item did not fit into this row or column
- }
- View view = layoutState.next(recycler);
- if (view == null) {
- break;
- }
- consumedSpanCount += spanSize;
- mSet[count] = view;
- count++;
- }
-
- if (count == 0) {
- result.mFinished = true;
- return;
- }
-
- int maxSize = 0;
-
- // we should assign spans before item decor offsets are calculated
- assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection);
- for (int i = 0; i < count; i++) {
- View view = mSet[i];
- if (layoutState.mScrapList == null) {
- if (layingOutInPrimaryDirection) {
- addView(view);
- } else {
- addView(view, 0);
- }
- } else {
- if (layingOutInPrimaryDirection) {
- addDisappearingView(view);
- } else {
- addDisappearingView(view, 0);
- }
- }
-
- int spanSize = getSpanSize(recycler, state, getPosition(view));
- final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize,
- View.MeasureSpec.EXACTLY);
- final LayoutParams lp = (LayoutParams) view.getLayoutParams();
- if (mOrientation == VERTICAL) {
- measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height));
- } else {
- measureChildWithDecorationsAndMargin(view, getMainDirSpec(lp.width), spec);
- }
- final int size = mOrientationHelper.getDecoratedMeasurement(view);
- if (size > maxSize) {
- maxSize = size;
- }
- }
-
- // views that did not measure the maxSize has to be re-measured
- final int maxMeasureSpec = getMainDirSpec(maxSize);
- for (int i = 0; i < count; i ++) {
- final View view = mSet[i];
- if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
- int spanSize = getSpanSize(recycler, state, getPosition(view));
- final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize,
- View.MeasureSpec.EXACTLY);
- if (mOrientation == VERTICAL) {
- measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec);
- } else {
- measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec);
- }
- }
- }
-
- result.mConsumed = maxSize;
-
- int left = 0, right = 0, top = 0, bottom = 0;
- if (mOrientation == VERTICAL) {
- if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
- bottom = layoutState.mOffset;
- top = bottom - maxSize;
- } else {
- top = layoutState.mOffset;
- bottom = top + maxSize;
- }
- } else {
- if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
- right = layoutState.mOffset;
- left = right - maxSize;
- } else {
- left = layoutState.mOffset;
- right = left + maxSize;
- }
- }
- for (int i = 0; i < count; i++) {
- View view = mSet[i];
- LayoutParams params = (LayoutParams) view.getLayoutParams();
- if (mOrientation == VERTICAL) {
- left = getPaddingLeft() + mSizePerSpan * params.mSpanIndex;
- right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
- } else {
- top = getPaddingTop() + mSizePerSpan * params.mSpanIndex;
- bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
- }
- // We calculate everything with View's bounding box (which includes decor and margins)
- // To calculate correct layout position, we subtract margins.
- layoutDecorated(view, left + params.leftMargin, top + params.topMargin,
- right - params.rightMargin, bottom - params.bottomMargin);
- if (DEBUG) {
- Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
- + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
- + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)
- + ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize);
- }
- // Consume the available space if the view is not removed OR changed
- if (params.isItemRemoved() || params.isItemChanged()) {
- result.mIgnoreConsumed = true;
- }
- result.mFocusable |= view.isFocusable();
- }
- Arrays.fill(mSet, null);
- }
-
- private int getMainDirSpec(int dim) {
- if (dim < 0) {
- return MAIN_DIR_SPEC;
- } else {
- return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY);
- }
- }
-
- private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) {
- calculateItemDecorationsForChild(child, mDecorInsets);
- RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
- widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left,
- lp.rightMargin + mDecorInsets.right);
- heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top,
- lp.bottomMargin + mDecorInsets.bottom);
- child.measure(widthSpec, heightSpec);
- }
-
- private int updateSpecWithExtra(int spec, int startInset, int endInset) {
- if (startInset == 0 && endInset == 0) {
- return spec;
- }
- final int mode = View.MeasureSpec.getMode(spec);
- if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
- return View.MeasureSpec.makeMeasureSpec(
- View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
- }
- return spec;
- }
-
- private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count,
- int consumedSpanCount, boolean layingOutInPrimaryDirection) {
- int span, spanDiff, start, end, diff;
- // make sure we traverse from min position to max position
- if (layingOutInPrimaryDirection) {
- start = 0;
- end = count;
- diff = 1;
- } else {
- start = count - 1;
- end = -1;
- diff = -1;
- }
- if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span
- span = consumedSpanCount - 1;
- spanDiff = -1;
- } else {
- span = 0;
- spanDiff = 1;
- }
- for (int i = start; i != end; i += diff) {
- View view = mSet[i];
- LayoutParams params = (LayoutParams) view.getLayoutParams();
- params.mSpanSize = getSpanSize(recycler, state, getPosition(view));
- if (spanDiff == -1 && params.mSpanSize > 1) {
- params.mSpanIndex = span - (params.mSpanSize - 1);
- } else {
- params.mSpanIndex = span;
- }
- span += spanDiff * params.mSpanSize;
- }
- }
-
- /**
- * Returns the number of spans laid out by this grid.
- *
- * @return The number of spans
- * @see #setSpanCount(int)
- */
- public int getSpanCount() {
- return mSpanCount;
- }
-
- /**
- * Sets the number of spans to be laid out.
- *
- * If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns.
- * If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows.
- *
- * @param spanCount The total number of spans in the grid
- * @see #getSpanCount()
- */
- public void setSpanCount(int spanCount) {
- if (spanCount == mSpanCount) {
- return;
- }
- if (spanCount < 1) {
- throw new IllegalArgumentException("Span count should be at least 1. Provided "
- + spanCount);
- }
- mSpanCount = spanCount;
- mSpanSizeLookup.invalidateSpanIndexCache();
- }
-
- /**
- * A helper class to provide the number of spans each item occupies.
- *
- * Default implementation sets each item to occupy exactly 1 span.
- *
- * @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup)
- */
- public static abstract class SpanSizeLookup {
-
- final SparseIntArray mSpanIndexCache = new SparseIntArray();
-
- private boolean mCacheSpanIndices = false;
-
- /**
- * Returns the number of span occupied by the item at position.
- *
- * @param position The adapter position of the item
- * @return The number of spans occupied by the item at the provided position
- */
- abstract public int getSpanSize(int position);
-
- /**
- * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or
- * not. By default these values are not cached. If you are not overriding
- * {@link #getSpanIndex(int, int)}, you should set this to true for better performance.
- *
- * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not.
- */
- public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {
- mCacheSpanIndices = cacheSpanIndices;
- }
-
- /**
- * Clears the span index cache. GridLayoutManager automatically calls this method when
- * adapter changes occur.
- */
- public void invalidateSpanIndexCache() {
- mSpanIndexCache.clear();
- }
-
- /**
- * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not.
- *
- * @return True if results of {@link #getSpanIndex(int, int)} are cached.
- */
- public boolean isSpanIndexCacheEnabled() {
- return mCacheSpanIndices;
- }
-
- int getCachedSpanIndex(int position, int spanCount) {
- if (!mCacheSpanIndices) {
- return getSpanIndex(position, spanCount);
- }
- final int existing = mSpanIndexCache.get(position, -1);
- if (existing != -1) {
- return existing;
- }
- final int value = getSpanIndex(position, spanCount);
- mSpanIndexCache.put(position, value);
- return value;
- }
-
- /**
- * Returns the final span index of the provided position.
- *
- * If you have a faster way to calculate span index for your items, you should override
- * this method. Otherwise, you should enable span index cache
- * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is
- * disabled, default implementation traverses all items from 0 to
- * position. When caching is enabled, it calculates from the closest cached
- * value before the position.
- *
- * If you override this method, you need to make sure it is consistent with
- * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for
- * each item. It is called only for the reference item and rest of the items
- * are assigned to spans based on the reference item. For example, you cannot assign a
- * position to span 2 while span 1 is empty.
- *
- * Note that span offsets always start with 0 and are not affected by RTL.
- *
- * @param position The position of the item
- * @param spanCount The total number of spans in the grid
- * @return The final span position of the item. Should be between 0 (inclusive) and
- * spanCount(exclusive)
- */
- public int getSpanIndex(int position, int spanCount) {
- int positionSpanSize = getSpanSize(position);
- if (positionSpanSize == spanCount) {
- return 0; // quick return for full-span items
- }
- int span = 0;
- int startPos = 0;
- // If caching is enabled, try to jump
- if (mCacheSpanIndices && mSpanIndexCache.size() > 0) {
- int prevKey = findReferenceIndexFromCache(position);
- if (prevKey >= 0) {
- span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey);
- startPos = prevKey + 1;
- }
- }
- for (int i = startPos; i < position; i++) {
- int size = getSpanSize(i);
- span += size;
- if (span == spanCount) {
- span = 0;
- } else if (span > spanCount) {
- // did not fit, moving to next row / column
- span = size;
- }
- }
- if (span + positionSpanSize <= spanCount) {
- return span;
- }
- return 0;
- }
-
- int findReferenceIndexFromCache(int position) {
- int lo = 0;
- int hi = mSpanIndexCache.size() - 1;
-
- while (lo <= hi) {
- final int mid = (lo + hi) >>> 1;
- final int midVal = mSpanIndexCache.keyAt(mid);
- if (midVal < position) {
- lo = mid + 1;
- } else {
- hi = mid - 1;
- }
- }
- int index = lo - 1;
- if (index >= 0 && index < mSpanIndexCache.size()) {
- return mSpanIndexCache.keyAt(index);
- }
- return -1;
- }
-
- /**
- * Returns the index of the group this position belongs.
- *
- * For example, if grid has 3 columns and each item occupies 1 span, span group index
- * for item 1 will be 0, item 5 will be 1.
- *
- * @param adapterPosition The position in adapter
- * @param spanCount The total number of spans in the grid
- * @return The index of the span group including the item at the given adapter position
- */
- public int getSpanGroupIndex(int adapterPosition, int spanCount) {
- int span = 0;
- int group = 0;
- int positionSpanSize = getSpanSize(adapterPosition);
- for (int i = 0; i < adapterPosition; i++) {
- int size = getSpanSize(i);
- span += size;
- if (span == spanCount) {
- span = 0;
- group++;
- } else if (span > spanCount) {
- // did not fit, moving to next row / column
- span = size;
- group++;
- }
- }
- if (span + positionSpanSize > spanCount) {
- group++;
- }
- return group;
- }
- }
-
- @Override
- public boolean supportsPredictiveItemAnimations() {
- return mPendingSavedState == null;
- }
-
- /**
- * Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span.
- */
- public static final class DefaultSpanSizeLookup extends SpanSizeLookup {
-
- @Override
- public int getSpanSize(int position) {
- return 1;
- }
-
- @Override
- public int getSpanIndex(int position, int spanCount) {
- return position % spanCount;
- }
- }
-
- /**
- * LayoutParams used by GridLayoutManager.
- */
- public static class LayoutParams extends RecyclerView.LayoutParams {
-
- /**
- * Span Id for Views that are not laid out yet.
- */
- public static final int INVALID_SPAN_ID = -1;
-
- private int mSpanIndex = INVALID_SPAN_ID;
-
- private int mSpanSize = 0;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(ViewGroup.MarginLayoutParams source) {
- super(source);
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- }
-
- public LayoutParams(RecyclerView.LayoutParams source) {
- super(source);
- }
-
- /**
- * Returns the current span index of this View. If the View is not laid out yet, the return
- * value is undefined.
- *
- * Note that span index may change by whether the RecyclerView is RTL or not. For
- * example, if the number of spans is 3 and layout is RTL, the rightmost item will have
- * span index of 2. If the layout changes back to LTR, span index for this view will be 0.
- * If the item was occupying 2 spans, span indices would be 1 and 0 respectively.
- *
- * If the View occupies multiple spans, span with the minimum index is returned.
- *
- * @return The span index of the View.
- */
- public int getSpanIndex() {
- return mSpanIndex;
- }
-
- /**
- * Returns the number of spans occupied by this View. If the View not laid out yet, the
- * return value is undefined.
- *
- * @return The number of spans occupied by this View.
- */
- public int getSpanSize() {
- return mSpanSize;
- }
- }
-
-}
diff --git a/app/src/main/java/android/support/v7/widget/LayoutState.java b/app/src/main/java/android/support/v7/widget/LayoutState.java
deleted file mode 100644
index e62a80a289..0000000000
--- a/app/src/main/java/android/support/v7/widget/LayoutState.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2014 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 languag`e governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.widget;
-import android.view.View;
-
-/**
- * Helper class that keeps temporary state while {LayoutManager} is filling out the empty
- * space.
- */
-class LayoutState {
-
- final static String TAG = "LayoutState";
-
- final static int LAYOUT_START = -1;
-
- final static int LAYOUT_END = 1;
-
- final static int INVALID_LAYOUT = Integer.MIN_VALUE;
-
- final static int ITEM_DIRECTION_HEAD = -1;
-
- final static int ITEM_DIRECTION_TAIL = 1;
-
- final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE;
-
- /**
- * Number of pixels that we should fill, in the layout direction.
- */
- int mAvailable;
-
- /**
- * Current position on the adapter to get the next item.
- */
- int mCurrentPosition;
-
- /**
- * Defines the direction in which the data adapter is traversed.
- * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}
- */
- int mItemDirection;
-
- /**
- * Defines the direction in which the layout is filled.
- * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}
- */
- int mLayoutDirection;
-
- /**
- * Used if you want to pre-layout items that are not yet visible.
- * The difference with {@link #mAvailable} is that, when recycling, distance rendered for
- * {@link #mExtra} is not considered not to recycle visible children.
- */
- int mExtra = 0;
-
- /**
- * @return true if there are more items in the data adapter
- */
- boolean hasMore(RecyclerView.State state) {
- return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
- }
-
- /**
- * Gets the view for the next element that we should render.
- * Also updates current item index to the next item, based on {@link #mItemDirection}
- *
- * @return The next element that we should render.
- */
- View next(RecyclerView.Recycler recycler) {
- final View view = recycler.getViewForPosition(mCurrentPosition);
- mCurrentPosition += mItemDirection;
- return view;
- }
-}
diff --git a/app/src/main/java/android/support/v7/widget/LinearLayoutManager.java b/app/src/main/java/android/support/v7/widget/LinearLayoutManager.java
deleted file mode 100644
index 230db34523..0000000000
--- a/app/src/main/java/android/support/v7/widget/LinearLayoutManager.java
+++ /dev/null
@@ -1,1981 +0,0 @@
-/*
- * Copyright (C) 2014 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 languag`e governing permissions and
- * limitations under the License.
- */
-
-package android.support.v7.widget;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-
-import java.util.List;
-
-import static android.support.v7.widget.RecyclerView.NO_POSITION;
-
-/**
- * A {@link RecyclerView.LayoutManager} implementation which provides
- * similar functionality to {@link android.widget.ListView}.
- */
-public class LinearLayoutManager extends RecyclerView.LayoutManager {
-
- private static final String TAG = "LinearLayoutManager";
-
- private static final boolean DEBUG = false;
-
- public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;
-
- public static final int VERTICAL = OrientationHelper.VERTICAL;
-
- public static final int INVALID_OFFSET = Integer.MIN_VALUE;
-
-
- /**
- * While trying to find next view to focus, LayoutManager will not try to scroll more
- * than this factor times the total space of the list. If layout is vertical, total space is the
- * height minus padding, if layout is horizontal, total space is the width minus padding.
- */
- private static final float MAX_SCROLL_FACTOR = 0.33f;
-
-
- /**
- * Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}
- */
- int mOrientation;
-
- /**
- * Helper class that keeps temporary layout state.
- * It does not keep state after layout is complete but we still keep a reference to re-use
- * the same object.
- */
- private LayoutState mLayoutState;
-
- /**
- * Many calculations are made depending on orientation. To keep it clean, this interface
- * helps {@link LinearLayoutManager} make those decisions.
- * Based on {@link #mOrientation}, an implementation is lazily created in
- * {@link #ensureLayoutState} method.
- */
- OrientationHelper mOrientationHelper;
-
- /**
- * We need to track this so that we can ignore current position when it changes.
- */
- private boolean mLastStackFromEnd;
-
-
- /**
- * Defines if layout should be calculated from end to start.
- *
- * @see #mShouldReverseLayout
- */
- private boolean mReverseLayout = false;
-
- /**
- * This keeps the final value for how LayoutManager should start laying out views.
- * It is calculated by checking {@link #getReverseLayout()} and View's layout direction.
- * {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run.
- */
- boolean mShouldReverseLayout = false;
-
- /**
- * Works the same way as {@link android.widget.AbsListView#setStackFromBottom(boolean)} and
- * it supports both orientations.
- * see {@link android.widget.AbsListView#setStackFromBottom(boolean)}
- */
- private boolean mStackFromEnd = false;
-
- /**
- * Works the same way as {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}.
- * see {@link android.widget.AbsListView#setSmoothScrollbarEnabled(boolean)}
- */
- private boolean mSmoothScrollbarEnabled = true;
-
- /**
- * When LayoutManager needs to scroll to a position, it sets this variable and requests a
- * layout which will check this variable and re-layout accordingly.
- */
- int mPendingScrollPosition = NO_POSITION;
-
- /**
- * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is
- * called.
- */
- int mPendingScrollPositionOffset = INVALID_OFFSET;
-
- private boolean mRecycleChildrenOnDetach;
-
- SavedState mPendingSavedState = null;
-
- /**
- * Re-used variable to keep anchor information on re-layout.
- * Anchor position and coordinate defines the reference point for LLM while doing a layout.
- * */
- final AnchorInfo mAnchorInfo;
-
- /**
- * Creates a vertical LinearLayoutManager
- *
- * @param context Current context, will be used to access resources.
- */
- public LinearLayoutManager(Context context) {
- this(context, VERTICAL, false);
- }
-
- /**
- * @param context Current context, will be used to access resources.
- * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
- * #VERTICAL}.
- * @param reverseLayout When set to true, layouts from end to start.
- */
- public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
- mAnchorInfo = new AnchorInfo();
- setOrientation(orientation);
- setReverseLayout(reverseLayout);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public RecyclerView.LayoutParams generateDefaultLayoutParams() {
- return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- }
-
- /**
- * Returns whether LayoutManager will recycle its children when it is detached from
- * RecyclerView.
- *
- * @return true if LayoutManager will recycle its children when it is detached from
- * RecyclerView.
- */
- public boolean getRecycleChildrenOnDetach() {
- return mRecycleChildrenOnDetach;
- }
-
- /**
- * Set whether LayoutManager will recycle its children when it is detached from
- * RecyclerView.
- *
- * If you are using a {@link RecyclerView.RecycledViewPool}, it might be a good idea to set
- * this flag to true so that views will be avilable to other RecyclerViews
- * immediately.
- *
- * Note that, setting this flag will result in a performance drop if RecyclerView
- * is restored.
- *
- * @param recycleChildrenOnDetach Whether children should be recycled in detach or not.
- */
- public void setRecycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
- mRecycleChildrenOnDetach = recycleChildrenOnDetach;
- }
-
- @Override
- public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {
- super.onDetachedFromWindow(view, recycler);
- if (mRecycleChildrenOnDetach) {
- removeAndRecycleAllViews(recycler);
- recycler.clear();
- }
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- if (getChildCount() > 0) {
- final AccessibilityRecordCompat record = AccessibilityEventCompat
- .asRecord(event);
- record.setFromIndex(findFirstVisibleItemPosition());
- record.setToIndex(findLastVisibleItemPosition());
- }
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- if (mPendingSavedState != null) {
- return new SavedState(mPendingSavedState);
- }
- SavedState state = new SavedState();
- if (getChildCount() > 0) {
- boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayout;
- state.mAnchorLayoutFromEnd = didLayoutFromEnd;
- if (didLayoutFromEnd) {
- final View refChild = getChildClosestToEnd();
- state.mAnchorOffset = mOrientationHelper.getEndAfterPadding() -
- mOrientationHelper.getDecoratedEnd(refChild);
- state.mAnchorPosition = getPosition(refChild);
- } else {
- final View refChild = getChildClosestToStart();
- state.mAnchorPosition = getPosition(refChild);
- state.mAnchorOffset = mOrientationHelper.getDecoratedStart(refChild) -
- mOrientationHelper.getStartAfterPadding();
- }
- } else {
- state.invalidateAnchor();
- }
- return state;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (state instanceof SavedState) {
- mPendingSavedState = (SavedState) state;
- requestLayout();
- if (DEBUG) {
- Log.d(TAG, "loaded saved state");
- }
- } else if (DEBUG) {
- Log.d(TAG, "invalid saved state class");
- }
- }
-
- /**
- * @return true if {@link #getOrientation()} is {@link #HORIZONTAL}
- */
- @Override
- public boolean canScrollHorizontally() {
- return mOrientation == HORIZONTAL;
- }
-
- /**
- * @return true if {@link #getOrientation()} is {@link #VERTICAL}
- */
- @Override
- public boolean canScrollVertically() {
- return mOrientation == VERTICAL;
- }
-
- /**
- * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)}
- */
- public void setStackFromEnd(boolean stackFromEnd) {
- assertNotInLayoutOrScroll(null);
- if (mStackFromEnd == stackFromEnd) {
- return;
- }
- mStackFromEnd = stackFromEnd;
- requestLayout();
- }
-
- public boolean getStackFromEnd() {
- return mStackFromEnd;
- }
-
- /**
- * Returns the current orientaion of the layout.
- *
- * @return Current orientation.
- * @see #mOrientation
- * @see #setOrientation(int)
- */
- public int getOrientation() {
- return mOrientation;
- }
-
- /**
- * Sets the orientation of the layout. {@link LinearLayoutManager}
- * will do its best to keep scroll position.
- *
- * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
- */
- public void setOrientation(int orientation) {
- if (orientation != HORIZONTAL && orientation != VERTICAL) {
- throw new IllegalArgumentException("invalid orientation:" + orientation);
- }
- assertNotInLayoutOrScroll(null);
- if (orientation == mOrientation) {
- return;
- }
- mOrientation = orientation;
- mOrientationHelper = null;
- requestLayout();
- }
-
- /**
- * Calculates the view layout order. (e.g. from end to start or start to end)
- * RTL layout support is applied automatically. So if layout is RTL and
- * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.
- */
- private void resolveShouldLayoutReverse() {
- // A == B is the same result, but we rather keep it readable
- if (mOrientation == VERTICAL || !isLayoutRTL()) {
- mShouldReverseLayout = mReverseLayout;
- } else {
- mShouldReverseLayout = !mReverseLayout;
- }
- }
-
- /**
- * Returns if views are laid out from the opposite direction of the layout.
- *
- * @return If layout is reversed or not.
- * @see {@link #setReverseLayout(boolean)}
- */
- public boolean getReverseLayout() {
- return mReverseLayout;
- }
-
- /**
- * Used to reverse item traversal and layout order.
- * This behaves similar to the layout change for RTL views. When set to true, first item is
- * laid out at the end of the UI, second item is laid out before it etc.
- *
- * For horizontal layouts, it depends on the layout direction.
- * When set to true, If {@link RecyclerView} is LTR, than it will
- * layout from RTL, if {@link RecyclerView}} is RTL, it will layout
- * from LTR.
- *
- * If you are looking for the exact same behavior of
- * {@link android.widget.AbsListView#setStackFromBottom(boolean)}, use
- * {@link #setStackFromEnd(boolean)}
- */
- public void setReverseLayout(boolean reverseLayout) {
- assertNotInLayoutOrScroll(null);
- if (reverseLayout == mReverseLayout) {
- return;
- }
- mReverseLayout = reverseLayout;
- requestLayout();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public View findViewByPosition(int position) {
- final int childCount = getChildCount();
- if (childCount == 0) {
- return null;
- }
- final int firstChild = getPosition(getChildAt(0));
- final int viewPosition = position - firstChild;
- if (viewPosition >= 0 && viewPosition < childCount) {
- return getChildAt(viewPosition);
- }
- return null;
- }
-
- /**
- *
Returns the amount of extra space that should be laid out by LayoutManager.
- * By default, {@link LinearLayoutManager} lays out 1 extra page of
- * items while smooth scrolling and 0 otherwise. You can override this method to implement your
- * custom layout pre-cache logic.
- *
Laying out invisible elements will eventually come with performance cost. On the other
- * hand, in places like smooth scrolling to an unknown location, this extra content helps
- * LayoutManager to calculate a much smoother scrolling; which improves user experience.
- *
You can also use this if you are trying to pre-layout your upcoming views.
- *
- * @return The extra space that should be laid out (in pixels).
- */
- protected int getExtraLayoutSpace(RecyclerView.State state) {
- if (state.hasTargetScrollPosition()) {
- return mOrientationHelper.getTotalSpace();
- } else {
- return 0;
- }
- }
-
- @Override
- public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
- int position) {
- LinearSmoothScroller linearSmoothScroller =
- new LinearSmoothScroller(recyclerView.getContext()) {
- @Override
- public PointF computeScrollVectorForPosition(int targetPosition) {
- return LinearLayoutManager.this
- .computeScrollVectorForPosition(targetPosition);
- }
- };
- linearSmoothScroller.setTargetPosition(position);
- startSmoothScroll(linearSmoothScroller);
- }
-
- public PointF computeScrollVectorForPosition(int targetPosition) {
- if (getChildCount() == 0) {
- return null;
- }
- final int firstChildPos = getPosition(getChildAt(0));
- final int direction = targetPosition < firstChildPos != mShouldReverseLayout ? -1 : 1;
- if (mOrientation == HORIZONTAL) {
- return new PointF(direction, 0);
- } else {
- return new PointF(0, direction);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
- // layout algorithm:
- // 1) by checking children and other variables, find an anchor coordinate and an anchor
- // item position.
- // 2) fill towards start, stacking from bottom
- // 3) fill towards end, stacking from top
- // 4) scroll to fulfill requirements like stack from bottom.
- // create layout state
- if (DEBUG) {
- Log.d(TAG, "is pre layout:" + state.isPreLayout());
- }
- if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
- mPendingScrollPosition = mPendingSavedState.mAnchorPosition;
- }
-
- ensureLayoutState();
- mLayoutState.mRecycle = false;
- // resolve layout direction
- resolveShouldLayoutReverse();
-
- mAnchorInfo.reset();
- mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
- // calculate anchor position and coordinate
- updateAnchorInfoForLayout(state, mAnchorInfo);
- if (DEBUG) {
- Log.d(TAG, "Anchor info:" + mAnchorInfo);
- }
-
- // LLM may decide to layout items for "extra" pixels to account for scrolling target,
- // caching or predictive animations.
- int extraForStart;
- int extraForEnd;
- final int extra = getExtraLayoutSpace(state);
- boolean before = state.getTargetScrollPosition() < mAnchorInfo.mPosition;
- if (before == mShouldReverseLayout) {
- extraForEnd = extra;
- extraForStart = 0;
- } else {
- extraForStart = extra;
- extraForEnd = 0;
- }
- extraForStart += mOrientationHelper.getStartAfterPadding();
- extraForEnd += mOrientationHelper.getEndPadding();
- if (state.isPreLayout() && mPendingScrollPosition != NO_POSITION &&
- mPendingScrollPositionOffset != INVALID_OFFSET) {
- // if the child is visible and we are going to move it around, we should layout
- // extra items in the opposite direction to make sure new items animate nicely
- // instead of just fading in
- final View existing = findViewByPosition(mPendingScrollPosition);
- if (existing != null) {
- final int current;
- final int upcomingOffset;
- if (mShouldReverseLayout) {
- current = mOrientationHelper.getEndAfterPadding() -
- mOrientationHelper.getDecoratedEnd(existing);
- upcomingOffset = current - mPendingScrollPositionOffset;
- } else {
- current = mOrientationHelper.getDecoratedStart(existing)
- - mOrientationHelper.getStartAfterPadding();
- upcomingOffset = mPendingScrollPositionOffset - current;
- }
- if (upcomingOffset > 0) {
- extraForStart += upcomingOffset;
- } else {
- extraForEnd -= upcomingOffset;
- }
- }
- }
- int startOffset;
- int endOffset;
- onAnchorReady(state, mAnchorInfo);
- detachAndScrapAttachedViews(recycler);
- mLayoutState.mIsPreLayout = state.isPreLayout();
- if (mAnchorInfo.mLayoutFromEnd) {
- // fill towards start
- updateLayoutStateToFillStart(mAnchorInfo);
- mLayoutState.mExtra = extraForStart;
- fill(recycler, mLayoutState, state, false);
- startOffset = mLayoutState.mOffset;
- if (mLayoutState.mAvailable > 0) {
- extraForEnd += mLayoutState.mAvailable;
- }
- // fill towards end
- updateLayoutStateToFillEnd(mAnchorInfo);
- mLayoutState.mExtra = extraForEnd;
- mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
- fill(recycler, mLayoutState, state, false);
- endOffset = mLayoutState.mOffset;
- } else {
- // fill towards end
- updateLayoutStateToFillEnd(mAnchorInfo);
- mLayoutState.mExtra = extraForEnd;
- fill(recycler, mLayoutState, state, false);
- endOffset = mLayoutState.mOffset;
- if (mLayoutState.mAvailable > 0) {
- extraForStart += mLayoutState.mAvailable;
- }
- // fill towards start
- updateLayoutStateToFillStart(mAnchorInfo);
- mLayoutState.mExtra = extraForStart;
- mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
- fill(recycler, mLayoutState, state, false);
- startOffset = mLayoutState.mOffset;
- }
-
- // changes may cause gaps on the UI, try to fix them.
- // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have
- // changed
- if (getChildCount() > 0) {
- // because layout from end may be changed by scroll to position
- // we re-calculate it.
- // find which side we should check for gaps.
- if (mShouldReverseLayout ^ mStackFromEnd) {
- int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true);
- startOffset += fixOffset;
- endOffset += fixOffset;
- fixOffset = fixLayoutStartGap(startOffset, recycler, state, false);
- startOffset += fixOffset;
- endOffset += fixOffset;
- } else {
- int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);
- startOffset += fixOffset;
- endOffset += fixOffset;
- fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);
- startOffset += fixOffset;
- endOffset += fixOffset;
- }
- }
- layoutForPredictiveAnimations(recycler, state, startOffset, endOffset);
- if (!state.isPreLayout()) {
- mPendingScrollPosition = NO_POSITION;
- mPendingScrollPositionOffset = INVALID_OFFSET;
- mOrientationHelper.onLayoutComplete();
- }
- mLastStackFromEnd = mStackFromEnd;
- mPendingSavedState = null; // we don't need this anymore
- if (DEBUG) {
- validateChildOrder();
- }
- }
-
- /**
- * Method called when Anchor position is decided. Extending class can setup accordingly or
- * even update anchor info if necessary.
- *
- * @param state
- * @param anchorInfo Simple data structure to keep anchor point information for the next layout
- */
- void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) {
- }
-
- /**
- * If necessary, layouts new items for predictive animations
- */
- private void layoutForPredictiveAnimations(RecyclerView.Recycler recycler,
- RecyclerView.State state, int startOffset, int endOffset) {
- // If there are scrap children that we did not layout, we need to find where they did go
- // and layout them accordingly so that animations can work as expected.
- // This case may happen if new views are added or an existing view expands and pushes
- // another view out of bounds.
- if (!state.willRunPredictiveAnimations() || getChildCount() == 0 || state.isPreLayout()
- || !supportsPredictiveItemAnimations()) {
- return;
- }
-
- // to make the logic simpler, we calculate the size of children and call fill.
- int scrapExtraStart = 0, scrapExtraEnd = 0;
- final List scrapList = recycler.getScrapList();
- final int scrapSize = scrapList.size();
- final int firstChildPos = getPosition(getChildAt(0));
- for (int i = 0; i < scrapSize; i++) {
- RecyclerView.ViewHolder scrap = scrapList.get(i);
- final int position = scrap.getPosition();
- final int direction = position < firstChildPos != mShouldReverseLayout
- ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END;
- if (direction == LayoutState.LAYOUT_START) {
- scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
- } else {
- scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);
- }
- }
-
- if (DEBUG) {
- Log.d(TAG, "for unused scrap, decided to add " + scrapExtraStart
- + " towards start and " + scrapExtraEnd + " towards end");
- }
- mLayoutState.mScrapList = scrapList;
- if (scrapExtraStart > 0) {
- View anchor = getChildClosestToStart();
- updateLayoutStateToFillStart(getPosition(anchor), startOffset);
- mLayoutState.mExtra = scrapExtraStart;
- mLayoutState.mAvailable = 0;
- mLayoutState.mCurrentPosition += mShouldReverseLayout ? 1 : -1;
- fill(recycler, mLayoutState, state, false);
- }
-
- if (scrapExtraEnd > 0) {
- View anchor = getChildClosestToEnd();
- updateLayoutStateToFillEnd(getPosition(anchor), endOffset);
- mLayoutState.mExtra = scrapExtraEnd;
- mLayoutState.mAvailable = 0;
- mLayoutState.mCurrentPosition += mShouldReverseLayout ? -1 : 1;
- fill(recycler, mLayoutState, state, false);
- }
- mLayoutState.mScrapList = null;
- }
-
- private void updateAnchorInfoForLayout(RecyclerView.State state, AnchorInfo anchorInfo) {
- if (updateAnchorFromPendingData(state, anchorInfo)) {
- if (DEBUG) {
- Log.d(TAG, "updated anchor info from pending information");
- }
- return;
- }
-
- if (updateAnchorFromChildren(state, anchorInfo)) {
- if (DEBUG) {
- Log.d(TAG, "updated anchor info from existing children");
- }
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "deciding anchor info for fresh state");
- }
- anchorInfo.assignCoordinateFromPadding();
- anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0;
- }
-
- /**
- * Finds an anchor child from existing Views. Most of the time, this is the view closest to
- * start or end that has a valid position (e.g. not removed).
- *
- * If a child has focus, it is given priority.
- */
- private boolean updateAnchorFromChildren(RecyclerView.State state, AnchorInfo anchorInfo) {
- if (getChildCount() == 0) {
- return false;
- }
- View focused = getFocusedChild();
- if (focused != null && anchorInfo.assignFromViewIfValid(focused, state)) {
- if (DEBUG) {
- Log.d(TAG, "decided anchor child from focused view");
- }
- return true;
- }
-
- if (mLastStackFromEnd != mStackFromEnd) {
- return false;
- }
-
- View referenceChild = anchorInfo.mLayoutFromEnd ? findReferenceChildClosestToEnd(state)
- : findReferenceChildClosestToStart(state);
- if (referenceChild != null) {
- anchorInfo.assignFromView(referenceChild);
- // If all visible views are removed in 1 pass, reference child might be out of bounds.
- // If that is the case, offset it back to 0 so that we use these pre-layout children.
- if (!state.isPreLayout() && supportsPredictiveItemAnimations()) {
- // validate this child is at least partially visible. if not, offset it to start
- final boolean notVisible =
- mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper
- .getEndAfterPadding()
- || mOrientationHelper.getDecoratedEnd(referenceChild)
- < mOrientationHelper.getStartAfterPadding();
- if (notVisible) {
- anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd
- ? mOrientationHelper.getEndAfterPadding()
- : mOrientationHelper.getStartAfterPadding();
- }
- }
- return true;
- }
- return false;
- }
-
- /**
- * If there is a pending scroll position or saved states, updates the anchor info from that
- * data and returns true
- */
- private boolean updateAnchorFromPendingData(RecyclerView.State state, AnchorInfo anchorInfo) {
- if (state.isPreLayout() || mPendingScrollPosition == NO_POSITION) {
- return false;
- }
- // validate scroll position
- if (mPendingScrollPosition < 0 || mPendingScrollPosition >= state.getItemCount()) {
- mPendingScrollPosition = NO_POSITION;
- mPendingScrollPositionOffset = INVALID_OFFSET;
- if (DEBUG) {
- Log.e(TAG, "ignoring invalid scroll position " + mPendingScrollPosition);
- }
- return false;
- }
-
- // if child is visible, try to make it a reference child and ensure it is fully visible.
- // if child is not visible, align it depending on its virtual position.
- anchorInfo.mPosition = mPendingScrollPosition;
- if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
- // Anchor offset depends on how that child was laid out. Here, we update it
- // according to our current view bounds
- anchorInfo.mLayoutFromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
- if (anchorInfo.mLayoutFromEnd) {
- anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
- mPendingSavedState.mAnchorOffset;
- } else {
- anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() +
- mPendingSavedState.mAnchorOffset;
- }
- return true;
- }
-
- if (mPendingScrollPositionOffset == INVALID_OFFSET) {
- View child = findViewByPosition(mPendingScrollPosition);
- if (child != null) {
- final int childSize = mOrientationHelper.getDecoratedMeasurement(child);
- if (childSize > mOrientationHelper.getTotalSpace()) {
- // item does not fit. fix depending on layout direction
- anchorInfo.assignCoordinateFromPadding();
- return true;
- }
- final int startGap = mOrientationHelper.getDecoratedStart(child)
- - mOrientationHelper.getStartAfterPadding();
- if (startGap < 0) {
- anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding();
- anchorInfo.mLayoutFromEnd = false;
- return true;
- }
- final int endGap = mOrientationHelper.getEndAfterPadding() -
- mOrientationHelper.getDecoratedEnd(child);
- if (endGap < 0) {
- anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding();
- anchorInfo.mLayoutFromEnd = true;
- return true;
- }
- anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd
- ? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper
- .getTotalSpaceChange())
- : mOrientationHelper.getDecoratedStart(child);
- } else { // item is not visible.
- if (getChildCount() > 0) {
- // get position of any child, does not matter
- int pos = getPosition(getChildAt(0));
- anchorInfo.mLayoutFromEnd = mPendingScrollPosition < pos
- == mShouldReverseLayout;
- }
- anchorInfo.assignCoordinateFromPadding();
- }
- return true;
- }
- // override layout from end values for consistency
- anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
- if (mShouldReverseLayout) {
- anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
- mPendingScrollPositionOffset;
- } else {
- anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() +
- mPendingScrollPositionOffset;
- }
- return true;
- }
-
- /**
- * @return The final offset amount for children
- */
- private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler,
- RecyclerView.State state, boolean canOffsetChildren) {
- int gap = mOrientationHelper.getEndAfterPadding() - endOffset;
- int fixOffset = 0;
- if (gap > 0) {
- fixOffset = -scrollBy(-gap, recycler, state);
- } else {
- return 0; // nothing to fix
- }
- // move offset according to scroll amount
- endOffset += fixOffset;
- if (canOffsetChildren) {
- // re-calculate gap, see if we could fix it
- gap = mOrientationHelper.getEndAfterPadding() - endOffset;
- if (gap > 0) {
- mOrientationHelper.offsetChildren(gap);
- return gap + fixOffset;
- }
- }
- return fixOffset;
- }
-
- /**
- * @return The final offset amount for children
- */
- private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler,
- RecyclerView.State state, boolean canOffsetChildren) {
- int gap = startOffset - mOrientationHelper.getStartAfterPadding();
- int fixOffset = 0;
- if (gap > 0) {
- // check if we should fix this gap.
- fixOffset = -scrollBy(gap, recycler, state);
- } else {
- return 0; // nothing to fix
- }
- startOffset += fixOffset;
- if (canOffsetChildren) {
- // re-calculate gap, see if we could fix it
- gap = startOffset - mOrientationHelper.getStartAfterPadding();
- if (gap > 0) {
- mOrientationHelper.offsetChildren(-gap);
- return fixOffset - gap;
- }
- }
- return fixOffset;
- }
-
- private void updateLayoutStateToFillEnd(AnchorInfo anchorInfo) {
- updateLayoutStateToFillEnd(anchorInfo.mPosition, anchorInfo.mCoordinate);
- }
-
- private void updateLayoutStateToFillEnd(int itemPosition, int offset) {
- mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset;
- mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD :
- LayoutState.ITEM_DIRECTION_TAIL;
- mLayoutState.mCurrentPosition = itemPosition;
- mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END;
- mLayoutState.mOffset = offset;
- mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;
- }
-
- private void updateLayoutStateToFillStart(AnchorInfo anchorInfo) {
- updateLayoutStateToFillStart(anchorInfo.mPosition, anchorInfo.mCoordinate);
- }
-
- private void updateLayoutStateToFillStart(int itemPosition, int offset) {
- mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding();
- mLayoutState.mCurrentPosition = itemPosition;
- mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL :
- LayoutState.ITEM_DIRECTION_HEAD;
- mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START;
- mLayoutState.mOffset = offset;
- mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;
-
- }
-
- protected boolean isLayoutRTL() {
- return getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL;
- }
-
- void ensureLayoutState() {
- if (mLayoutState == null) {
- mLayoutState = new LayoutState();
- }
- if (mOrientationHelper == null) {
- mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
- }
- }
-
- /**
- *
Scroll the RecyclerView to make the position visible.
- *
- *
RecyclerView will scroll the minimum amount that is necessary to make the
- * target position visible. If you are looking for a similar behavior to
- * {@link android.widget.ListView#setSelection(int)} or
- * {@link android.widget.ListView#setSelectionFromTop(int, int)}, use
- * {@link #scrollToPositionWithOffset(int, int)}.
- *
- *
Note that scroll position change will not be reflected until the next layout call.
- *
- * @param position Scroll to this adapter position
- * @see #scrollToPositionWithOffset(int, int)
- */
- @Override
- public void scrollToPosition(int position) {
- mPendingScrollPosition = position;
- mPendingScrollPositionOffset = INVALID_OFFSET;
- if (mPendingSavedState != null) {
- mPendingSavedState.invalidateAnchor();
- }
- requestLayout();
- }
-
- /**
- * Scroll to the specified adapter position with the given offset from resolved layout
- * start. Resolved layout start depends on {@link #getReverseLayout()},
- * {@link ViewCompat#getLayoutDirection(View)} and {@link #getStackFromEnd()}.
- *
- * For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling
- * scrollToPositionWithOffset(10, 20) will layout such that
- * item[10]'s bottom is 20 pixels above the RecyclerView's bottom.
- *
- * Note that scroll position change will not be reflected until the next layout call.
- *
- *
- * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.
- *
- * @param position Index (starting at 0) of the reference item.
- * @param offset The distance (in pixels) between the start edge of the item view and
- * start edge of the RecyclerView.
- * @see #setReverseLayout(boolean)
- * @see #scrollToPosition(int)
- */
- public void scrollToPositionWithOffset(int position, int offset) {
- mPendingScrollPosition = position;
- mPendingScrollPositionOffset = offset;
- if (mPendingSavedState != null) {
- mPendingSavedState.invalidateAnchor();
- }
- requestLayout();
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
- RecyclerView.State state) {
- if (mOrientation == VERTICAL) {
- return 0;
- }
- return scrollBy(dx, recycler, state);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
- RecyclerView.State state) {
- if (mOrientation == HORIZONTAL) {
- return 0;
- }
- return scrollBy(dy, recycler, state);
- }
-
- @Override
- public int computeHorizontalScrollOffset(RecyclerView.State state) {
- return computeScrollOffset(state);
- }
-
- @Override
- public int computeVerticalScrollOffset(RecyclerView.State state) {
- return computeScrollOffset(state);
- }
-
- @Override
- public int computeHorizontalScrollExtent(RecyclerView.State state) {
- return computeScrollExtent(state);
- }
-
- @Override
- public int computeVerticalScrollExtent(RecyclerView.State state) {
- return computeScrollExtent(state);
- }
-
- @Override
- public int computeHorizontalScrollRange(RecyclerView.State state) {
- return computeScrollRange(state);
- }
-
- @Override
- public int computeVerticalScrollRange(RecyclerView.State state) {
- return computeScrollRange(state);
- }
-
- private int computeScrollOffset(RecyclerView.State state) {
- if (getChildCount() == 0) {
- return 0;
- }
- return ScrollbarHelper.computeScrollOffset(state, mOrientationHelper,
- getChildClosestToStart(), getChildClosestToEnd(), this,
- mSmoothScrollbarEnabled, mShouldReverseLayout);
- }
-
- private int computeScrollExtent(RecyclerView.State state) {
- if (getChildCount() == 0) {
- return 0;
- }
- return ScrollbarHelper.computeScrollExtent(state, mOrientationHelper,
- getChildClosestToStart(), getChildClosestToEnd(), this,
- mSmoothScrollbarEnabled);
- }
-
- private int computeScrollRange(RecyclerView.State state) {
- if (getChildCount() == 0) {
- return 0;
- }
- return ScrollbarHelper.computeScrollRange(state, mOrientationHelper,
- getChildClosestToStart(), getChildClosestToEnd(), this,
- mSmoothScrollbarEnabled);
- }
-
- /**
- * When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed
- * based on the number of visible pixels in the visible items. This however assumes that all
- * list items have similar or equal widths or heights (depending on list orientation).
- * If you use a list in which items have different dimensions, the scrollbar will change
- * appearance as the user scrolls through the list. To avoid this issue, you need to disable
- * this property.
- *
- * When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based
- * solely on the number of items in the adapter and the position of the visible items inside
- * the adapter. This provides a stable scrollbar as the user navigates through a list of items
- * with varying widths / heights.
- *
- * @param enabled Whether or not to enable smooth scrollbar.
- *
- * @see #setSmoothScrollbarEnabled(boolean)
- */
- public void setSmoothScrollbarEnabled(boolean enabled) {
- mSmoothScrollbarEnabled = enabled;
- }
-
- /**
- * Returns the current state of the smooth scrollbar feature. It is enabled by default.
- *
- * @return True if smooth scrollbar is enabled, false otherwise.
- *
- * @see #setSmoothScrollbarEnabled(boolean)
- */
- public boolean isSmoothScrollbarEnabled() {
- return mSmoothScrollbarEnabled;
- }
-
- private void updateLayoutState(int layoutDirection, int requiredSpace,
- boolean canUseExistingSpace, RecyclerView.State state) {
- mLayoutState.mExtra = getExtraLayoutSpace(state);
- mLayoutState.mLayoutDirection = layoutDirection;
- int fastScrollSpace;
- if (layoutDirection == LayoutState.LAYOUT_END) {
- mLayoutState.mExtra += mOrientationHelper.getEndPadding();
- // get the first child in the direction we are going
- final View child = getChildClosestToEnd();
- // the direction in which we are traversing children
- mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD
- : LayoutState.ITEM_DIRECTION_TAIL;
- mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
- mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child);
- // calculate how much we can scroll without adding new children (independent of layout)
- fastScrollSpace = mOrientationHelper.getDecoratedEnd(child)
- - mOrientationHelper.getEndAfterPadding();
-
- } else {
- final View child = getChildClosestToStart();
- mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding();
- mLayoutState.mItemDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL
- : LayoutState.ITEM_DIRECTION_HEAD;
- mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
- mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child);
- fastScrollSpace = -mOrientationHelper.getDecoratedStart(child)
- + mOrientationHelper.getStartAfterPadding();
- }
- mLayoutState.mAvailable = requiredSpace;
- if (canUseExistingSpace) {
- mLayoutState.mAvailable -= fastScrollSpace;
- }
- mLayoutState.mScrollingOffset = fastScrollSpace;
- }
-
- int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
- if (getChildCount() == 0 || dy == 0) {
- return 0;
- }
- mLayoutState.mRecycle = true;
- ensureLayoutState();
- final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
- final int absDy = Math.abs(dy);
- updateLayoutState(layoutDirection, absDy, true, state);
- final int freeScroll = mLayoutState.mScrollingOffset;
- final int consumed = freeScroll + fill(recycler, mLayoutState, state, false);
- if (consumed < 0) {
- if (DEBUG) {
- Log.d(TAG, "Don't have any more elements to scroll");
- }
- return 0;
- }
- final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
- mOrientationHelper.offsetChildren(-scrolled);
- if (DEBUG) {
- Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
- }
- return scrolled;
- }
-
- @Override
- public void assertNotInLayoutOrScroll(String message) {
- if (mPendingSavedState == null) {
- super.assertNotInLayoutOrScroll(message);
- }
- }
-
- /**
- * Recycles children between given indices.
- *
- * @param startIndex inclusive
- * @param endIndex exclusive
- */
- private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
- if (startIndex == endIndex) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "Recycling " + Math.abs(startIndex - endIndex) + " items");
- }
- if (endIndex > startIndex) {
- for (int i = endIndex - 1; i >= startIndex; i--) {
- removeAndRecycleViewAt(i, recycler);
- }
- } else {
- for (int i = startIndex; i > endIndex; i--) {
- removeAndRecycleViewAt(i, recycler);
- }
- }
- }
-
- /**
- * Recycles views that went out of bounds after scrolling towards the end of the layout.
- *
- * @param recycler Recycler instance of {@link RecyclerView}
- * @param dt This can be used to add additional padding to the visible area. This is used
- * to
- * detect children that will go out of bounds after scrolling, without actually
- * moving them.
- */
- private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
- if (dt < 0) {
- if (DEBUG) {
- Log.d(TAG, "Called recycle from start with a negative value. This might happen"
- + " during layout changes but may be sign of a bug");
- }
- return;
- }
- // ignore padding, ViewGroup may not clip children.
- final int limit = dt;
- final int childCount = getChildCount();
- if (mShouldReverseLayout) {
- for (int i = childCount - 1; i >= 0; i--) {
- View child = getChildAt(i);
- if (mOrientationHelper.getDecoratedEnd(child) > limit) {// stop here
- recycleChildren(recycler, childCount - 1, i);
- return;
- }
- }
- } else {
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (mOrientationHelper.getDecoratedEnd(child) > limit) {// stop here
- recycleChildren(recycler, 0, i);
- return;
- }
- }
- }
- }
-
-
- /**
- * Recycles views that went out of bounds after scrolling towards the start of the layout.
- *
- * @param recycler Recycler instance of {@link RecyclerView}
- * @param dt This can be used to add additional padding to the visible area. This is used
- * to detect children that will go out of bounds after scrolling, without
- * actually moving them.
- */
- private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) {
- final int childCount = getChildCount();
- if (dt < 0) {
- if (DEBUG) {
- Log.d(TAG, "Called recycle from end with a negative value. This might happen"
- + " during layout changes but may be sign of a bug");
- }
- return;
- }
- final int limit = mOrientationHelper.getEnd() - dt;
- if (mShouldReverseLayout) {
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here
- recycleChildren(recycler, 0, i);
- return;
- }
- }
- } else {
- for (int i = childCount - 1; i >= 0; i--) {
- View child = getChildAt(i);
- if (mOrientationHelper.getDecoratedStart(child) < limit) {// stop here
- recycleChildren(recycler, childCount - 1, i);
- return;
- }
- }
- }
-
- }
-
- /**
- * Helper method to call appropriate recycle method depending on current layout direction
- *
- * @param recycler Current recycler that is attached to RecyclerView
- * @param layoutState Current layout state. Right now, this object does not change but
- * we may consider moving it out of this view so passing around as a
- * parameter for now, rather than accessing {@link #mLayoutState}
- * @see #recycleViewsFromStart(RecyclerView.Recycler, int)
- * @see #recycleViewsFromEnd(RecyclerView.Recycler, int)
- * @see LayoutState#mLayoutDirection
- */
- private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
- if (!layoutState.mRecycle) {
- return;
- }
- if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
- recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
- } else {
- recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
- }
- }
-
- /**
- * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
- * independent from the rest of the {@link LinearLayoutManager}
- * and with little change, can be made publicly available as a helper class.
- *
- * @param recycler Current recycler that is attached to RecyclerView
- * @param layoutState Configuration on how we should fill out the available space.
- * @param state Context passed by the RecyclerView to control scroll steps.
- * @param stopOnFocusable If true, filling stops in the first focusable new child
- * @return Number of pixels that it added. Useful for scoll functions.
- */
- int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
- RecyclerView.State state, boolean stopOnFocusable) {
- // max offset we should set is mFastScroll + available
- final int start = layoutState.mAvailable;
- if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {
- // TODO ugly bug fix. should not happen
- if (layoutState.mAvailable < 0) {
- layoutState.mScrollingOffset += layoutState.mAvailable;
- }
- recycleByLayoutState(recycler, layoutState);
- }
- int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
- LayoutChunkResult layoutChunkResult = new LayoutChunkResult();
- while (remainingSpace > 0 && layoutState.hasMore(state)) {
- layoutChunkResult.resetInternal();
- layoutChunk(recycler, state, layoutState, layoutChunkResult);
- if (layoutChunkResult.mFinished) {
- break;
- }
- layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
- /**
- * Consume the available space if:
- * * layoutChunk did not request to be ignored
- * * OR we are laying out scrap children
- * * OR we are not doing pre-layout
- */
- if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
- || !state.isPreLayout()) {
- layoutState.mAvailable -= layoutChunkResult.mConsumed;
- // we keep a separate remaining space because mAvailable is important for recycling
- remainingSpace -= layoutChunkResult.mConsumed;
- }
-
- if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {
- layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
- if (layoutState.mAvailable < 0) {
- layoutState.mScrollingOffset += layoutState.mAvailable;
- }
- recycleByLayoutState(recycler, layoutState);
- }
- if (stopOnFocusable && layoutChunkResult.mFocusable) {
- break;
- }
- }
- if (DEBUG) {
- validateChildOrder();
- }
- return start - layoutState.mAvailable;
- }
-
- void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
- LayoutState layoutState, LayoutChunkResult result) {
- View view = layoutState.next(recycler);
- if (view == null) {
- if (DEBUG && layoutState.mScrapList == null) {
- throw new RuntimeException("received null view when unexpected");
- }
- // if we are laying out views in scrap, this may return null which means there is
- // no more items to layout.
- result.mFinished = true;
- return;
- }
- RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
- if (layoutState.mScrapList == null) {
- if (mShouldReverseLayout == (layoutState.mLayoutDirection
- == LayoutState.LAYOUT_START)) {
- addView(view);
- } else {
- addView(view, 0);
- }
- } else {
- if (mShouldReverseLayout == (layoutState.mLayoutDirection
- == LayoutState.LAYOUT_START)) {
- addDisappearingView(view);
- } else {
- addDisappearingView(view, 0);
- }
- }
- measureChildWithMargins(view, 0, 0);
- result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
- int left, top, right, bottom;
- if (mOrientation == VERTICAL) {
- if (isLayoutRTL()) {
- right = getWidth() - getPaddingRight();
- left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
- } else {
- left = getPaddingLeft();
- right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
- }
- if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
- bottom = layoutState.mOffset;
- top = layoutState.mOffset - result.mConsumed;
- } else {
- top = layoutState.mOffset;
- bottom = layoutState.mOffset + result.mConsumed;
- }
- } else {
- top = getPaddingTop();
- bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
-
- if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
- right = layoutState.mOffset;
- left = layoutState.mOffset - result.mConsumed;
- } else {
- left = layoutState.mOffset;
- right = layoutState.mOffset + result.mConsumed;
- }
- }
- // We calculate everything with View's bounding box (which includes decor and margins)
- // To calculate correct layout position, we subtract margins.
- layoutDecorated(view, left + params.leftMargin, top + params.topMargin,
- right - params.rightMargin, bottom - params.bottomMargin);
- if (DEBUG) {
- Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
- + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
- + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));
- }
- // Consume the available space if the view is not removed OR changed
- if (params.isItemRemoved() || params.isItemChanged()) {
- result.mIgnoreConsumed = true;
- }
- result.mFocusable = view.isFocusable();
- }
-
- /**
- * Converts a focusDirection to orientation.
- *
- * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
- * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
- * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
- * or 0 for not applicable
- * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction
- * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.
- */
- private int convertFocusDirectionToLayoutDirection(int focusDirection) {
- switch (focusDirection) {
- case View.FOCUS_BACKWARD:
- return LayoutState.LAYOUT_START;
- case View.FOCUS_FORWARD:
- return LayoutState.LAYOUT_END;
- case View.FOCUS_UP:
- return mOrientation == VERTICAL ? LayoutState.LAYOUT_START
- : LayoutState.INVALID_LAYOUT;
- case View.FOCUS_DOWN:
- return mOrientation == VERTICAL ? LayoutState.LAYOUT_END
- : LayoutState.INVALID_LAYOUT;
- case View.FOCUS_LEFT:
- return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START
- : LayoutState.INVALID_LAYOUT;
- case View.FOCUS_RIGHT:
- return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END
- : LayoutState.INVALID_LAYOUT;
- default:
- if (DEBUG) {
- Log.d(TAG, "Unknown focus request:" + focusDirection);
- }
- return LayoutState.INVALID_LAYOUT;
- }
-
- }
-
- /**
- * Convenience method to find the child closes to start. Caller should check it has enough
- * children.
- *
- * @return The child closes to start of the layout from user's perspective.
- */
- private View getChildClosestToStart() {
- return getChildAt(mShouldReverseLayout ? getChildCount() - 1 : 0);
- }
-
- /**
- * Convenience method to find the child closes to end. Caller should check it has enough
- * children.
- *
- * @return The child closes to end of the layout from user's perspective.
- */
- private View getChildClosestToEnd() {
- return getChildAt(mShouldReverseLayout ? 0 : getChildCount() - 1);
- }
-
-
- /**
- * Among the children that are suitable to be considered as an anchor child, returns the one
- * closest to the end of the layout.
- *
- * Due to ambiguous adapter updates or children being removed, some children's positions may be
- * invalid. This method is a best effort to find a position within adapter bounds if possible.
- *
- * It also prioritizes children that are within the visible bounds.
- * @return A View that can be used an an anchor View.
- */
- private View findReferenceChildClosestToEnd(RecyclerView.State state) {
- return mShouldReverseLayout ? findFirstReferenceChild(state.getItemCount()) :
- findLastReferenceChild(state.getItemCount());
- }
-
- /**
- * Among the children that are suitable to be considered as an anchor child, returns the one
- * closest to the start of the layout.
- *
- * Due to ambiguous adapter updates or children being removed, some children's positions may be
- * invalid. This method is a best effort to find a position within adapter bounds if possible.
- *
- * It also prioritizes children that are within the visible bounds.
- *
- * @return A View that can be used an an anchor View.
- */
- private View findReferenceChildClosestToStart(RecyclerView.State state) {
- return mShouldReverseLayout ? findLastReferenceChild(state.getItemCount()) :
- findFirstReferenceChild(state.getItemCount());
- }
-
- private View findFirstReferenceChild(int itemCount) {
- return findReferenceChild(0, getChildCount(), itemCount);
- }
-
- private View findLastReferenceChild(int itemCount) {
- return findReferenceChild(getChildCount() - 1, -1, itemCount);
- }
-
- private View findReferenceChild(int start, int end, int itemCount) {
- View invalidMatch = null;
- View outOfBoundsMatch = null;
- final int boundsStart = mOrientationHelper.getStartAfterPadding();
- final int boundsEnd = mOrientationHelper.getEndAfterPadding();
- final int diff = end > start ? 1 : -1;
- for (int i = start; i != end; i += diff) {
- final View view = getChildAt(i);
- final int position = getPosition(view);
- if (position >= 0 && position < itemCount) {
- if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {
- if (invalidMatch == null) {
- invalidMatch = view; // removed item, least preferred
- }
- } else if (mOrientationHelper.getDecoratedStart(view) >= boundsEnd ||
- mOrientationHelper.getDecoratedEnd(view) < boundsStart) {
- if (outOfBoundsMatch == null) {
- outOfBoundsMatch = view; // item is not visible, less preferred
- }
- } else {
- return view;
- }
- }
- }
- return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;
- }
-
- /**
- * Returns the adapter position of the first visible view.
- *
- * Note that, this value is not affected by layout orientation or item order traversal.
- * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
- * not in the layout.
- *
- * If RecyclerView has item decorators, they will be considered in calculations as well.
- *
- * LayoutManager may pre-cache some views that are not necessarily visible. Those views
- * are ignored in this method.
- *
- * @return The adapter position of the first visible item or {@link RecyclerView#NO_POSITION} if
- * there aren't any visible items.
- * @see #findFirstCompletelyVisibleItemPosition()
- * @see #findLastVisibleItemPosition()
- */
- public int findFirstVisibleItemPosition() {
- final View child = findOneVisibleChild(0, getChildCount(), false);
- return child == null ? NO_POSITION : getPosition(child);
- }
-
- /**
- * Returns the adapter position of the first fully visible view.
- *
- * Note that bounds check is only performed in the current orientation. That means, if
- * LayoutManager is horizontal, it will only check the view's left and right edges.
- *
- * @return The adapter position of the first fully visible item or
- * {@link RecyclerView#NO_POSITION} if there aren't any visible items.
- * @see #findFirstVisibleItemPosition()
- * @see #findLastCompletelyVisibleItemPosition()
- */
- public int findFirstCompletelyVisibleItemPosition() {
- final View child = findOneVisibleChild(0, getChildCount(), true);
- return child == null ? NO_POSITION : getPosition(child);
- }
-
- /**
- * Returns the adapter position of the last visible view.
- *
- * Note that, this value is not affected by layout orientation or item order traversal.
- * ({@link #setReverseLayout(boolean)}). Views are sorted by their positions in the adapter,
- * not in the layout.
- *
- * If RecyclerView has item decorators, they will be considered in calculations as well.
- *
- * LayoutManager may pre-cache some views that are not necessarily visible. Those views
- * are ignored in this method.
- *
- * @return The adapter position of the last visible view or {@link RecyclerView#NO_POSITION} if
- * there aren't any visible items.
- * @see #findLastCompletelyVisibleItemPosition()
- * @see #findFirstVisibleItemPosition()
- */
- public int findLastVisibleItemPosition() {
- final View child = findOneVisibleChild(getChildCount() - 1, -1, false);
- return child == null ? NO_POSITION : getPosition(child);
- }
-
- /**
- * Returns the adapter position of the last fully visible view.
- *
- * Note that bounds check is only performed in the current orientation. That means, if
- * LayoutManager is horizontal, it will only check the view's left and right edges.
- *
- * @return The adapter position of the last fully visible view or
- * {@link RecyclerView#NO_POSITION} if there aren't any visible items.
- * @see #findLastVisibleItemPosition()
- * @see #findFirstCompletelyVisibleItemPosition()
- */
- public int findLastCompletelyVisibleItemPosition() {
- final View child = findOneVisibleChild(getChildCount() - 1, -1, true);
- return child == null ? NO_POSITION : getPosition(child);
- }
-
- View findOneVisibleChild(int fromIndex, int toIndex, boolean completelyVisible) {
- final int start = mOrientationHelper.getStartAfterPadding();
- final int end = mOrientationHelper.getEndAfterPadding();
- final int next = toIndex > fromIndex ? 1 : -1;
- for (int i = fromIndex; i != toIndex; i+=next) {
- final View child = getChildAt(i);
- final int childStart = mOrientationHelper.getDecoratedStart(child);
- final int childEnd = mOrientationHelper.getDecoratedEnd(child);
- if (childStart < end && childEnd > start) {
- if (completelyVisible) {
- if (childStart >= start && childEnd <= end) {
- return child;
- }
- } else {
- return child;
- }
- }
- }
- return null;
- }
-
- @Override
- public View onFocusSearchFailed(View focused, int focusDirection,
- RecyclerView.Recycler recycler, RecyclerView.State state) {
- resolveShouldLayoutReverse();
- if (getChildCount() == 0) {
- return null;
- }
-
- final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection);
- if (layoutDir == LayoutState.INVALID_LAYOUT) {
- return null;
- }
- final View referenceChild;
- if (layoutDir == LayoutState.LAYOUT_START) {
- referenceChild = findReferenceChildClosestToStart(state);
- } else {
- referenceChild = findReferenceChildClosestToEnd(state);
- }
- if (referenceChild == null) {
- if (DEBUG) {
- Log.d(TAG,
- "Cannot find a child with a valid position to be used for focus search.");
- }
- return null;
- }
- ensureLayoutState();
- final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace());
- updateLayoutState(layoutDir, maxScroll, false, state);
- mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;
- mLayoutState.mRecycle = false;
- fill(recycler, mLayoutState, state, true);
- final View nextFocus;
- if (layoutDir == LayoutState.LAYOUT_START) {
- nextFocus = getChildClosestToStart();
- } else {
- nextFocus = getChildClosestToEnd();
- }
- if (nextFocus == referenceChild || !nextFocus.isFocusable()) {
- return null;
- }
- return nextFocus;
- }
-
- /**
- * Used for debugging.
- * Logs the internal representation of children to default logger.
- */
- private void logChildren() {
- Log.d(TAG, "internal representation of views on the screen");
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- Log.d(TAG, "item " + getPosition(child) + ", coord:"
- + mOrientationHelper.getDecoratedStart(child));
- }
- Log.d(TAG, "==============");
- }
-
- /**
- * Used for debugging.
- * Validates that child views are laid out in correct order. This is important because rest of
- * the algorithm relies on this constraint.
- *
- * In default layout, child 0 should be closest to screen position 0 and last child should be
- * closest to position WIDTH or HEIGHT.
- * In reverse layout, last child should be closes to screen position 0 and first child should
- * be closest to position WIDTH or HEIGHT
- */
- void validateChildOrder() {
- Log.d(TAG, "validating child count " + getChildCount());
- if (getChildCount() < 1) {
- return;
- }
- int lastPos = getPosition(getChildAt(0));
- int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0));
- if (mShouldReverseLayout) {
- for (int i = 1; i < getChildCount(); i++) {
- View child = getChildAt(i);
- int pos = getPosition(child);
- int screenLoc = mOrientationHelper.getDecoratedStart(child);
- if (pos < lastPos) {
- logChildren();
- throw new RuntimeException("detected invalid position. loc invalid? " +
- (screenLoc < lastScreenLoc));
- }
- if (screenLoc > lastScreenLoc) {
- logChildren();
- throw new RuntimeException("detected invalid location");
- }
- }
- } else {
- for (int i = 1; i < getChildCount(); i++) {
- View child = getChildAt(i);
- int pos = getPosition(child);
- int screenLoc = mOrientationHelper.getDecoratedStart(child);
- if (pos < lastPos) {
- logChildren();
- throw new RuntimeException("detected invalid position. loc invalid? " +
- (screenLoc < lastScreenLoc));
- }
- if (screenLoc < lastScreenLoc) {
- logChildren();
- throw new RuntimeException("detected invalid location");
- }
- }
- }
- }
-
- @Override
- public boolean supportsPredictiveItemAnimations() {
- return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd;
- }
-
- /**
- * Helper class that keeps temporary state while {LayoutManager} is filling out the empty
- * space.
- */
- static class LayoutState {
-
- final static String TAG = "LinearLayoutManager#LayoutState";
-
- final static int LAYOUT_START = -1;
-
- final static int LAYOUT_END = 1;
-
- final static int INVALID_LAYOUT = Integer.MIN_VALUE;
-
- final static int ITEM_DIRECTION_HEAD = -1;
-
- final static int ITEM_DIRECTION_TAIL = 1;
-
- final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE;
-
- /**
- * We may not want to recycle children in some cases (e.g. layout)
- */
- boolean mRecycle = true;
-
- /**
- * Pixel offset where layout should start
- */
- int mOffset;
-
- /**
- * Number of pixels that we should fill, in the layout direction.
- */
- int mAvailable;
-
- /**
- * Current position on the adapter to get the next item.
- */
- int mCurrentPosition;
-
- /**
- * Defines the direction in which the data adapter is traversed.
- * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}
- */
- int mItemDirection;
-
- /**
- * Defines the direction in which the layout is filled.
- * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}
- */
- int mLayoutDirection;
-
- /**
- * Used when LayoutState is constructed in a scrolling state.
- * It should be set the amount of scrolling we can make without creating a new view.
- * Settings this is required for efficient view recycling.
- */
- int mScrollingOffset;
-
- /**
- * Used if you want to pre-layout items that are not yet visible.
- * The difference with {@link #mAvailable} is that, when recycling, distance laid out for
- * {@link #mExtra} is not considered to avoid recycling visible children.
- */
- int mExtra = 0;
-
- /**
- * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value
- * is set to true, we skip removed views since they should not be laid out in post layout
- * step.
- */
- boolean mIsPreLayout = false;
-
- /**
- * When LLM needs to layout particular views, it sets this list in which case, LayoutState
- * will only return views from this list and return null if it cannot find an item.
- */
- List mScrapList = null;
-
- /**
- * @return true if there are more items in the data adapter
- */
- boolean hasMore(RecyclerView.State state) {
- return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
- }
-
- /**
- * Gets the view for the next element that we should layout.
- * Also updates current item index to the next item, based on {@link #mItemDirection}
- *
- * @return The next element that we should layout.
- */
- View next(RecyclerView.Recycler recycler) {
- if (mScrapList != null) {
- return nextFromLimitedList();
- }
- final View view = recycler.getViewForPosition(mCurrentPosition);
- mCurrentPosition += mItemDirection;
- return view;
- }
-
- /**
- * Returns next item from limited list.
- *
- * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection
- *
- * @return View if an item in the current position or direction exists if not null.
- */
- private View nextFromLimitedList() {
- int size = mScrapList.size();
- RecyclerView.ViewHolder closest = null;
- int closestDistance = Integer.MAX_VALUE;
- for (int i = 0; i < size; i++) {
- RecyclerView.ViewHolder viewHolder = mScrapList.get(i);
- if (!mIsPreLayout && viewHolder.isRemoved()) {
- continue;
- }
- final int distance = (viewHolder.getPosition() - mCurrentPosition) * mItemDirection;
- if (distance < 0) {
- continue; // item is not in current direction
- }
- if (distance < closestDistance) {
- closest = viewHolder;
- closestDistance = distance;
- if (distance == 0) {
- break;
- }
- }
- }
- if (DEBUG) {
- Log.d(TAG, "layout from scrap. found view:?" + (closest != null));
- }
- if (closest != null) {
- mCurrentPosition = closest.getPosition() + mItemDirection;
- return closest.itemView;
- }
- return null;
- }
-
- void log() {
- Log.d(TAG, "avail:" + mAvailable + ", ind:" + mCurrentPosition + ", dir:" +
- mItemDirection + ", offset:" + mOffset + ", layoutDir:" + mLayoutDirection);
- }
- }
-
- static class SavedState implements Parcelable {
-
- int mAnchorPosition;
-
- int mAnchorOffset;
-
- boolean mAnchorLayoutFromEnd;
-
- public SavedState() {
-
- }
-
- SavedState(Parcel in) {
- mAnchorPosition = in.readInt();
- mAnchorOffset = in.readInt();
- mAnchorLayoutFromEnd = in.readInt() == 1;
- }
-
- public SavedState(SavedState other) {
- mAnchorPosition = other.mAnchorPosition;
- mAnchorOffset = other.mAnchorOffset;
- mAnchorLayoutFromEnd = other.mAnchorLayoutFromEnd;
- }
-
- boolean hasValidAnchor() {
- return mAnchorPosition >= 0;
- }
-
- void invalidateAnchor() {
- mAnchorPosition = NO_POSITION;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mAnchorPosition);
- dest.writeInt(mAnchorOffset);
- dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0);
- }
-
- public static final Creator CREATOR
- = new Creator() {
- @Override
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- /**
- * Simple data class to keep Anchor information
- */
- class AnchorInfo {
- int mPosition;
- int mCoordinate;
- boolean mLayoutFromEnd;
- void reset() {
- mPosition = NO_POSITION;
- mCoordinate = INVALID_OFFSET;
- mLayoutFromEnd = false;
- }
-
- /**
- * assigns anchor coordinate from the RecyclerView's padding depending on current
- * layoutFromEnd value
- */
- void assignCoordinateFromPadding() {
- mCoordinate = mLayoutFromEnd
- ? mOrientationHelper.getEndAfterPadding()
- : mOrientationHelper.getStartAfterPadding();
- }
-
- @Override
- public String toString() {
- return "AnchorInfo{" +
- "mPosition=" + mPosition +
- ", mCoordinate=" + mCoordinate +
- ", mLayoutFromEnd=" + mLayoutFromEnd +
- '}';
- }
-
- /**
- * Assign anchor position information from the provided view if it is valid as a reference
- * child.
- */
- public boolean assignFromViewIfValid(View child, RecyclerView.State state) {
- RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
- if (!lp.isItemRemoved() && lp.getViewPosition() >= 0
- && lp.getViewPosition() < state.getItemCount()) {
- assignFromView(child);
- return true;
- }
- return false;
- }
-
- public void assignFromView(View child) {
- if (mLayoutFromEnd) {
- mCoordinate = mOrientationHelper.getDecoratedEnd(child) +
- mOrientationHelper.getTotalSpaceChange();
- } else {
- mCoordinate = mOrientationHelper.getDecoratedStart(child);
- }
-
- mPosition = getPosition(child);
- }
- }
-
- protected static class LayoutChunkResult {
- public int mConsumed;
- public boolean mFinished;
- public boolean mIgnoreConsumed;
- public boolean mFocusable;
-
- void resetInternal() {
- mConsumed = 0;
- mFinished = false;
- mIgnoreConsumed = false;
- mFocusable = false;
- }
- }
-}
diff --git a/app/src/main/java/android/support/v7/widget/LinearSmoothScroller.java b/app/src/main/java/android/support/v7/widget/LinearSmoothScroller.java
deleted file mode 100644
index bdf2803d73..0000000000
--- a/app/src/main/java/android/support/v7/widget/LinearSmoothScroller.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-package android.support.v7.widget;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
-
-/**
- * {@link RecyclerView.SmoothScroller} implementation which uses
- * {@link LinearInterpolator} until the target position becames a child of
- * the RecyclerView and then uses
- * {@link DecelerateInterpolator} to slowly approach to target position.
- */
-abstract public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
-
- private static final String TAG = "LinearSmoothScroller";
-
- private static final boolean DEBUG = false;
-
- private static final float MILLISECONDS_PER_INCH = 25f;
-
- private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
-
- /**
- * Align child view's left or top with parent view's left or top
- *
- * @see #calculateDtToFit(int, int, int, int, int)
- * @see #calculateDxToMakeVisible(View, int)
- * @see #calculateDyToMakeVisible(View, int)
- */
- public static final int SNAP_TO_START = -1;
-
- /**
- * Align child view's right or bottom with parent view's right or bottom
- *
- * @see #calculateDtToFit(int, int, int, int, int)
- * @see #calculateDxToMakeVisible(View, int)
- * @see #calculateDyToMakeVisible(View, int)
- */
- public static final int SNAP_TO_END = 1;
-
- /**
- *
Decides if the child should be snapped from start or end, depending on where it
- * currently is in relation to its parent.
- *
For instance, if the view is virtually on the left of RecyclerView, using
- * {@code SNAP_TO_ANY} is the same as using {@code SNAP_TO_START}
- *
- * @see #calculateDtToFit(int, int, int, int, int)
- * @see #calculateDxToMakeVisible(View, int)
- * @see #calculateDyToMakeVisible(View, int)
- */
- public static final int SNAP_TO_ANY = 0;
-
- // Trigger a scroll to a further distance than TARGET_SEEK_SCROLL_DISTANCE_PX so that if target
- // view is not laid out until interim target position is reached, we can detect the case before
- // scrolling slows down and reschedule another interim target scroll
- private static final float TARGET_SEEK_EXTRA_SCROLL_RATIO = 1.2f;
-
- protected final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
-
- protected final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
-
- protected PointF mTargetVector;
-
- private final float MILLISECONDS_PER_PX;
-
- // Temporary variables to keep track of the interim scroll target. These values do not
- // point to a real item position, rather point to an estimated location pixels.
- protected int mInterimTargetDx = 0, mInterimTargetDy = 0;
-
- public LinearSmoothScroller(Context context) {
- MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onStart() {
-
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
- final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference());
- final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
- final int distance = (int) Math.sqrt(dx * dx + dy * dy);
- final int time = calculateTimeForDeceleration(distance);
- if (time > 0) {
- action.update(-dx, -dy, time, mDecelerateInterpolator);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onSeekTargetStep(int dx, int dy, RecyclerView.State state, Action action) {
- if (getChildCount() == 0) {
- stop();
- return;
- }
- if (DEBUG && mTargetVector != null
- && ((mTargetVector.x * dx < 0 || mTargetVector.y * dy < 0))) {
- throw new IllegalStateException("Scroll happened in the opposite direction"
- + " of the target. Some calculations are wrong");
- }
- mInterimTargetDx = clampApplyScroll(mInterimTargetDx, dx);
- mInterimTargetDy = clampApplyScroll(mInterimTargetDy, dy);
-
- if (mInterimTargetDx == 0 && mInterimTargetDy == 0) {
- updateActionForInterimTarget(action);
- } // everything is valid, keep going
-
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onStop() {
- mInterimTargetDx = mInterimTargetDy = 0;
- mTargetVector = null;
- }
-
- /**
- * Calculates the scroll speed.
- *
- * @param displayMetrics DisplayMetrics to be used for real dimension calculations
- * @return The time (in ms) it should take for each pixel. For instance, if returned value is
- * 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds.
- */
- protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
- return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
- }
-
- /**
- *
Calculates the time for deceleration so that transition from LinearInterpolator to
- * DecelerateInterpolator looks smooth.
- *
- * @param dx Distance to scroll
- * @return Time for DecelerateInterpolator to smoothly traverse the distance when transitioning
- * from LinearInterpolation
- */
- protected int calculateTimeForDeceleration(int dx) {
- // we want to cover same area with the linear interpolator for the first 10% of the
- // interpolation. After that, deceleration will take control.
- // area under curve (1-(1-x)^2) can be calculated as (1 - x/3) * x * x
- // which gives 0.100028 when x = .3356
- // this is why we divide linear scrolling time with .3356
- return (int) Math.ceil(calculateTimeForScrolling(dx) / .3356);
- }
-
- /**
- * Calculates the time it should take to scroll the given distance (in pixels)
- *
- * @param dx Distance in pixels that we want to scroll
- * @return Time in milliseconds
- * @see #calculateSpeedPerPixel(DisplayMetrics)
- */
- protected int calculateTimeForScrolling(int dx) {
- // In a case where dx is very small, rounding may return 0 although dx > 0.
- // To avoid that issue, ceil the result so that if dx > 0, we'll always return positive
- // time.
- return (int) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX);
- }
-
- /**
- * When scrolling towards a child view, this method defines whether we should align the left
- * or the right edge of the child with the parent RecyclerView.
- *
- * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
- * @see #SNAP_TO_START
- * @see #SNAP_TO_END
- * @see #SNAP_TO_ANY
- */
- protected int getHorizontalSnapPreference() {
- return mTargetVector == null || mTargetVector.x == 0 ? SNAP_TO_ANY :
- mTargetVector.x > 0 ? SNAP_TO_END : SNAP_TO_START;
- }
-
- /**
- * When scrolling towards a child view, this method defines whether we should align the top
- * or the bottom edge of the child with the parent RecyclerView.
- *
- * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
- * @see #SNAP_TO_START
- * @see #SNAP_TO_END
- * @see #SNAP_TO_ANY
- */
- protected int getVerticalSnapPreference() {
- return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
- mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
- }
-
- /**
- * When the target scroll position is not a child of the RecyclerView, this method calculates
- * a direction vector towards that child and triggers a smooth scroll.
- *
- * @see #computeScrollVectorForPosition(int)
- */
- protected void updateActionForInterimTarget(Action action) {
- // find an interim target position
- PointF scrollVector = computeScrollVectorForPosition(getTargetPosition());
- if (scrollVector == null || (scrollVector.x == 0 && scrollVector.y == 0)) {
- Log.e(TAG, "To support smooth scrolling, you should override \n"
- + "LayoutManager#computeScrollVectorForPosition.\n"
- + "Falling back to instant scroll");
- final int target = getTargetPosition();
- stop();
- instantScrollToPosition(target);
- return;
- }
- normalize(scrollVector);
- mTargetVector = scrollVector;
-
- mInterimTargetDx = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.x);
- mInterimTargetDy = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.y);
- final int time = calculateTimeForScrolling(TARGET_SEEK_SCROLL_DISTANCE_PX);
- // To avoid UI hiccups, trigger a smooth scroll to a distance little further than the
- // interim target. Since we track the distance travelled in onSeekTargetStep callback, it
- // won't actually scroll more than what we need.
- action.update((int) (mInterimTargetDx * TARGET_SEEK_EXTRA_SCROLL_RATIO)
- , (int) (mInterimTargetDy * TARGET_SEEK_EXTRA_SCROLL_RATIO)
- , (int) (time * TARGET_SEEK_EXTRA_SCROLL_RATIO), mLinearInterpolator);
- }
-
- private int clampApplyScroll(int tmpDt, int dt) {
- final int before = tmpDt;
- tmpDt -= dt;
- if (before * tmpDt <= 0) { // changed sign, reached 0 or was 0, reset
- return 0;
- }
- return tmpDt;
- }
-
- /**
- * Helper method for {@link #calculateDxToMakeVisible(View, int)} and
- * {@link #calculateDyToMakeVisible(View, int)}
- */
- public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
- snapPreference) {
- switch (snapPreference) {
- case SNAP_TO_START:
- return boxStart - viewStart;
- case SNAP_TO_END:
- return boxEnd - viewEnd;
- case SNAP_TO_ANY:
- final int dtStart = boxStart - viewStart;
- if (dtStart > 0) {
- return dtStart;
- }
- final int dtEnd = boxEnd - viewEnd;
- if (dtEnd < 0) {
- return dtEnd;
- }
- break;
- default:
- throw new IllegalArgumentException("snap preference should be one of the"
- + " constants defined in SmoothScroller, starting with SNAP_");
- }
- return 0;
- }
-
- /**
- * Calculates the vertical scroll amount necessary to make the given view fully visible
- * inside the RecyclerView.
- *
- * @param view The view which we want to make fully visible
- * @param snapPreference The edge which the view should snap to when entering the visible
- * area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
- * {@link #SNAP_TO_END}.
- * @return The vertical scroll amount necessary to make the view visible with the given
- * snap preference.
- */
- public int calculateDyToMakeVisible(View view, int snapPreference) {
- final RecyclerView.LayoutManager layoutManager = getLayoutManager();
- if (!layoutManager.canScrollVertically()) {
- return 0;
- }
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- final int top = layoutManager.getDecoratedTop(view) - params.topMargin;
- final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin;
- final int start = layoutManager.getPaddingTop();
- final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom();
- return calculateDtToFit(top, bottom, start, end, snapPreference);
- }
-
- /**
- * Calculates the horizontal scroll amount necessary to make the given view fully visible
- * inside the RecyclerView.
- *
- * @param view The view which we want to make fully visible
- * @param snapPreference The edge which the view should snap to when entering the visible
- * area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
- * {@link #SNAP_TO_END}
- * @return The vertical scroll amount necessary to make the view visible with the given
- * snap preference.
- */
- public int calculateDxToMakeVisible(View view, int snapPreference) {
- final RecyclerView.LayoutManager layoutManager = getLayoutManager();
- if (!layoutManager.canScrollHorizontally()) {
- return 0;
- }
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- final int left = layoutManager.getDecoratedLeft(view) - params.leftMargin;
- final int right = layoutManager.getDecoratedRight(view) + params.rightMargin;
- final int start = layoutManager.getPaddingLeft();
- final int end = layoutManager.getWidth() - layoutManager.getPaddingRight();
- return calculateDtToFit(left, right, start, end, snapPreference);
- }
-
- abstract public PointF computeScrollVectorForPosition(int targetPosition);
-}
diff --git a/app/src/main/java/android/support/v7/widget/OpReorderer.java b/app/src/main/java/android/support/v7/widget/OpReorderer.java
deleted file mode 100644
index 1a34cba3e2..0000000000
--- a/app/src/main/java/android/support/v7/widget/OpReorderer.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-package android.support.v7.widget;
-
-import android.support.v7.widget.AdapterHelper.UpdateOp;
-
-import java.util.List;
-
-import static android.support.v7.widget.AdapterHelper.UpdateOp.ADD;
-import static android.support.v7.widget.AdapterHelper.UpdateOp.MOVE;
-import static android.support.v7.widget.AdapterHelper.UpdateOp.REMOVE;
-import static android.support.v7.widget.AdapterHelper.UpdateOp.UPDATE;
-
-class OpReorderer {
-
- final Callback mCallback;
-
- public OpReorderer(Callback callback) {
- mCallback = callback;
- }
-
- void reorderOps(List ops) {
- // since move operations breaks continuity, their effects on ADD/RM are hard to handle.
- // we push them to the end of the list so that they can be handled easily.
- int badMove;
- while ((badMove = getLastMoveOutOfOrder(ops)) != -1) {
- swapMoveOp(ops, badMove, badMove + 1);
- }
- }
-
- private void swapMoveOp(List list, int badMove, int next) {
- final UpdateOp moveOp = list.get(badMove);
- final UpdateOp nextOp = list.get(next);
- switch (nextOp.cmd) {
- case REMOVE:
- swapMoveRemove(list, badMove, moveOp, next, nextOp);
- break;
- case ADD:
- swapMoveAdd(list, badMove, moveOp, next, nextOp);
- break;
- case UPDATE:
- swapMoveUpdate(list, badMove, moveOp, next, nextOp);
- break;
- }
- }
-
- void swapMoveRemove(List list, int movePos, UpdateOp moveOp,
- int removePos, UpdateOp removeOp) {
- UpdateOp extraRm = null;
- // check if move is nulled out by remove
- boolean revertedMove = false;
- final boolean moveIsBackwards;
-
- if (moveOp.positionStart < moveOp.itemCount) {
- moveIsBackwards = false;
- if (removeOp.positionStart == moveOp.positionStart
- && removeOp.itemCount == moveOp.itemCount - moveOp.positionStart) {
- revertedMove = true;
- }
- } else {
- moveIsBackwards = true;
- if (removeOp.positionStart == moveOp.itemCount + 1 &&
- removeOp.itemCount == moveOp.positionStart - moveOp.itemCount) {
- revertedMove = true;
- }
- }
-
- // going in reverse, first revert the effect of add
- if (moveOp.itemCount < removeOp.positionStart) {
- removeOp.positionStart--;
- } else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) {
- // move is removed.
- removeOp.itemCount --;
- moveOp.cmd = REMOVE;
- moveOp.itemCount = 1;
- if (removeOp.itemCount == 0) {
- list.remove(removePos);
- mCallback.recycleUpdateOp(removeOp);
- }
- // no need to swap, it is already a remove
- return;
- }
-
- // now affect of add is consumed. now apply effect of first remove
- if (moveOp.positionStart <= removeOp.positionStart) {
- removeOp.positionStart++;
- } else if (moveOp.positionStart < removeOp.positionStart + removeOp.itemCount) {
- final int remaining = removeOp.positionStart + removeOp.itemCount
- - moveOp.positionStart;
- extraRm = mCallback.obtainUpdateOp(REMOVE, moveOp.positionStart + 1, remaining);
- removeOp.itemCount = moveOp.positionStart - removeOp.positionStart;
- }
-
- // if effects of move is reverted by remove, we are done.
- if (revertedMove) {
- list.set(movePos, removeOp);
- list.remove(removePos);
- mCallback.recycleUpdateOp(moveOp);
- return;
- }
-
- // now find out the new locations for move actions
- if (moveIsBackwards) {
- if (extraRm != null) {
- if (moveOp.positionStart > extraRm.positionStart) {
- moveOp.positionStart -= extraRm.itemCount;
- }
- if (moveOp.itemCount > extraRm.positionStart) {
- moveOp.itemCount -= extraRm.itemCount;
- }
- }
- if (moveOp.positionStart > removeOp.positionStart) {
- moveOp.positionStart -= removeOp.itemCount;
- }
- if (moveOp.itemCount > removeOp.positionStart) {
- moveOp.itemCount -= removeOp.itemCount;
- }
- } else {
- if (extraRm != null) {
- if (moveOp.positionStart >= extraRm.positionStart) {
- moveOp.positionStart -= extraRm.itemCount;
- }
- if (moveOp.itemCount >= extraRm.positionStart) {
- moveOp.itemCount -= extraRm.itemCount;
- }
- }
- if (moveOp.positionStart >= removeOp.positionStart) {
- moveOp.positionStart -= removeOp.itemCount;
- }
- if (moveOp.itemCount >= removeOp.positionStart) {
- moveOp.itemCount -= removeOp.itemCount;
- }
- }
-
- list.set(movePos, removeOp);
- if (moveOp.positionStart != moveOp.itemCount) {
- list.set(removePos, moveOp);
- } else {
- list.remove(removePos);
- }
- if (extraRm != null) {
- list.add(movePos, extraRm);
- }
- }
-
- private void swapMoveAdd(List list, int move, UpdateOp moveOp, int add,
- UpdateOp addOp) {
- int offset = 0;
- // going in reverse, first revert the effect of add
- if (moveOp.itemCount < addOp.positionStart) {
- offset--;
- }
- if (moveOp.positionStart < addOp.positionStart) {
- offset++;
- }
- if (addOp.positionStart <= moveOp.positionStart) {
- moveOp.positionStart += addOp.itemCount;
- }
- if (addOp.positionStart <= moveOp.itemCount) {
- moveOp.itemCount += addOp.itemCount;
- }
- addOp.positionStart += offset;
- list.set(move, addOp);
- list.set(add, moveOp);
- }
-
- void swapMoveUpdate(List list, int move, UpdateOp moveOp, int update,
- UpdateOp updateOp) {
- UpdateOp extraUp1 = null;
- UpdateOp extraUp2 = null;
- // going in reverse, first revert the effect of add
- if (moveOp.itemCount < updateOp.positionStart) {
- updateOp.positionStart--;
- } else if (moveOp.itemCount < updateOp.positionStart + updateOp.itemCount) {
- // moved item is updated. add an update for it
- updateOp.itemCount--;
- extraUp1 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart, 1);
- }
- // now affect of add is consumed. now apply effect of first remove
- if (moveOp.positionStart <= updateOp.positionStart) {
- updateOp.positionStart++;
- } else if (moveOp.positionStart < updateOp.positionStart + updateOp.itemCount) {
- final int remaining = updateOp.positionStart + updateOp.itemCount
- - moveOp.positionStart;
- extraUp2 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart + 1, remaining);
- updateOp.itemCount -= remaining;
- }
- list.set(update, moveOp);
- if (updateOp.itemCount > 0) {
- list.set(move, updateOp);
- } else {
- list.remove(move);
- mCallback.recycleUpdateOp(updateOp);
- }
- if (extraUp1 != null) {
- list.add(move, extraUp1);
- }
- if (extraUp2 != null) {
- list.add(move, extraUp2);
- }
- }
-
- private int getLastMoveOutOfOrder(List list) {
- boolean foundNonMove = false;
- for (int i = list.size() - 1; i >= 0; i--) {
- final UpdateOp op1 = list.get(i);
- if (op1.cmd == MOVE) {
- if (foundNonMove) {
- return i;
- }
- } else {
- foundNonMove = true;
- }
- }
- return -1;
- }
-
- static interface Callback {
-
- UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount);
-
- void recycleUpdateOp(UpdateOp op);
- }
-}
diff --git a/app/src/main/java/android/support/v7/widget/OrientationHelper.java b/app/src/main/java/android/support/v7/widget/OrientationHelper.java
deleted file mode 100644
index a678d86de0..0000000000
--- a/app/src/main/java/android/support/v7/widget/OrientationHelper.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-package android.support.v7.widget;
-
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * Helper class for LayoutManagers to abstract measurements depending on the View's orientation.
- *
- * It is developed to easily support vertical and horizontal orientations in a LayoutManager but
- * can also be used to abstract calls around view bounds and child measurements with margins and
- * decorations.
- *
- * @see #createHorizontalHelper(RecyclerView.LayoutManager)
- * @see #createVerticalHelper(RecyclerView.LayoutManager)
- */
-public abstract class OrientationHelper {
-
- private static final int INVALID_SIZE = Integer.MIN_VALUE;
-
- protected final RecyclerView.LayoutManager mLayoutManager;
-
- public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
-
- public static final int VERTICAL = LinearLayout.VERTICAL;
-
- private int mLastTotalSpace = INVALID_SIZE;
-
- private OrientationHelper(RecyclerView.LayoutManager layoutManager) {
- mLayoutManager = layoutManager;
- }
-
- /**
- * Call this method after onLayout method is complete if state is NOT pre-layout.
- * This method records information like layout bounds that might be useful in the next layout
- * calculations.
- */
- public void onLayoutComplete() {
- mLastTotalSpace = getTotalSpace();
- }
-
- /**
- * Returns the layout space change between the previous layout pass and current layout pass.
- *
- * Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's
- * {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler,
- * RecyclerView.State)} method.
- *
- * @return The difference between the current total space and previous layout's total space.
- * @see #onLayoutComplete()
- */
- public int getTotalSpaceChange() {
- return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace;
- }
-
- /**
- * Returns the start of the view including its decoration and margin.
- *
- * For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left
- * decoration and 3px left margin, returned value will be 15px.
- *
- * @param view The view element to check
- * @return The first pixel of the element
- * @see #getDecoratedEnd(View)
- */
- public abstract int getDecoratedStart(View view);
-
- /**
- * Returns the end of the view including its decoration and margin.
- *
- * For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right
- * decoration and 3px right margin, returned value will be 205.
- *
- * @param view The view element to check
- * @return The last pixel of the element
- * @see #getDecoratedStart(View)
- */
- public abstract int getDecoratedEnd(View view);
-
- /**
- * Returns the space occupied by this View in the current orientation including decorations and
- * margins.
- *
- * @param view The view element to check
- * @return Total space occupied by this view
- * @see #getDecoratedMeasurementInOther(View)
- */
- public abstract int getDecoratedMeasurement(View view);
-
- /**
- * Returns the space occupied by this View in the perpendicular orientation including
- * decorations and margins.
- *
- * @param view The view element to check
- * @return Total space occupied by this view in the perpendicular orientation to current one
- * @see #getDecoratedMeasurement(View)
- */
- public abstract int getDecoratedMeasurementInOther(View view);
-
- /**
- * Returns the start position of the layout after the start padding is added.
- *
- * @return The very first pixel we can draw.
- */
- public abstract int getStartAfterPadding();
-
- /**
- * Returns the end position of the layout after the end padding is removed.
- *
- * @return The end boundary for this layout.
- */
- public abstract int getEndAfterPadding();
-
- /**
- * Returns the end position of the layout without taking padding into account.
- *
- * @return The end boundary for this layout without considering padding.
- */
- public abstract int getEnd();
-
- /**
- * Offsets all children's positions by the given amount.
- *
- * @param amount Value to add to each child's layout parameters
- */
- public abstract void offsetChildren(int amount);
-
- /**
- * Returns the total space to layout. This number is the difference between
- * {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
- *
- * @return Total space to layout children
- */
- public abstract int getTotalSpace();
-
- /**
- * Offsets the child in this orientation.
- *
- * @param view View to offset
- * @param offset offset amount
- */
- public abstract void offsetChild(View view, int offset);
-
- /**
- * Returns the padding at the end of the layout. For horizontal helper, this is the right
- * padding and for vertical helper, this is the bottom padding. This method does not check
- * whether the layout is RTL or not.
- *
- * @return The padding at the end of the layout.
- */
- public abstract int getEndPadding();
-
- /**
- * Creates an OrientationHelper for the given LayoutManager and orientation.
- *
- * @param layoutManager LayoutManager to attach to
- * @param orientation Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
- * @return A new OrientationHelper
- */
- public static OrientationHelper createOrientationHelper(
- RecyclerView.LayoutManager layoutManager, int orientation) {
- switch (orientation) {
- case HORIZONTAL:
- return createHorizontalHelper(layoutManager);
- case VERTICAL:
- return createVerticalHelper(layoutManager);
- }
- throw new IllegalArgumentException("invalid orientation");
- }
-
- /**
- * Creates a horizontal OrientationHelper for the given LayoutManager.
- *
- * @param layoutManager The LayoutManager to attach to.
- * @return A new OrientationHelper
- */
- public static OrientationHelper createHorizontalHelper(
- RecyclerView.LayoutManager layoutManager) {
- return new OrientationHelper(layoutManager) {
- @Override
- public int getEndAfterPadding() {
- return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();
- }
-
- @Override
- public int getEnd() {
- return mLayoutManager.getWidth();
- }
-
- @Override
- public void offsetChildren(int amount) {
- mLayoutManager.offsetChildrenHorizontal(amount);
- }
-
- @Override
- public int getStartAfterPadding() {
- return mLayoutManager.getPaddingLeft();
- }
-
- @Override
- public int getDecoratedMeasurement(View view) {
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
- + params.rightMargin;
- }
-
- @Override
- public int getDecoratedMeasurementInOther(View view) {
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
- + params.bottomMargin;
- }
-
- @Override
- public int getDecoratedEnd(View view) {
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- return mLayoutManager.getDecoratedRight(view) + params.rightMargin;
- }
-
- @Override
- public int getDecoratedStart(View view) {
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- return mLayoutManager.getDecoratedLeft(view) - params.leftMargin;
- }
-
- @Override
- public int getTotalSpace() {
- return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
- - mLayoutManager.getPaddingRight();
- }
-
- @Override
- public void offsetChild(View view, int offset) {
- view.offsetLeftAndRight(offset);
- }
-
- @Override
- public int getEndPadding() {
- return mLayoutManager.getPaddingRight();
- }
- };
- }
-
- /**
- * Creates a vertical OrientationHelper for the given LayoutManager.
- *
- * @param layoutManager The LayoutManager to attach to.
- * @return A new OrientationHelper
- */
- public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
- return new OrientationHelper(layoutManager) {
- @Override
- public int getEndAfterPadding() {
- return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom();
- }
-
- @Override
- public int getEnd() {
- return mLayoutManager.getHeight();
- }
-
- @Override
- public void offsetChildren(int amount) {
- mLayoutManager.offsetChildrenVertical(amount);
- }
-
- @Override
- public int getStartAfterPadding() {
- return mLayoutManager.getPaddingTop();
- }
-
- @Override
- public int getDecoratedMeasurement(View view) {
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
- + params.bottomMargin;
- }
-
- @Override
- public int getDecoratedMeasurementInOther(View view) {
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
- + params.rightMargin;
- }
-
- @Override
- public int getDecoratedEnd(View view) {
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin;
- }
-
- @Override
- public int getDecoratedStart(View view) {
- final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
- view.getLayoutParams();
- return mLayoutManager.getDecoratedTop(view) - params.topMargin;
- }
-
- @Override
- public int getTotalSpace() {
- return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
- - mLayoutManager.getPaddingBottom();
- }
-
- @Override
- public void offsetChild(View view, int offset) {
- view.offsetTopAndBottom(offset);
- }
-
- @Override
- public int getEndPadding() {
- return mLayoutManager.getPaddingBottom();
- }
- };
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/android/support/v7/widget/PositionMap.java b/app/src/main/java/android/support/v7/widget/PositionMap.java
deleted file mode 100644
index 3777937b72..0000000000
--- a/app/src/main/java/android/support/v7/widget/PositionMap.java
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-
-package android.support.v7.widget;
-
-import java.util.ArrayList;
-
-/**
- * Like a SparseArray, but with the ability to offset key ranges for bulk insertions/deletions.
- */
-class PositionMap implements Cloneable {
- private static final Object DELETED = new Object();
- private boolean mGarbage = false;
-
- private int[] mKeys;
- private Object[] mValues;
- private int mSize;
-
- /**
- * Creates a new SparseArray containing no mappings.
- */
- public PositionMap() {
- this(10);
- }
-
- /**
- * Creates a new PositionMap containing no mappings that will not
- * require any additional memory allocation to store the specified
- * number of mappings. If you supply an initial capacity of 0, the
- * sparse array will be initialized with a light-weight representation
- * not requiring any additional array allocations.
- */
- public PositionMap(int initialCapacity) {
- if (initialCapacity == 0) {
- mKeys = ContainerHelpers.EMPTY_INTS;
- mValues = ContainerHelpers.EMPTY_OBJECTS;
- } else {
- initialCapacity = idealIntArraySize(initialCapacity);
- mKeys = new int[initialCapacity];
- mValues = new Object[initialCapacity];
- }
- mSize = 0;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public PositionMap clone() {
- PositionMap clone = null;
- try {
- clone = (PositionMap) super.clone();
- clone.mKeys = mKeys.clone();
- clone.mValues = mValues.clone();
- } catch (CloneNotSupportedException cnse) {
- /* ignore */
- }
- return clone;
- }
-
- /**
- * Gets the Object mapped from the specified key, or null
- * if no such mapping has been made.
- */
- public E get(int key) {
- return get(key, null);
- }
-
- /**
- * Gets the Object mapped from the specified key, or the specified Object
- * if no such mapping has been made.
- */
- @SuppressWarnings("unchecked")
- public E get(int key, E valueIfKeyNotFound) {
- int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
- if (i < 0 || mValues[i] == DELETED) {
- return valueIfKeyNotFound;
- } else {
- return (E) mValues[i];
- }
- }
-
- /**
- * Removes the mapping from the specified key, if there was any.
- */
- public void delete(int key) {
- int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
- if (i >= 0) {
- if (mValues[i] != DELETED) {
- mValues[i] = DELETED;
- mGarbage = true;
- }
- }
- }
-
- /**
- * Alias for {@link #delete(int)}.
- */
- public void remove(int key) {
- delete(key);
- }
-
- /**
- * Removes the mapping at the specified index.
- */
- public void removeAt(int index) {
- if (mValues[index] != DELETED) {
- mValues[index] = DELETED;
- mGarbage = true;
- }
- }
-
- /**
- * Remove a range of mappings as a batch.
- *
- * @param index Index to begin at
- * @param size Number of mappings to remove
- */
- public void removeAtRange(int index, int size) {
- final int end = Math.min(mSize, index + size);
- for (int i = index; i < end; i++) {
- removeAt(i);
- }
- }
-
- public void insertKeyRange(int keyStart, int count) {
-
- }
-
- public void removeKeyRange(ArrayList removedItems, int keyStart, int count) {
-
- }
-
- private void gc() {
- // Log.e("SparseArray", "gc start with " + mSize);
-
- int n = mSize;
- int o = 0;
- int[] keys = mKeys;
- Object[] values = mValues;
-
- for (int i = 0; i < n; i++) {
- Object val = values[i];
-
- if (val != DELETED) {
- if (i != o) {
- keys[o] = keys[i];
- values[o] = val;
- values[i] = null;
- }
-
- o++;
- }
- }
-
- mGarbage = false;
- mSize = o;
-
- // Log.e("SparseArray", "gc end with " + mSize);
- }
-
- /**
- * Adds a mapping from the specified key to the specified value,
- * replacing the previous mapping from the specified key if there
- * was one.
- */
- public void put(int key, E value) {
- int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
-
- if (i >= 0) {
- mValues[i] = value;
- } else {
- i = ~i;
-
- if (i < mSize && mValues[i] == DELETED) {
- mKeys[i] = key;
- mValues[i] = value;
- return;
- }
-
- if (mGarbage && mSize >= mKeys.length) {
- gc();
-
- // Search again because indices may have changed.
- i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
- }
-
- if (mSize >= mKeys.length) {
- int n = idealIntArraySize(mSize + 1);
-
- int[] nkeys = new int[n];
- Object[] nvalues = new Object[n];
-
- // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
- System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
- System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
- mKeys = nkeys;
- mValues = nvalues;
- }
-
- if (mSize - i != 0) {
- // Log.e("SparseArray", "move " + (mSize - i));
- System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
- System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
- }
-
- mKeys[i] = key;
- mValues[i] = value;
- mSize++;
- }
- }
-
- /**
- * Returns the number of key-value mappings that this SparseArray
- * currently stores.
- */
- public int size() {
- if (mGarbage) {
- gc();
- }
-
- return mSize;
- }
-
- /**
- * Given an index in the range 0...size()-1, returns
- * the key from the indexth key-value mapping that this
- * SparseArray stores.
- */
- public int keyAt(int index) {
- if (mGarbage) {
- gc();
- }
-
- return mKeys[index];
- }
-
- /**
- * Given an index in the range 0...size()-1, returns
- * the value from the indexth key-value mapping that this
- * SparseArray stores.
- */
- @SuppressWarnings("unchecked")
- public E valueAt(int index) {
- if (mGarbage) {
- gc();
- }
-
- return (E) mValues[index];
- }
-
- /**
- * Given an index in the range 0...size()-1, sets a new
- * value for the indexth key-value mapping that this
- * SparseArray stores.
- */
- public void setValueAt(int index, E value) {
- if (mGarbage) {
- gc();
- }
-
- mValues[index] = value;
- }
-
- /**
- * Returns the index for which {@link #keyAt} would return the
- * specified key, or a negative number if the specified
- * key is not mapped.
- */
- public int indexOfKey(int key) {
- if (mGarbage) {
- gc();
- }
-
- return ContainerHelpers.binarySearch(mKeys, mSize, key);
- }
-
- /**
- * Returns an index for which {@link #valueAt} would return the
- * specified key, or a negative number if no keys map to the
- * specified value.
- *
Beware that this is a linear search, unlike lookups by key,
- * and that multiple keys can map to the same value and this will
- * find only one of them.
- *
Note also that unlike most collections' {@code indexOf} methods,
- * this method compares values using {@code ==} rather than {@code equals}.
- */
- public int indexOfValue(E value) {
- if (mGarbage) {
- gc();
- }
-
- for (int i = 0; i < mSize; i++)
- if (mValues[i] == value)
- return i;
-
- return -1;
- }
-
- /**
- * Removes all key-value mappings from this SparseArray.
- */
- public void clear() {
- int n = mSize;
- Object[] values = mValues;
-
- for (int i = 0; i < n; i++) {
- values[i] = null;
- }
-
- mSize = 0;
- mGarbage = false;
- }
-
- /**
- * Puts a key/value pair into the array, optimizing for the case where
- * the key is greater than all existing keys in the array.
- */
- public void append(int key, E value) {
- if (mSize != 0 && key <= mKeys[mSize - 1]) {
- put(key, value);
- return;
- }
-
- if (mGarbage && mSize >= mKeys.length) {
- gc();
- }
-
- int pos = mSize;
- if (pos >= mKeys.length) {
- int n = idealIntArraySize(pos + 1);
-
- int[] nkeys = new int[n];
- Object[] nvalues = new Object[n];
-
- // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
- System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
- System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
- mKeys = nkeys;
- mValues = nvalues;
- }
-
- mKeys[pos] = key;
- mValues[pos] = value;
- mSize = pos + 1;
- }
-
- /**
- * {@inheritDoc}
- *
- *
This implementation composes a string by iterating over its mappings. If
- * this map contains itself as a value, the string "(this Map)"
- * will appear in its place.
- */
- @Override
- public String toString() {
- if (size() <= 0) {
- return "{}";
- }
-
- StringBuilder buffer = new StringBuilder(mSize * 28);
- buffer.append('{');
- for (int i=0; i 0) {
- buffer.append(", ");
- }
- int key = keyAt(i);
- buffer.append(key);
- buffer.append('=');
- Object value = valueAt(i);
- if (value != this) {
- buffer.append(value);
- } else {
- buffer.append("(this Map)");
- }
- }
- buffer.append('}');
- return buffer.toString();
- }
-
- static int idealByteArraySize(int need) {
- for (int i = 4; i < 32; i++)
- if (need <= (1 << i) - 12)
- return (1 << i) - 12;
-
- return need;
- }
-
- static int idealBooleanArraySize(int need) {
- return idealByteArraySize(need);
- }
-
- static int idealShortArraySize(int need) {
- return idealByteArraySize(need * 2) / 2;
- }
-
- static int idealCharArraySize(int need) {
- return idealByteArraySize(need * 2) / 2;
- }
-
- static int idealIntArraySize(int need) {
- return idealByteArraySize(need * 4) / 4;
- }
-
- static int idealFloatArraySize(int need) {
- return idealByteArraySize(need * 4) / 4;
- }
-
- static int idealObjectArraySize(int need) {
- return idealByteArraySize(need * 4) / 4;
- }
-
- static int idealLongArraySize(int need) {
- return idealByteArraySize(need * 8) / 8;
- }
-
- static class ContainerHelpers {
- static final boolean[] EMPTY_BOOLEANS = new boolean[0];
- static final int[] EMPTY_INTS = new int[0];
- static final long[] EMPTY_LONGS = new long[0];
- static final Object[] EMPTY_OBJECTS = new Object[0];
-
- // This is Arrays.binarySearch(), but doesn't do any argument validation.
- static int binarySearch(int[] array, int size, int value) {
- int lo = 0;
- int hi = size - 1;
-
- while (lo <= hi) {
- final int mid = (lo + hi) >>> 1;
- final int midVal = array[mid];
-
- if (midVal < value) {
- lo = mid + 1;
- } else if (midVal > value) {
- hi = mid - 1;
- } else {
- return mid; // value found
- }
- }
- return ~lo; // value not present
- }
- }
-
-}
diff --git a/app/src/main/java/android/support/v7/widget/RecyclerView.java b/app/src/main/java/android/support/v7/widget/RecyclerView.java
deleted file mode 100644
index 2bfa3cee6d..0000000000
--- a/app/src/main/java/android/support/v7/widget/RecyclerView.java
+++ /dev/null
@@ -1,8303 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-
-package android.support.v7.widget;
-
-import android.content.Context;
-import android.database.Observable;
-import android.graphics.Canvas;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.Nullable;
-import android.support.v4.util.ArrayMap;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.VelocityTrackerCompat;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
-import android.support.v4.widget.EdgeEffectCompat;
-import android.support.v4.widget.ScrollerCompat;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.view.FocusFinder;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static android.support.v7.widget.AdapterHelper.Callback;
-import static android.support.v7.widget.AdapterHelper.UpdateOp;
-
-/**
- * A flexible view for providing a limited window into a large data set.
- *
- *
Glossary of terms:
- *
- *
- *
Adapter: A subclass of {@link Adapter} responsible for providing views
- * that represent items in a data set.
- *
Position: The position of a data item within an Adapter.
- *
Index: The index of an attached child view as used in a call to
- * {@link ViewGroup#getChildAt}. Contrast with Position.
- *
Binding: The process of preparing a child view to display data corresponding
- * to a position within the adapter.
- *
Recycle (view): A view previously used to display data for a specific adapter
- * position may be placed in a cache for later reuse to display the same type of data again
- * later. This can drastically improve performance by skipping initial layout inflation
- * or construction.
- *
Scrap (view): A child view that has entered into a temporarily detached
- * state during layout. Scrap views may be reused without becoming fully detached
- * from the parent RecyclerView, either unmodified if no rebinding is required or modified
- * by the adapter if the view was considered dirty.
- *
Dirty (view): A child view that must be rebound by the adapter before
- * being displayed.
- *
- */
-public class RecyclerView extends ViewGroup {
- private static final String TAG = "RecyclerView";
-
- private static final boolean DEBUG = false;
-
- /**
- * On Kitkat, there is a bug which prevents DisplayList from being invalidated if a View is two
- * levels deep(wrt to ViewHolder.itemView). DisplayList can be invalidated by setting
- * View's visibility to INVISIBLE when View is detached. On Kitkat, Recycler recursively
- * traverses itemView and invalidates display list for each ViewGroup that matches this
- * criteria.
- */
- private static final boolean FORCE_INVALIDATE_DISPLAY_LIST = Build.VERSION.SDK_INT == 19 ||
- Build.VERSION.SDK_INT == 20;
-
- private static final boolean DISPATCH_TEMP_DETACH = false;
- public static final int HORIZONTAL = 0;
- public static final int VERTICAL = 1;
-
- public static final int NO_POSITION = -1;
- public static final long NO_ID = -1;
- public static final int INVALID_TYPE = -1;
-
- private static final int MAX_SCROLL_DURATION = 2000;
-
- private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
-
- final Recycler mRecycler = new Recycler();
-
- private SavedState mPendingSavedState;
-
- AdapterHelper mAdapterHelper;
-
- ChildHelper mChildHelper;
-
- // we use this like a set
- final List mDisappearingViewsInLayoutPass = new ArrayList();
-
- /**
- * Prior to L, there is no way to query this variable which is why we override the setter and
- * track it here.
- */
- private boolean mClipToPadding;
-
- /**
- * Note: this Runnable is only ever posted if:
- * 1) We've been through first layout
- * 2) We know we have a fixed size (mHasFixedSize)
- * 3) We're attached
- */
- private final Runnable mUpdateChildViewsRunnable = new Runnable() {
- public void run() {
- if (!mAdapterHelper.hasPendingUpdates()) {
- return;
- }
- if (!mFirstLayoutComplete) {
- // a layout request will happen, we should not do layout here.
- return;
- }
- if (mDataSetHasChangedAfterLayout) {
- dispatchLayout();
- } else {
- eatRequestLayout();
- mAdapterHelper.preProcess();
- if (!mLayoutRequestEaten) {
- // We run this after pre-processing is complete so that ViewHolders have their
- // final adapter positions. No need to run it if a layout is already requested.
- rebindUpdatedViewHolders();
- }
- resumeRequestLayout(true);
- }
- }
- };
-
- private final Rect mTempRect = new Rect();
- private Adapter mAdapter;
- private LayoutManager mLayout;
- private RecyclerListener mRecyclerListener;
- private final ArrayList mItemDecorations = new ArrayList();
- private final ArrayList mOnItemTouchListeners =
- new ArrayList();
- private OnItemTouchListener mActiveOnItemTouchListener;
- private boolean mIsAttached;
- private boolean mHasFixedSize;
- private boolean mFirstLayoutComplete;
- private boolean mEatRequestLayout;
- private boolean mLayoutRequestEaten;
- private boolean mAdapterUpdateDuringMeasure;
- private final boolean mPostUpdatesOnAnimation;
- private final AccessibilityManager mAccessibilityManager;
-
- /**
- * Set to true when an adapter data set changed notification is received.
- * In that case, we cannot run any animations since we don't know what happened.
- */
- private boolean mDataSetHasChangedAfterLayout = false;
-
- /**
- * This variable is set to true during a dispatchLayout and/or scroll.
- * Some methods should not be called during these periods (e.g. adapter data change).
- * Doing so will create hard to find bugs so we better check it and throw an exception.
- *
- * @see #assertInLayoutOrScroll(String)
- * @see #assertNotInLayoutOrScroll(String)
- */
- private boolean mRunningLayoutOrScroll = false;
-
- private EdgeEffectCompat mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
-
- ItemAnimator mItemAnimator = new DefaultItemAnimator();
-
- private static final int INVALID_POINTER = -1;
-
- /**
- * The RecyclerView is not currently scrolling.
- * @see #getScrollState()
- */
- public static final int SCROLL_STATE_IDLE = 0;
-
- /**
- * The RecyclerView is currently being dragged by outside input such as user touch input.
- * @see #getScrollState()
- */
- public static final int SCROLL_STATE_DRAGGING = 1;
-
- /**
- * The RecyclerView is currently animating to a final position while not under
- * outside control.
- * @see #getScrollState()
- */
- public static final int SCROLL_STATE_SETTLING = 2;
-
- // Touch/scrolling handling
-
- private int mScrollState = SCROLL_STATE_IDLE;
- private int mScrollPointerId = INVALID_POINTER;
- private VelocityTracker mVelocityTracker;
- private int mInitialTouchX;
- private int mInitialTouchY;
- private int mLastTouchX;
- private int mLastTouchY;
- private final int mTouchSlop;
- private final int mMinFlingVelocity;
- private final int mMaxFlingVelocity;
-
- private final ViewFlinger mViewFlinger = new ViewFlinger();
-
- final State mState = new State();
-
- private OnScrollListener mScrollListener;
-
- // For use in item animations
- boolean mItemsAddedOrRemoved = false;
- boolean mItemsChanged = false;
- private ItemAnimator.ItemAnimatorListener mItemAnimatorListener =
- new ItemAnimatorRestoreListener();
- private boolean mPostedAnimatorRunner = false;
- private RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
- private Runnable mItemAnimatorRunner = new Runnable() {
- @Override
- public void run() {
- if (mItemAnimator != null) {
- mItemAnimator.runPendingAnimations();
- }
- mPostedAnimatorRunner = false;
- }
- };
-
- private static final Interpolator sQuinticInterpolator = new Interpolator() {
- public float getInterpolation(float t) {
- t -= 1.0f;
- return t * t * t * t * t + 1.0f;
- }
- };
-
- public RecyclerView(Context context) {
- this(context, null);
- }
-
- public RecyclerView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public RecyclerView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- setFocusableInTouchMode(true);
- final int version = Build.VERSION.SDK_INT;
- mPostUpdatesOnAnimation = version >= 16;
-
- final ViewConfiguration vc = ViewConfiguration.get(context);
- mTouchSlop = vc.getScaledTouchSlop();
- mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
- mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
- setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
-
- mItemAnimator.setListener(mItemAnimatorListener);
- initAdapterManager();
- initChildrenHelper();
- // If not explicitly specified this view is important for accessibility.
- if (ViewCompat.getImportantForAccessibility(this)
- == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- ViewCompat.setImportantForAccessibility(this,
- ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
- mAccessibilityManager = (AccessibilityManager) getContext()
- .getSystemService(Context.ACCESSIBILITY_SERVICE);
- setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
- }
-
- /**
- * Returns the accessibility delegate compatibility implementation used by the RecyclerView.
- * @return An instance of AccessibilityDelegateCompat used by RecyclerView
- */
- public RecyclerViewAccessibilityDelegate getCompatAccessibilityDelegate() {
- return mAccessibilityDelegate;
- }
-
- /**
- * Sets the accessibility delegate compatibility implementation used by RecyclerView.
- * @param accessibilityDelegate The accessibility delegate to be used by RecyclerView.
- */
- public void setAccessibilityDelegateCompat(
- RecyclerViewAccessibilityDelegate accessibilityDelegate) {
- mAccessibilityDelegate = accessibilityDelegate;
- ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
- }
-
- private void initChildrenHelper() {
- mChildHelper = new ChildHelper(new ChildHelper.Callback() {
- @Override
- public int getChildCount() {
- return RecyclerView.this.getChildCount();
- }
-
- @Override
- public void addView(View child, int index) {
- RecyclerView.this.addView(child, index);
- dispatchChildAttached(child);
- }
-
- @Override
- public int indexOfChild(View view) {
- return RecyclerView.this.indexOfChild(view);
- }
-
- @Override
- public void removeViewAt(int index) {
- final View child = RecyclerView.this.getChildAt(index);
- if (child != null) {
- dispatchChildDetached(child);
- }
- RecyclerView.this.removeViewAt(index);
- }
-
- @Override
- public View getChildAt(int offset) {
- return RecyclerView.this.getChildAt(offset);
- }
-
- @Override
- public void removeAllViews() {
- final int count = getChildCount();
- for (int i = 0; i < count; i ++) {
- dispatchChildDetached(getChildAt(i));
- }
- RecyclerView.this.removeAllViews();
- }
-
- @Override
- public ViewHolder getChildViewHolder(View view) {
- return getChildViewHolderInt(view);
- }
-
- @Override
- public void attachViewToParent(View child, int index,
- ViewGroup.LayoutParams layoutParams) {
- RecyclerView.this.attachViewToParent(child, index, layoutParams);
- }
-
- @Override
- public void detachViewFromParent(int offset) {
- RecyclerView.this.detachViewFromParent(offset);
- }
- });
- }
-
- void initAdapterManager() {
- mAdapterHelper = new AdapterHelper(new Callback() {
- @Override
- public ViewHolder findViewHolder(int position) {
- return findViewHolderForPosition(position, true);
- }
-
- @Override
- public void offsetPositionsForRemovingInvisible(int start, int count) {
- offsetPositionRecordsForRemove(start, count, true);
- mItemsAddedOrRemoved = true;
- mState.mDeletedInvisibleItemCountSincePreviousLayout += count;
- }
-
- @Override
- public void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount) {
- offsetPositionRecordsForRemove(positionStart, itemCount, false);
- mItemsAddedOrRemoved = true;
- }
-
- @Override
- public void markViewHoldersUpdated(int positionStart, int itemCount) {
- viewRangeUpdate(positionStart, itemCount);
- mItemsChanged = true;
- }
-
- @Override
- public void onDispatchFirstPass(UpdateOp op) {
- dispatchUpdate(op);
- }
-
- void dispatchUpdate(UpdateOp op) {
- switch (op.cmd) {
- case UpdateOp.ADD:
- mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
- break;
- case UpdateOp.REMOVE:
- mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
- break;
- case UpdateOp.UPDATE:
- mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount);
- break;
- case UpdateOp.MOVE:
- mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
- break;
- }
- }
-
- @Override
- public void onDispatchSecondPass(UpdateOp op) {
- dispatchUpdate(op);
- }
-
- @Override
- public void offsetPositionsForAdd(int positionStart, int itemCount) {
- offsetPositionRecordsForInsert(positionStart, itemCount);
- mItemsAddedOrRemoved = true;
- }
-
- @Override
- public void offsetPositionsForMove(int from, int to) {
- offsetPositionRecordsForMove(from, to);
- // should we create mItemsMoved ?
- mItemsAddedOrRemoved = true;
- }
- });
- }
-
- /**
- * RecyclerView can perform several optimizations if it can know in advance that changes in
- * adapter content cannot change the size of the RecyclerView itself.
- * If your use of RecyclerView falls into this category, set this to true.
- *
- * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
- */
- public void setHasFixedSize(boolean hasFixedSize) {
- mHasFixedSize = hasFixedSize;
- }
-
- /**
- * @return true if the app has specified that changes in adapter content cannot change
- * the size of the RecyclerView itself.
- */
- public boolean hasFixedSize() {
- return mHasFixedSize;
- }
-
- @Override
- public void setClipToPadding(boolean clipToPadding) {
- if (clipToPadding != mClipToPadding) {
- invalidateGlows();
- }
- mClipToPadding = clipToPadding;
- super.setClipToPadding(clipToPadding);
- if (mFirstLayoutComplete) {
- requestLayout();
- }
- }
-
- /**
- * Swaps the current adapter with the provided one. It is similar to
- * {@link #setAdapter(Adapter)} but assumes existing adapter and the new adapter uses the same
- * {@link ViewHolder} and does not clear the RecycledViewPool.
- *
- * Note that it still calls onAdapterChanged callbacks.
- *
- * @param adapter The new adapter to set, or null to set no adapter.
- * @param removeAndRecycleExistingViews If set to true, RecyclerView will recycle all existing
- * Views. If adapters have stable ids and/or you want to
- * animate the disappearing views, you may prefer to set
- * this to false.
- * @see #setAdapter(Adapter)
- */
- public void swapAdapter(Adapter adapter, boolean removeAndRecycleExistingViews) {
- setAdapterInternal(adapter, true, removeAndRecycleExistingViews);
- mDataSetHasChangedAfterLayout = true;
- requestLayout();
- }
- /**
- * Set a new adapter to provide child views on demand.
- *
- * When adapter is changed, all existing views are recycled back to the pool. If the pool has
- * only one adapter, it will be cleared.
- *
- * @param adapter The new adapter to set, or null to set no adapter.
- * @see #swapAdapter(Adapter, boolean)
- */
- public void setAdapter(Adapter adapter) {
- setAdapterInternal(adapter, false, true);
- requestLayout();
- }
-
- /**
- * Replaces the current adapter with the new one and triggers listeners.
- * @param adapter The new adapter
- * @param compatibleWithPrevious If true, the new adapter is using the same View Holders and
- * item types with the current adapter (helps us avoid cache
- * invalidation).
- * @param removeAndRecycleViews If true, we'll remove and recycle all existing views. If
- * compatibleWithPrevious is false, this parameter is ignored.
- */
- private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
- boolean removeAndRecycleViews) {
- if (mAdapter != null) {
- mAdapter.unregisterAdapterDataObserver(mObserver);
- }
- if (!compatibleWithPrevious || removeAndRecycleViews) {
- // end all running animations
- if (mItemAnimator != null) {
- mItemAnimator.endAnimations();
- }
- // Since animations are ended, mLayout.children should be equal to
- // recyclerView.children. This may not be true if item animator's end does not work as
- // expected. (e.g. not release children instantly). It is safer to use mLayout's child
- // count.
- if (mLayout != null) {
- mLayout.removeAndRecycleAllViews(mRecycler);
- mLayout.removeAndRecycleScrapInt(mRecycler, true);
- }
- }
- mAdapterHelper.reset();
- final Adapter oldAdapter = mAdapter;
- mAdapter = adapter;
- if (adapter != null) {
- adapter.registerAdapterDataObserver(mObserver);
- }
- if (mLayout != null) {
- mLayout.onAdapterChanged(oldAdapter, mAdapter);
- }
- mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
- mState.mStructureChanged = true;
- markKnownViewsInvalid();
- }
-
- /**
- * Retrieves the previously set adapter or null if no adapter is set.
- *
- * @return The previously set adapter
- * @see #setAdapter(Adapter)
- */
- public Adapter getAdapter() {
- return mAdapter;
- }
-
- /**
- * Register a listener that will be notified whenever a child view is recycled.
- *
- *
This listener will be called when a LayoutManager or the RecyclerView decides
- * that a child view is no longer needed. If an application associates expensive
- * or heavyweight data with item views, this may be a good place to release
- * or free those resources.
- *
- * @param listener Listener to register, or null to clear
- */
- public void setRecyclerListener(RecyclerListener listener) {
- mRecyclerListener = listener;
- }
-
- /**
- * Set the {@link LayoutManager} that this RecyclerView will use.
- *
- *
In contrast to other adapter-backed views such as {@link android.widget.ListView}
- * or {@link android.widget.GridView}, RecyclerView allows client code to provide custom
- * layout arrangements for child views. These arrangements are controlled by the
- * {@link LayoutManager}. A LayoutManager must be provided for RecyclerView to function.
- *
- *
Several default strategies are provided for common uses such as lists and grids.
- *
- * @param layout LayoutManager to use
- */
- public void setLayoutManager(LayoutManager layout) {
- if (layout == mLayout) {
- return;
- }
- // TODO We should do this switch a dispachLayout pass and animate children. There is a good
- // chance that LayoutManagers will re-use views.
- if (mLayout != null) {
- if (mIsAttached) {
- mLayout.onDetachedFromWindow(this, mRecycler);
- }
- mLayout.setRecyclerView(null);
- }
- mRecycler.clear();
- mChildHelper.removeAllViewsUnfiltered();
- mLayout = layout;
- if (layout != null) {
- if (layout.mRecyclerView != null) {
- throw new IllegalArgumentException("LayoutManager " + layout +
- " is already attached to a RecyclerView: " + layout.mRecyclerView);
- }
- mLayout.setRecyclerView(this);
- if (mIsAttached) {
- mLayout.onAttachedToWindow(this);
- }
- }
- requestLayout();
- }
-
- @Override
- protected Parcelable onSaveInstanceState() {
- SavedState state = new SavedState(super.onSaveInstanceState());
- if (mPendingSavedState != null) {
- state.copyFrom(mPendingSavedState);
- } else if (mLayout != null) {
- state.mLayoutState = mLayout.onSaveInstanceState();
- } else {
- state.mLayoutState = null;
- }
-
- return state;
- }
-
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- mPendingSavedState = (SavedState) state;
- super.onRestoreInstanceState(mPendingSavedState.getSuperState());
- if (mLayout != null && mPendingSavedState.mLayoutState != null) {
- mLayout.onRestoreInstanceState(mPendingSavedState.mLayoutState);
- }
- }
-
- /**
- * Adds a view to the animatingViews list.
- * mAnimatingViews holds the child views that are currently being kept around
- * purely for the purpose of being animated out of view. They are drawn as a regular
- * part of the child list of the RecyclerView, but they are invisible to the LayoutManager
- * as they are managed separately from the regular child views.
- * @param view The view to be removed
- */
- private void addAnimatingView(View view) {
- final boolean alreadyParented = view.getParent() == this;
- mRecycler.unscrapView(getChildViewHolder(view));
- if (!alreadyParented) {
- mChildHelper.addView(view, true);
- } else {
- mChildHelper.hide(view);
- }
- }
-
- /**
- * Removes a view from the animatingViews list.
- * @param view The view to be removed
- * @see #addAnimatingView(View)
- */
- private void removeAnimatingView(View view) {
- eatRequestLayout();
- if (mChildHelper.removeViewIfHidden(view)) {
- final ViewHolder viewHolder = getChildViewHolderInt(view);
- mRecycler.unscrapView(viewHolder);
- mRecycler.recycleViewHolderInternal(viewHolder);
- if (DEBUG) {
- Log.d(TAG, "after removing animated view: " + view + ", " + this);
- }
- }
- resumeRequestLayout(false);
- }
-
- /**
- * Return the {@link LayoutManager} currently responsible for
- * layout policy for this RecyclerView.
- *
- * @return The currently bound LayoutManager
- */
- public LayoutManager getLayoutManager() {
- return mLayout;
- }
-
- /**
- * Retrieve this RecyclerView's {@link RecycledViewPool}. This method will never return null;
- * if no pool is set for this view a new one will be created. See
- * {@link #setRecycledViewPool(RecycledViewPool) setRecycledViewPool} for more information.
- *
- * @return The pool used to store recycled item views for reuse.
- * @see #setRecycledViewPool(RecycledViewPool)
- */
- public RecycledViewPool getRecycledViewPool() {
- return mRecycler.getRecycledViewPool();
- }
-
- /**
- * Recycled view pools allow multiple RecyclerViews to share a common pool of scrap views.
- * This can be useful if you have multiple RecyclerViews with adapters that use the same
- * view types, for example if you have several data sets with the same kinds of item views
- * displayed by a {@link android.support.v4.view.ViewPager ViewPager}.
- *
- * @param pool Pool to set. If this parameter is null a new pool will be created and used.
- */
- public void setRecycledViewPool(RecycledViewPool pool) {
- mRecycler.setRecycledViewPool(pool);
- }
-
- /**
- * Sets a new {@link ViewCacheExtension} to be used by the Recycler.
- *
- * @param extension ViewCacheExtension to be used or null if you want to clear the existing one.
- *
- * @see {@link ViewCacheExtension#getViewForPositionAndType(Recycler, int, int)}
- */
- public void setViewCacheExtension(ViewCacheExtension extension) {
- mRecycler.setViewCacheExtension(extension);
- }
-
- /**
- * Set the number of offscreen views to retain before adding them to the potentially shared
- * {@link #getRecycledViewPool() recycled view pool}.
- *
- *
The offscreen view cache stays aware of changes in the attached adapter, allowing
- * a LayoutManager to reuse those views unmodified without needing to return to the adapter
- * to rebind them.
- *
- * @param size Number of views to cache offscreen before returning them to the general
- * recycled view pool
- */
- public void setItemViewCacheSize(int size) {
- mRecycler.setViewCacheSize(size);
- }
-
- /**
- * Return the current scrolling state of the RecyclerView.
- *
- * @return {@link #SCROLL_STATE_IDLE}, {@link #SCROLL_STATE_DRAGGING} or
- * {@link #SCROLL_STATE_SETTLING}
- */
- public int getScrollState() {
- return mScrollState;
- }
-
- private void setScrollState(int state) {
- if (state == mScrollState) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "setting scroll state to " + state + " from " + mScrollState, new Exception());
- }
- mScrollState = state;
- if (state != SCROLL_STATE_SETTLING) {
- stopScrollersInternal();
- }
- if (mScrollListener != null) {
- mScrollListener.onScrollStateChanged(this, state);
- }
- mLayout.onScrollStateChanged(state);
- }
-
- /**
- * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
- * affect both measurement and drawing of individual item views.
- *
- *
Item decorations are ordered. Decorations placed earlier in the list will
- * be run/queried/drawn first for their effects on item views. Padding added to views
- * will be nested; a padding added by an earlier decoration will mean further
- * item decorations in the list will be asked to draw/pad within the previous decoration's
- * given area.
- *
- * @param decor Decoration to add
- * @param index Position in the decoration chain to insert this decoration at. If this value
- * is negative the decoration will be added at the end.
- */
- public void addItemDecoration(ItemDecoration decor, int index) {
- if (mLayout != null) {
- mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or"
- + " layout");
- }
- if (mItemDecorations.isEmpty()) {
- setWillNotDraw(false);
- }
- if (index < 0) {
- mItemDecorations.add(decor);
- } else {
- mItemDecorations.add(index, decor);
- }
- markItemDecorInsetsDirty();
- requestLayout();
- }
-
- /**
- * Add an {@link ItemDecoration} to this RecyclerView. Item decorations can
- * affect both measurement and drawing of individual item views.
- *
- *
Item decorations are ordered. Decorations placed earlier in the list will
- * be run/queried/drawn first for their effects on item views. Padding added to views
- * will be nested; a padding added by an earlier decoration will mean further
- * item decorations in the list will be asked to draw/pad within the previous decoration's
- * given area.
- *
- * @param decor Decoration to add
- */
- public void addItemDecoration(ItemDecoration decor) {
- addItemDecoration(decor, -1);
- }
-
- /**
- * Remove an {@link ItemDecoration} from this RecyclerView.
- *
- *
The given decoration will no longer impact the measurement and drawing of
- * item views.
- *
- * @param decor Decoration to remove
- * @see #addItemDecoration(ItemDecoration)
- */
- public void removeItemDecoration(ItemDecoration decor) {
- if (mLayout != null) {
- mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll or"
- + " layout");
- }
- mItemDecorations.remove(decor);
- if (mItemDecorations.isEmpty()) {
- setWillNotDraw(ViewCompat.getOverScrollMode(this) == ViewCompat.OVER_SCROLL_NEVER);
- }
- markItemDecorInsetsDirty();
- requestLayout();
- }
-
- /**
- * Set a listener that will be notified of any changes in scroll state or position.
- *
- * @param listener Listener to set or null to clear
- */
- public void setOnScrollListener(OnScrollListener listener) {
- mScrollListener = listener;
- }
-
- /**
- * Convenience method to scroll to a certain position.
- *
- * RecyclerView does not implement scrolling logic, rather forwards the call to
- * {@link LayoutManager#scrollToPosition(int)}
- * @param position Scroll to this adapter position
- * @see LayoutManager#scrollToPosition(int)
- */
- public void scrollToPosition(int position) {
- stopScroll();
- mLayout.scrollToPosition(position);
- awakenScrollBars();
- }
-
- /**
- * Starts a smooth scroll to an adapter position.
- *
- * To support smooth scrolling, you must override
- * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
- * {@link SmoothScroller}.
- *
- * {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
- * provide a custom smooth scroll logic, override
- * {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
- * LayoutManager.
- *
- * @param position The adapter position to scroll to
- * @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
- */
- public void smoothScrollToPosition(int position) {
- mLayout.smoothScrollToPosition(this, mState, position);
- }
-
- @Override
- public void scrollTo(int x, int y) {
- throw new UnsupportedOperationException(
- "RecyclerView does not support scrolling to an absolute position.");
- }
-
- @Override
- public void scrollBy(int x, int y) {
- if (mLayout == null) {
- throw new IllegalStateException("Cannot scroll without a LayoutManager set. " +
- "Call setLayoutManager with a non-null argument.");
- }
- final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
- final boolean canScrollVertical = mLayout.canScrollVertically();
- if (canScrollHorizontal || canScrollVertical) {
- scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0);
- }
- }
-
- /**
- * Helper method reflect data changes to the state.
- *
- * Adapter changes during a scroll may trigger a crash because scroll assumes no data change
- * but data actually changed.
- *
- * This method consumes all deferred changes to avoid that case.
- */
- private void consumePendingUpdateOperations() {
- if (mAdapterHelper.hasPendingUpdates()) {
- mUpdateChildViewsRunnable.run();
- }
- }
-
- /**
- * Does not perform bounds checking. Used by internal methods that have already validated input.
- */
- void scrollByInternal(int x, int y) {
- int overscrollX = 0, overscrollY = 0;
- int hresult = 0, vresult = 0;
- consumePendingUpdateOperations();
- if (mAdapter != null) {
- eatRequestLayout();
- mRunningLayoutOrScroll = true;
- if (x != 0) {
- hresult = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
- overscrollX = x - hresult;
- }
- if (y != 0) {
- vresult = mLayout.scrollVerticallyBy(y, mRecycler, mState);
- overscrollY = y - vresult;
- }
- if (supportsChangeAnimations()) {
- // Fix up shadow views used by changing animations
- int count = mChildHelper.getChildCount();
- for (int i = 0; i < count; i++) {
- View view = mChildHelper.getChildAt(i);
- ViewHolder holder = getChildViewHolder(view);
- if (holder != null && holder.mShadowingHolder != null) {
- ViewHolder shadowingHolder = holder.mShadowingHolder;
- View shadowingView = shadowingHolder != null ? shadowingHolder.itemView : null;
- if (shadowingView != null) {
- int left = view.getLeft();
- int top = view.getTop();
- if (left != shadowingView.getLeft() || top != shadowingView.getTop()) {
- shadowingView.layout(left, top,
- left + shadowingView.getWidth(),
- top + shadowingView.getHeight());
- }
- }
- }
- }
- }
- mRunningLayoutOrScroll = false;
- resumeRequestLayout(false);
- }
- if (!mItemDecorations.isEmpty()) {
- invalidate();
- }
- if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
- considerReleasingGlowsOnScroll(x, y);
- pullGlows(overscrollX, overscrollY);
- }
- if (hresult != 0 || vresult != 0) {
- onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
- if (mScrollListener != null) {
- mScrollListener.onScrolled(this, hresult, vresult);
- }
- }
- if (!awakenScrollBars()) {
- invalidate();
- }
- }
-
- /**
- *
Compute the horizontal offset of the horizontal scrollbar's thumb within the horizontal
- * range. This value is used to compute the length of the thumb within the scrollbar's track.
- *
- *
- *
The range is expressed in arbitrary units that must be the same as the units used by
- * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollExtent()}.
- *
- *
Default implementation returns 0.
- *
- *
If you want to support scroll bars, override
- * {@link LayoutManager#computeHorizontalScrollOffset(State)} in your
- * LayoutManager.
- *
- * @return The horizontal offset of the scrollbar's thumb
- * @see LayoutManager#computeHorizontalScrollOffset
- * (RecyclerView.Adapter)
- */
- @Override
- protected int computeHorizontalScrollOffset() {
- return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollOffset(mState)
- : 0;
- }
-
- /**
- *
Compute the horizontal extent of the horizontal scrollbar's thumb within the
- * horizontal range. This value is used to compute the length of the thumb within the
- * scrollbar's track.
- *
- *
The range is expressed in arbitrary units that must be the same as the units used by
- * {@link #computeHorizontalScrollRange()} and {@link #computeHorizontalScrollOffset()}.
- *
- *
Default implementation returns 0.
- *
- *
If you want to support scroll bars, override
- * {@link LayoutManager#computeHorizontalScrollExtent(State)} in your
- * LayoutManager.
- *
- * @return The horizontal extent of the scrollbar's thumb
- * @see LayoutManager#computeHorizontalScrollExtent(State)
- */
- @Override
- protected int computeHorizontalScrollExtent() {
- return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollExtent(mState) : 0;
- }
-
- /**
- *
Compute the horizontal range that the horizontal scrollbar represents.
- *
- *
The range is expressed in arbitrary units that must be the same as the units used by
- * {@link #computeHorizontalScrollExtent()} and {@link #computeHorizontalScrollOffset()}.
- *
- *
Default implementation returns 0.
- *
- *
If you want to support scroll bars, override
- * {@link LayoutManager#computeHorizontalScrollRange(State)} in your
- * LayoutManager.
- *
- * @return The total horizontal range represented by the vertical scrollbar
- * @see LayoutManager#computeHorizontalScrollRange(State)
- */
- @Override
- protected int computeHorizontalScrollRange() {
- return mLayout.canScrollHorizontally() ? mLayout.computeHorizontalScrollRange(mState) : 0;
- }
-
- /**
- *
Compute the vertical offset of the vertical scrollbar's thumb within the vertical range.
- * This value is used to compute the length of the thumb within the scrollbar's track.
- *
- *
The range is expressed in arbitrary units that must be the same as the units used by
- * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollExtent()}.
- *
- *
Default implementation returns 0.
- *
- *
If you want to support scroll bars, override
- * {@link LayoutManager#computeVerticalScrollOffset(State)} in your
- * LayoutManager.
- *
- * @return The vertical offset of the scrollbar's thumb
- * @see LayoutManager#computeVerticalScrollOffset
- * (RecyclerView.Adapter)
- */
- @Override
- protected int computeVerticalScrollOffset() {
- return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollOffset(mState) : 0;
- }
-
- /**
- *
Compute the vertical extent of the vertical scrollbar's thumb within the vertical range.
- * This value is used to compute the length of the thumb within the scrollbar's track.
- *
- *
The range is expressed in arbitrary units that must be the same as the units used by
- * {@link #computeVerticalScrollRange()} and {@link #computeVerticalScrollOffset()}.
- *
- *
Default implementation returns 0.
- *
- *
If you want to support scroll bars, override
- * {@link LayoutManager#computeVerticalScrollExtent(State)} in your
- * LayoutManager.
- *
- * @return The vertical extent of the scrollbar's thumb
- * @see LayoutManager#computeVerticalScrollExtent(State)
- */
- @Override
- protected int computeVerticalScrollExtent() {
- return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollExtent(mState) : 0;
- }
-
- /**
- *
Compute the vertical range that the vertical scrollbar represents.
- *
- *
The range is expressed in arbitrary units that must be the same as the units used by
- * {@link #computeVerticalScrollExtent()} and {@link #computeVerticalScrollOffset()}.
- *
- *
Default implementation returns 0.
- *
- *
If you want to support scroll bars, override
- * {@link LayoutManager#computeVerticalScrollRange(State)} in your
- * LayoutManager.
- *
- * @return The total vertical range represented by the vertical scrollbar
- * @see LayoutManager#computeVerticalScrollRange(State)
- */
- @Override
- protected int computeVerticalScrollRange() {
- return mLayout.canScrollVertically() ? mLayout.computeVerticalScrollRange(mState) : 0;
- }
-
-
- void eatRequestLayout() {
- if (!mEatRequestLayout) {
- mEatRequestLayout = true;
- mLayoutRequestEaten = false;
- }
- }
-
- void resumeRequestLayout(boolean performLayoutChildren) {
- if (mEatRequestLayout) {
- if (performLayoutChildren && mLayoutRequestEaten &&
- mLayout != null && mAdapter != null) {
- dispatchLayout();
- }
- mEatRequestLayout = false;
- mLayoutRequestEaten = false;
- }
- }
-
- /**
- * Animate a scroll by the given amount of pixels along either axis.
- *
- * @param dx Pixels to scroll horizontally
- * @param dy Pixels to scroll vertically
- */
- public void smoothScrollBy(int dx, int dy) {
- if (dx != 0 || dy != 0) {
- mViewFlinger.smoothScrollBy(dx, dy);
- }
- }
-
- /**
- * Begin a standard fling with an initial velocity along each axis in pixels per second.
- * If the velocity given is below the system-defined minimum this method will return false
- * and no fling will occur.
- *
- * @param velocityX Initial horizontal velocity in pixels per second
- * @param velocityY Initial vertical velocity in pixels per second
- * @return true if the fling was started, false if the velocity was too low to fling
- */
- public boolean fling(int velocityX, int velocityY) {
- if (Math.abs(velocityX) < mMinFlingVelocity) {
- velocityX = 0;
- }
- if (Math.abs(velocityY) < mMinFlingVelocity) {
- velocityY = 0;
- }
- velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
- velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
- if (velocityX != 0 || velocityY != 0) {
- mViewFlinger.fling(velocityX, velocityY);
- return true;
- }
- return false;
- }
-
- /**
- * Stop any current scroll in progress, such as one started by
- * {@link #smoothScrollBy(int, int)}, {@link #fling(int, int)} or a touch-initiated fling.
- */
- public void stopScroll() {
- setScrollState(SCROLL_STATE_IDLE);
- stopScrollersInternal();
- }
-
- /**
- * Similar to {@link #stopScroll()} but does not set the state.
- */
- private void stopScrollersInternal() {
- mViewFlinger.stop();
- mLayout.stopSmoothScroller();
- }
-
- /**
- * Apply a pull to relevant overscroll glow effects
- */
- private void pullGlows(int overscrollX, int overscrollY) {
- if (overscrollX < 0) {
- ensureLeftGlow();
- mLeftGlow.onPull(-overscrollX / (float) getWidth());
- } else if (overscrollX > 0) {
- ensureRightGlow();
- mRightGlow.onPull(overscrollX / (float) getWidth());
- }
-
- if (overscrollY < 0) {
- ensureTopGlow();
- mTopGlow.onPull(-overscrollY / (float) getHeight());
- } else if (overscrollY > 0) {
- ensureBottomGlow();
- mBottomGlow.onPull(overscrollY / (float) getHeight());
- }
-
- if (overscrollX != 0 || overscrollY != 0) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- private void releaseGlows() {
- boolean needsInvalidate = false;
- if (mLeftGlow != null) needsInvalidate = mLeftGlow.onRelease();
- if (mTopGlow != null) needsInvalidate |= mTopGlow.onRelease();
- if (mRightGlow != null) needsInvalidate |= mRightGlow.onRelease();
- if (mBottomGlow != null) needsInvalidate |= mBottomGlow.onRelease();
- if (needsInvalidate) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- private void considerReleasingGlowsOnScroll(int dx, int dy) {
- boolean needsInvalidate = false;
- if (mLeftGlow != null && !mLeftGlow.isFinished() && dx > 0) {
- needsInvalidate = mLeftGlow.onRelease();
- }
- if (mRightGlow != null && !mRightGlow.isFinished() && dx < 0) {
- needsInvalidate |= mRightGlow.onRelease();
- }
- if (mTopGlow != null && !mTopGlow.isFinished() && dy > 0) {
- needsInvalidate |= mTopGlow.onRelease();
- }
- if (mBottomGlow != null && !mBottomGlow.isFinished() && dy < 0) {
- needsInvalidate |= mBottomGlow.onRelease();
- }
- if (needsInvalidate) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- void absorbGlows(int velocityX, int velocityY) {
- if (velocityX < 0) {
- ensureLeftGlow();
- mLeftGlow.onAbsorb(-velocityX);
- } else if (velocityX > 0) {
- ensureRightGlow();
- mRightGlow.onAbsorb(velocityX);
- }
-
- if (velocityY < 0) {
- ensureTopGlow();
- mTopGlow.onAbsorb(-velocityY);
- } else if (velocityY > 0) {
- ensureBottomGlow();
- mBottomGlow.onAbsorb(velocityY);
- }
-
- if (velocityX != 0 || velocityY != 0) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- void ensureLeftGlow() {
- if (mLeftGlow != null) {
- return;
- }
- mLeftGlow = new EdgeEffectCompat(getContext());
- if (mClipToPadding) {
- mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
- getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
- } else {
- mLeftGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
- }
- }
-
- void ensureRightGlow() {
- if (mRightGlow != null) {
- return;
- }
- mRightGlow = new EdgeEffectCompat(getContext());
- if (mClipToPadding) {
- mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
- getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
- } else {
- mRightGlow.setSize(getMeasuredHeight(), getMeasuredWidth());
- }
- }
-
- void ensureTopGlow() {
- if (mTopGlow != null) {
- return;
- }
- mTopGlow = new EdgeEffectCompat(getContext());
- if (mClipToPadding) {
- mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
- getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
- } else {
- mTopGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
- }
-
- }
-
- void ensureBottomGlow() {
- if (mBottomGlow != null) {
- return;
- }
- mBottomGlow = new EdgeEffectCompat(getContext());
- if (mClipToPadding) {
- mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
- getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
- } else {
- mBottomGlow.setSize(getMeasuredWidth(), getMeasuredHeight());
- }
- }
-
- void invalidateGlows() {
- mLeftGlow = mRightGlow = mTopGlow = mBottomGlow = null;
- }
-
- // Focus handling
-
- @Override
- public View focusSearch(View focused, int direction) {
- View result = mLayout.onInterceptFocusSearch(focused, direction);
- if (result != null) {
- return result;
- }
- final FocusFinder ff = FocusFinder.getInstance();
- result = ff.findNextFocus(this, focused, direction);
- if (result == null && mAdapter != null) {
- eatRequestLayout();
- result = mLayout.onFocusSearchFailed(focused, direction, mRecycler, mState);
- resumeRequestLayout(false);
- }
- return result != null ? result : super.focusSearch(focused, direction);
- }
-
- @Override
- public void requestChildFocus(View child, View focused) {
- if (!mLayout.onRequestChildFocus(this, mState, child, focused) && focused != null) {
- mTempRect.set(0, 0, focused.getWidth(), focused.getHeight());
- offsetDescendantRectToMyCoords(focused, mTempRect);
- offsetRectIntoDescendantCoords(child, mTempRect);
- requestChildRectangleOnScreen(child, mTempRect, !mFirstLayoutComplete);
- }
- super.requestChildFocus(child, focused);
- }
-
- @Override
- public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
- return mLayout.requestChildRectangleOnScreen(this, child, rect, immediate);
- }
-
- @Override
- public void addFocusables(ArrayList views, int direction, int focusableMode) {
- if (!mLayout.onAddFocusables(this, views, direction, focusableMode)) {
- super.addFocusables(views, direction, focusableMode);
- }
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mIsAttached = true;
- mFirstLayoutComplete = false;
- if (mLayout != null) {
- mLayout.onAttachedToWindow(this);
- }
- mPostedAnimatorRunner = false;
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mItemAnimator != null) {
- mItemAnimator.endAnimations();
- }
- mFirstLayoutComplete = false;
-
- stopScroll();
- mIsAttached = false;
- if (mLayout != null) {
- mLayout.onDetachedFromWindow(this, mRecycler);
- }
- removeCallbacks(mItemAnimatorRunner);
- }
-
- /**
- * Checks if RecyclerView is in the middle of a layout or scroll and throws an
- * {@link IllegalStateException} if it is not.
- *
- * @param message The message for the exception. Can be null.
- * @see #assertNotInLayoutOrScroll(String)
- */
- void assertInLayoutOrScroll(String message) {
- if (!mRunningLayoutOrScroll) {
- if (message == null) {
- throw new IllegalStateException("Cannot call this method unless RecyclerView is "
- + "computing a layout or scrolling");
- }
- throw new IllegalStateException(message);
-
- }
- }
-
- /**
- * Checks if RecyclerView is in the middle of a layout or scroll and throws an
- * {@link IllegalStateException} if it is.
- *
- * @param message The message for the exception. Can be null.
- * @see #assertInLayoutOrScroll(String)
- */
- void assertNotInLayoutOrScroll(String message) {
- if (mRunningLayoutOrScroll) {
- if (message == null) {
- throw new IllegalStateException("Cannot call this method while RecyclerView is "
- + "computing a layout or scrolling");
- }
- throw new IllegalStateException(message);
- }
- }
-
- /**
- * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
- * to child views or this view's standard scrolling behavior.
- *
- *
Client code may use listeners to implement item manipulation behavior. Once a listener
- * returns true from
- * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
- * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
- * for each incoming MotionEvent until the end of the gesture.
- *
- * @param listener Listener to add
- */
- public void addOnItemTouchListener(OnItemTouchListener listener) {
- mOnItemTouchListeners.add(listener);
- }
-
- /**
- * Remove an {@link OnItemTouchListener}. It will no longer be able to intercept touch events.
- *
- * @param listener Listener to remove
- */
- public void removeOnItemTouchListener(OnItemTouchListener listener) {
- mOnItemTouchListeners.remove(listener);
- if (mActiveOnItemTouchListener == listener) {
- mActiveOnItemTouchListener = null;
- }
- }
-
- private boolean dispatchOnItemTouchIntercept(MotionEvent e) {
- final int action = e.getAction();
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_DOWN) {
- mActiveOnItemTouchListener = null;
- }
-
- final int listenerCount = mOnItemTouchListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
- if (listener.onInterceptTouchEvent(this, e) && action != MotionEvent.ACTION_CANCEL) {
- mActiveOnItemTouchListener = listener;
- return true;
- }
- }
- return false;
- }
-
- private boolean dispatchOnItemTouch(MotionEvent e) {
- final int action = e.getAction();
- if (mActiveOnItemTouchListener != null) {
- if (action == MotionEvent.ACTION_DOWN) {
- // Stale state from a previous gesture, we're starting a new one. Clear it.
- mActiveOnItemTouchListener = null;
- } else {
- mActiveOnItemTouchListener.onTouchEvent(this, e);
- if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
- // Clean up for the next gesture.
- mActiveOnItemTouchListener = null;
- }
- return true;
- }
- }
-
- // Listeners will have already received the ACTION_DOWN via dispatchOnItemTouchIntercept
- // as called from onInterceptTouchEvent; skip it.
- if (action != MotionEvent.ACTION_DOWN) {
- final int listenerCount = mOnItemTouchListeners.size();
- for (int i = 0; i < listenerCount; i++) {
- final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
- if (listener.onInterceptTouchEvent(this, e)) {
- mActiveOnItemTouchListener = listener;
- return true;
- }
- }
- }
- return false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent e) {
- if (dispatchOnItemTouchIntercept(e)) {
- cancelTouch();
- return true;
- }
-
- final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
- final boolean canScrollVertically = mLayout.canScrollVertically();
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(e);
-
- final int action = MotionEventCompat.getActionMasked(e);
- final int actionIndex = MotionEventCompat.getActionIndex(e);
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
- mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
- mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
-
- if (mScrollState == SCROLL_STATE_SETTLING) {
- getParent().requestDisallowInterceptTouchEvent(true);
- setScrollState(SCROLL_STATE_DRAGGING);
- }
- break;
-
- case MotionEventCompat.ACTION_POINTER_DOWN:
- mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
- mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
- mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
- break;
-
- case MotionEvent.ACTION_MOVE: {
- final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
- if (index < 0) {
- Log.e(TAG, "Error processing scroll; pointer index for id " +
- mScrollPointerId + " not found. Did any MotionEvents get skipped?");
- return false;
- }
-
- final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
- final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
- if (mScrollState != SCROLL_STATE_DRAGGING) {
- final int dx = x - mInitialTouchX;
- final int dy = y - mInitialTouchY;
- boolean startScroll = false;
- if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
- mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
- startScroll = true;
- }
- if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
- mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
- startScroll = true;
- }
- if (startScroll) {
- getParent().requestDisallowInterceptTouchEvent(true);
- setScrollState(SCROLL_STATE_DRAGGING);
- }
- }
- } break;
-
- case MotionEventCompat.ACTION_POINTER_UP: {
- onPointerUp(e);
- } break;
-
- case MotionEvent.ACTION_UP: {
- mVelocityTracker.clear();
- } break;
-
- case MotionEvent.ACTION_CANCEL: {
- cancelTouch();
- }
- }
- return mScrollState == SCROLL_STATE_DRAGGING;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent e) {
- if (dispatchOnItemTouch(e)) {
- cancelTouch();
- return true;
- }
-
- final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
- final boolean canScrollVertically = mLayout.canScrollVertically();
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(e);
-
- final int action = MotionEventCompat.getActionMasked(e);
- final int actionIndex = MotionEventCompat.getActionIndex(e);
-
- switch (action) {
- case MotionEvent.ACTION_DOWN: {
- mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
- mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
- mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
- } break;
-
- case MotionEventCompat.ACTION_POINTER_DOWN: {
- mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
- mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
- mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
- } break;
-
- case MotionEvent.ACTION_MOVE: {
- final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
- if (index < 0) {
- Log.e(TAG, "Error processing scroll; pointer index for id " +
- mScrollPointerId + " not found. Did any MotionEvents get skipped?");
- return false;
- }
-
- final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
- final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
- if (mScrollState != SCROLL_STATE_DRAGGING) {
- final int dx = x - mInitialTouchX;
- final int dy = y - mInitialTouchY;
- boolean startScroll = false;
- if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
- mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
- startScroll = true;
- }
- if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
- mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
- startScroll = true;
- }
- if (startScroll) {
- getParent().requestDisallowInterceptTouchEvent(true);
- setScrollState(SCROLL_STATE_DRAGGING);
- }
- }
- if (mScrollState == SCROLL_STATE_DRAGGING) {
- final int dx = x - mLastTouchX;
- final int dy = y - mLastTouchY;
- scrollByInternal(canScrollHorizontally ? -dx : 0,
- canScrollVertically ? -dy : 0);
- }
- mLastTouchX = x;
- mLastTouchY = y;
- } break;
-
- case MotionEventCompat.ACTION_POINTER_UP: {
- onPointerUp(e);
- } break;
-
- case MotionEvent.ACTION_UP: {
- mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
- final float xvel = canScrollHorizontally ?
- -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
- final float yvel = canScrollVertically ?
- -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
- if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
- setScrollState(SCROLL_STATE_IDLE);
- }
- mVelocityTracker.clear();
- releaseGlows();
- } break;
-
- case MotionEvent.ACTION_CANCEL: {
- cancelTouch();
- } break;
- }
-
- return true;
- }
-
- private void cancelTouch() {
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
- releaseGlows();
- setScrollState(SCROLL_STATE_IDLE);
- }
-
- private void onPointerUp(MotionEvent e) {
- final int actionIndex = MotionEventCompat.getActionIndex(e);
- if (MotionEventCompat.getPointerId(e, actionIndex) == mScrollPointerId) {
- // Pick a new pointer to pick up the slack.
- final int newIndex = actionIndex == 0 ? 1 : 0;
- mScrollPointerId = MotionEventCompat.getPointerId(e, newIndex);
- mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, newIndex) + 0.5f);
- mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, newIndex) + 0.5f);
- }
- }
-
- @Override
- protected void onMeasure(int widthSpec, int heightSpec) {
- if (mAdapterUpdateDuringMeasure) {
- eatRequestLayout();
- processAdapterUpdatesAndSetAnimationFlags();
-
- if (mState.mRunPredictiveAnimations) {
- // TODO: try to provide a better approach.
- // When RV decides to run predictive animations, we need to measure in pre-layout
- // state so that pre-layout pass results in correct layout.
- // On the other hand, this will prevent the layout manager from resizing properly.
- mState.mInPreLayout = true;
- } else {
- // consume remaining updates to provide a consistent state with the layout pass.
- mAdapterHelper.consumeUpdatesInOnePass();
- mState.mInPreLayout = false;
- }
- mAdapterUpdateDuringMeasure = false;
- resumeRequestLayout(false);
- }
-
- if (mAdapter != null) {
- mState.mItemCount = mAdapter.getItemCount();
- } else {
- mState.mItemCount = 0;
- }
-
- mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
- mState.mInPreLayout = false; // clear
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- if (w != oldw || h != oldh) {
- invalidateGlows();
- }
- }
-
- /**
- * Sets the {@link ItemAnimator} that will handle animations involving changes
- * to the items in this RecyclerView. By default, RecyclerView instantiates and
- * uses an instance of {@link DefaultItemAnimator}. Whether item animations are
- * enabled for the RecyclerView depends on the ItemAnimator and whether
- * the LayoutManager {@link LayoutManager#supportsPredictiveItemAnimations()
- * supports item animations}.
- *
- * @param animator The ItemAnimator being set. If null, no animations will occur
- * when changes occur to the items in this RecyclerView.
- */
- public void setItemAnimator(ItemAnimator animator) {
- if (mItemAnimator != null) {
- mItemAnimator.endAnimations();
- mItemAnimator.setListener(null);
- }
- mItemAnimator = animator;
- if (mItemAnimator != null) {
- mItemAnimator.setListener(mItemAnimatorListener);
- }
- }
-
- /**
- * Gets the current ItemAnimator for this RecyclerView. A null return value
- * indicates that there is no animator and that item changes will happen without
- * any animations. By default, RecyclerView instantiates and
- * uses an instance of {@link DefaultItemAnimator}.
- *
- * @return ItemAnimator The current ItemAnimator. If null, no animations will occur
- * when changes occur to the items in this RecyclerView.
- */
- public ItemAnimator getItemAnimator() {
- return mItemAnimator;
- }
-
- private boolean supportsChangeAnimations() {
- return mItemAnimator != null && mItemAnimator.getSupportsChangeAnimations();
- }
-
- /**
- * Post a runnable to the next frame to run pending item animations. Only the first such
- * request will be posted, governed by the mPostedAnimatorRunner flag.
- */
- private void postAnimationRunner() {
- if (!mPostedAnimatorRunner && mIsAttached) {
- ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
- mPostedAnimatorRunner = true;
- }
- }
-
- private boolean predictiveItemAnimationsEnabled() {
- return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
- }
-
- /**
- * Consumes adapter updates and calculates which type of animations we want to run.
- * Called in onMeasure and dispatchLayout.
- *
- * This method may process only the pre-layout state of updates or all of them.
- */
- private void processAdapterUpdatesAndSetAnimationFlags() {
- if (mDataSetHasChangedAfterLayout) {
- // Processing these items have no value since data set changed unexpectedly.
- // Instead, we just reset it.
- mAdapterHelper.reset();
- markKnownViewsInvalid();
- mLayout.onItemsChanged(this);
- }
- // simple animations are a subset of advanced animations (which will cause a
- // pre-layout step)
- // If layout supports predictive animations, pre-process to decide if we want to run them
- if (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations()) {
- mAdapterHelper.preProcess();
- } else {
- mAdapterHelper.consumeUpdatesInOnePass();
- }
- boolean animationTypeSupported = (mItemsAddedOrRemoved && !mItemsChanged) ||
- (mItemsAddedOrRemoved || (mItemsChanged && supportsChangeAnimations()));
- mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null &&
- (mDataSetHasChangedAfterLayout || animationTypeSupported ||
- mLayout.mRequestedSimpleAnimations) &&
- (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds());
- mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations &&
- animationTypeSupported && !mDataSetHasChangedAfterLayout &&
- predictiveItemAnimationsEnabled();
- }
-
- /**
- * Wrapper around layoutChildren() that handles animating changes caused by layout.
- * Animations work on the assumption that there are five different kinds of items
- * in play:
- * PERSISTENT: items are visible before and after layout
- * REMOVED: items were visible before layout and were removed by the app
- * ADDED: items did not exist before layout and were added by the app
- * DISAPPEARING: items exist in the data set before/after, but changed from
- * visible to non-visible in the process of layout (they were moved off
- * screen as a side-effect of other changes)
- * APPEARING: items exist in the data set before/after, but changed from
- * non-visible to visible in the process of layout (they were moved on
- * screen as a side-effect of other changes)
- * The overall approach figures out what items exist before/after layout and
- * infers one of the five above states for each of the items. Then the animations
- * are set up accordingly:
- * PERSISTENT views are moved ({@link ItemAnimator#animateMove(ViewHolder, int, int, int, int)})
- * REMOVED views are removed ({@link ItemAnimator#animateRemove(ViewHolder)})
- * ADDED views are added ({@link ItemAnimator#animateAdd(ViewHolder)})
- * DISAPPEARING views are moved off screen
- * APPEARING views are moved on screen
- */
- void dispatchLayout() {
- if (mAdapter == null) {
- Log.e(TAG, "No adapter attached; skipping layout");
- return;
- }
- mDisappearingViewsInLayoutPass.clear();
- eatRequestLayout();
- mRunningLayoutOrScroll = true;
-
- processAdapterUpdatesAndSetAnimationFlags();
-
- mState.mOldChangedHolders = mState.mRunSimpleAnimations && mItemsChanged
- && supportsChangeAnimations() ? new ArrayMap() : null;
- mItemsAddedOrRemoved = mItemsChanged = false;
- ArrayMap appearingViewInitialBounds = null;
- mState.mInPreLayout = mState.mRunPredictiveAnimations;
- mState.mItemCount = mAdapter.getItemCount();
-
- if (mState.mRunSimpleAnimations) {
- // Step 0: Find out where all non-removed items are, pre-layout
- mState.mPreLayoutHolderMap.clear();
- mState.mPostLayoutHolderMap.clear();
- int count = mChildHelper.getChildCount();
- for (int i = 0; i < count; ++i) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
- if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
- continue;
- }
- final View view = holder.itemView;
- mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder,
- view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
- }
- }
- if (mState.mRunPredictiveAnimations) {
- // Step 1: run prelayout: This will use the old positions of items. The layout manager
- // is expected to layout everything, even removed items (though not to add removed
- // items back to the container). This gives the pre-layout position of APPEARING views
- // which come into existence as part of the real layout.
-
- // Save old positions so that LayoutManager can run its mapping logic.
- saveOldPositions();
- // processAdapterUpdatesAndSetAnimationFlags already run pre-layout animations.
- if (mState.mOldChangedHolders != null) {
- int count = mChildHelper.getChildCount();
- for (int i = 0; i < count; ++i) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
- if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) {
- long key = getChangedHolderKey(holder);
- mState.mOldChangedHolders.put(key, holder);
- mState.mPreLayoutHolderMap.remove(holder);
- }
- }
- }
-
- final boolean didStructureChange = mState.mStructureChanged;
- mState.mStructureChanged = false;
- // temporarily disable flag because we are asking for previous layout
- mLayout.onLayoutChildren(mRecycler, mState);
- mState.mStructureChanged = didStructureChange;
-
- appearingViewInitialBounds = new ArrayMap();
- for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
- boolean found = false;
- View child = mChildHelper.getChildAt(i);
- if (getChildViewHolderInt(child).shouldIgnore()) {
- continue;
- }
- for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) {
- ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j);
- if (holder.itemView == child) {
- found = true;
- break;
- }
- }
- if (!found) {
- appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(),
- child.getRight(), child.getBottom()));
- }
- }
- // we don't process disappearing list because they may re-appear in post layout pass.
- clearOldPositions();
- mAdapterHelper.consumePostponedUpdates();
- } else {
- clearOldPositions();
- // in case pre layout did run but we decided not to run predictive animations.
- mAdapterHelper.consumeUpdatesInOnePass();
- if (mState.mOldChangedHolders != null) {
- int count = mChildHelper.getChildCount();
- for (int i = 0; i < count; ++i) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
- if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) {
- long key = getChangedHolderKey(holder);
- mState.mOldChangedHolders.put(key, holder);
- mState.mPreLayoutHolderMap.remove(holder);
- }
- }
- }
- }
- mState.mItemCount = mAdapter.getItemCount();
- mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
-
- // Step 2: Run layout
- mState.mInPreLayout = false;
- mLayout.onLayoutChildren(mRecycler, mState);
-
- mState.mStructureChanged = false;
- mPendingSavedState = null;
-
- // onLayoutChildren may have caused client code to disable item animations; re-check
- mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
-
- if (mState.mRunSimpleAnimations) {
- // Step 3: Find out where things are now, post-layout
- ArrayMap newChangedHolders = mState.mOldChangedHolders != null ?
- new ArrayMap() : null;
- int count = mChildHelper.getChildCount();
- for (int i = 0; i < count; ++i) {
- ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
- if (holder.shouldIgnore()) {
- continue;
- }
- final View view = holder.itemView;
- long key = getChangedHolderKey(holder);
- if (newChangedHolders != null && mState.mOldChangedHolders.get(key) != null) {
- newChangedHolders.put(key, holder);
- } else {
- mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder,
- view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
- }
- }
- processDisappearingList(appearingViewInitialBounds);
- // Step 4: Animate DISAPPEARING and REMOVED items
- int preLayoutCount = mState.mPreLayoutHolderMap.size();
- for (int i = preLayoutCount - 1; i >= 0; i--) {
- ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i);
- if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) {
- ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i);
- mState.mPreLayoutHolderMap.removeAt(i);
-
- View disappearingItemView = disappearingItem.holder.itemView;
- removeDetachedView(disappearingItemView, false);
- mRecycler.unscrapView(disappearingItem.holder);
-
- animateDisappearance(disappearingItem);
- }
- }
- // Step 5: Animate APPEARING and ADDED items
- int postLayoutCount = mState.mPostLayoutHolderMap.size();
- if (postLayoutCount > 0) {
- for (int i = postLayoutCount - 1; i >= 0; i--) {
- ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i);
- ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i);
- if ((mState.mPreLayoutHolderMap.isEmpty() ||
- !mState.mPreLayoutHolderMap.containsKey(itemHolder))) {
- mState.mPostLayoutHolderMap.removeAt(i);
- Rect initialBounds = (appearingViewInitialBounds != null) ?
- appearingViewInitialBounds.get(itemHolder.itemView) : null;
- animateAppearance(itemHolder, initialBounds,
- info.left, info.top);
- }
- }
- }
- // Step 6: Animate PERSISTENT items
- count = mState.mPostLayoutHolderMap.size();
- for (int i = 0; i < count; ++i) {
- ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i);
- ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i);
- ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder);
- if (preInfo != null && postInfo != null) {
- if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
- postHolder.setIsRecyclable(false);
- if (DEBUG) {
- Log.d(TAG, "PERSISTENT: " + postHolder +
- " with view " + postHolder.itemView);
- }
- if (mItemAnimator.animateMove(postHolder,
- preInfo.left, preInfo.top, postInfo.left, postInfo.top)) {
- postAnimationRunner();
- }
- }
- }
- }
- // Step 7: Animate CHANGING items
- count = mState.mOldChangedHolders != null ? mState.mOldChangedHolders.size() : 0;
- // traverse reverse in case view gets recycled while we are traversing the list.
- for (int i = count - 1; i >= 0; i--) {
- long key = mState.mOldChangedHolders.keyAt(i);
- ViewHolder oldHolder = mState.mOldChangedHolders.get(key);
- View oldView = oldHolder.itemView;
- if (oldHolder.shouldIgnore()) {
- continue;
- }
- // We probably don't need this check anymore since these views are removed from
- // the list if they are recycled.
- if (mRecycler.mChangedScrap != null &&
- mRecycler.mChangedScrap.contains(oldHolder)) {
- animateChange(oldHolder, newChangedHolders.get(key));
- } else if (DEBUG) {
- Log.e(TAG, "cannot find old changed holder in changed scrap :/" + oldHolder);
- }
- }
- }
- resumeRequestLayout(false);
- mLayout.removeAndRecycleScrapInt(mRecycler, !mState.mRunPredictiveAnimations);
- mState.mPreviousLayoutItemCount = mState.mItemCount;
- mDataSetHasChangedAfterLayout = false;
- mState.mRunSimpleAnimations = false;
- mState.mRunPredictiveAnimations = false;
- mRunningLayoutOrScroll = false;
- mLayout.mRequestedSimpleAnimations = false;
- if (mRecycler.mChangedScrap != null) {
- mRecycler.mChangedScrap.clear();
- }
- mState.mOldChangedHolders = null;
- }
-
- /**
- * Returns a unique key to be used while handling change animations.
- * It might be child's position or stable id depending on the adapter type.
- */
- long getChangedHolderKey(ViewHolder holder) {
- return mAdapter.hasStableIds() ? holder.getItemId() : holder.mPosition;
- }
-
- /**
- * A LayoutManager may want to layout a view just to animate disappearance.
- * This method handles those views and triggers remove animation on them.
- */
- private void processDisappearingList(ArrayMap appearingViews) {
- final int count = mDisappearingViewsInLayoutPass.size();
- for (int i = 0; i < count; i ++) {
- View view = mDisappearingViewsInLayoutPass.get(i);
- ViewHolder vh = getChildViewHolderInt(view);
- final ItemHolderInfo info = mState.mPreLayoutHolderMap.remove(vh);
- if (!mState.isPreLayout()) {
- mState.mPostLayoutHolderMap.remove(vh);
- }
- if (appearingViews.remove(view) != null) {
- mLayout.removeAndRecycleView(view, mRecycler);
- continue;
- }
- if (info != null) {
- animateDisappearance(info);
- } else {
- // let it disappear from the position it becomes visible
- animateDisappearance(new ItemHolderInfo(vh, view.getLeft(), view.getTop(),
- view.getRight(), view.getBottom()));
- }
- }
- mDisappearingViewsInLayoutPass.clear();
- }
-
- private void animateAppearance(ViewHolder itemHolder, Rect beforeBounds, int afterLeft,
- int afterTop) {
- View newItemView = itemHolder.itemView;
- if (beforeBounds != null &&
- (beforeBounds.left != afterLeft || beforeBounds.top != afterTop)) {
- // slide items in if before/after locations differ
- itemHolder.setIsRecyclable(false);
- if (DEBUG) {
- Log.d(TAG, "APPEARING: " + itemHolder + " with view " + newItemView);
- }
- if (mItemAnimator.animateMove(itemHolder,
- beforeBounds.left, beforeBounds.top,
- afterLeft, afterTop)) {
- postAnimationRunner();
- }
- } else {
- if (DEBUG) {
- Log.d(TAG, "ADDED: " + itemHolder + " with view " + newItemView);
- }
- itemHolder.setIsRecyclable(false);
- if (mItemAnimator.animateAdd(itemHolder)) {
- postAnimationRunner();
- }
- }
- }
-
- private void animateDisappearance(ItemHolderInfo disappearingItem) {
- View disappearingItemView = disappearingItem.holder.itemView;
- addAnimatingView(disappearingItemView);
- int oldLeft = disappearingItem.left;
- int oldTop = disappearingItem.top;
- int newLeft = disappearingItemView.getLeft();
- int newTop = disappearingItemView.getTop();
- if (oldLeft != newLeft || oldTop != newTop) {
- disappearingItem.holder.setIsRecyclable(false);
- disappearingItemView.layout(newLeft, newTop,
- newLeft + disappearingItemView.getWidth(),
- newTop + disappearingItemView.getHeight());
- if (DEBUG) {
- Log.d(TAG, "DISAPPEARING: " + disappearingItem.holder +
- " with view " + disappearingItemView);
- }
- if (mItemAnimator.animateMove(disappearingItem.holder, oldLeft, oldTop,
- newLeft, newTop)) {
- postAnimationRunner();
- }
- } else {
- if (DEBUG) {
- Log.d(TAG, "REMOVED: " + disappearingItem.holder +
- " with view " + disappearingItemView);
- }
- disappearingItem.holder.setIsRecyclable(false);
- if (mItemAnimator.animateRemove(disappearingItem.holder)) {
- postAnimationRunner();
- }
- }
- }
-
- private void animateChange(ViewHolder oldHolder, ViewHolder newHolder) {
- oldHolder.setIsRecyclable(false);
- removeDetachedView(oldHolder.itemView, false);
- addAnimatingView(oldHolder.itemView);
- oldHolder.mShadowedHolder = newHolder;
- mRecycler.unscrapView(oldHolder);
- if (DEBUG) {
- Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
- }
- final int fromLeft = oldHolder.itemView.getLeft();
- final int fromTop = oldHolder.itemView.getTop();
- final int toLeft, toTop;
- if (newHolder == null || newHolder.shouldIgnore()) {
- toLeft = fromLeft;
- toTop = fromTop;
- } else {
- toLeft = newHolder.itemView.getLeft();
- toTop = newHolder.itemView.getTop();
- newHolder.setIsRecyclable(false);
- newHolder.mShadowingHolder = oldHolder;
- }
- if(mItemAnimator.animateChange(oldHolder, newHolder,
- fromLeft, fromTop, toLeft, toTop)) {
- postAnimationRunner();
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- eatRequestLayout();
- dispatchLayout();
- resumeRequestLayout(false);
- mFirstLayoutComplete = true;
- }
-
- @Override
- public void requestLayout() {
- if (!mEatRequestLayout) {
- super.requestLayout();
- } else {
- mLayoutRequestEaten = true;
- }
- }
-
- void markItemDecorInsetsDirty() {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- for (int i = 0; i < childCount; i++) {
- final View child = mChildHelper.getUnfilteredChildAt(i);
- ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
- }
- mRecycler.markItemDecorInsetsDirty();
- }
-
- @Override
- public void draw(Canvas c) {
- super.draw(c);
-
- final int count = mItemDecorations.size();
- for (int i = 0; i < count; i++) {
- mItemDecorations.get(i).onDrawOver(c, this, mState);
- }
- // TODO If padding is not 0 and chilChildrenToPadding is false, to draw glows properly, we
- // need find children closest to edges. Not sure if it is worth the effort.
- boolean needsInvalidate = false;
- if (mLeftGlow != null && !mLeftGlow.isFinished()) {
- final int restore = c.save();
- final int padding = mClipToPadding ? getPaddingBottom() : 0;
- c.rotate(270);
- c.translate(-getHeight() + padding, 0);
- needsInvalidate = mLeftGlow != null && mLeftGlow.draw(c);
- c.restoreToCount(restore);
- }
- if (mTopGlow != null && !mTopGlow.isFinished()) {
- final int restore = c.save();
- if (mClipToPadding) {
- c.translate(getPaddingLeft(), getPaddingTop());
- }
- needsInvalidate |= mTopGlow != null && mTopGlow.draw(c);
- c.restoreToCount(restore);
- }
- if (mRightGlow != null && !mRightGlow.isFinished()) {
- final int restore = c.save();
- final int width = getWidth();
- final int padding = mClipToPadding ? getPaddingTop() : 0;
- c.rotate(90);
- c.translate(-padding, -width);
- needsInvalidate |= mRightGlow != null && mRightGlow.draw(c);
- c.restoreToCount(restore);
- }
- if (mBottomGlow != null && !mBottomGlow.isFinished()) {
- final int restore = c.save();
- c.rotate(180);
- if (mClipToPadding) {
- c.translate(-getWidth() + getPaddingRight(), -getHeight() + getPaddingBottom());
- } else {
- c.translate(-getWidth(), -getHeight());
- }
- needsInvalidate |= mBottomGlow != null && mBottomGlow.draw(c);
- c.restoreToCount(restore);
- }
-
- // If some views are animating, ItemDecorators are likely to move/change with them.
- // Invalidate RecyclerView to re-draw decorators. This is still efficient because children's
- // display lists are not invalidated.
- if (!needsInvalidate && mItemAnimator != null && mItemDecorations.size() > 0 &&
- mItemAnimator.isRunning()) {
- needsInvalidate = true;
- }
-
- if (needsInvalidate) {
- ViewCompat.postInvalidateOnAnimation(this);
- }
- }
-
- @Override
- public void onDraw(Canvas c) {
- super.onDraw(c);
-
- final int count = mItemDecorations.size();
- for (int i = 0; i < count; i++) {
- mItemDecorations.get(i).onDraw(c, this, mState);
- }
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams && mLayout.checkLayoutParams((LayoutParams) p);
- }
-
- @Override
- protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
- if (mLayout == null) {
- throw new IllegalStateException("RecyclerView has no LayoutManager");
- }
- return mLayout.generateDefaultLayoutParams();
- }
-
- @Override
- public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
- if (mLayout == null) {
- throw new IllegalStateException("RecyclerView has no LayoutManager");
- }
- return mLayout.generateLayoutParams(getContext(), attrs);
- }
-
- @Override
- protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- if (mLayout == null) {
- throw new IllegalStateException("RecyclerView has no LayoutManager");
- }
- return mLayout.generateLayoutParams(p);
- }
-
- void saveOldPositions() {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
- if (DEBUG && holder.mPosition == -1 && !holder.isRemoved()) {
- throw new IllegalStateException("view holder cannot have position -1 unless it"
- + " is removed");
- }
- if (!holder.shouldIgnore()) {
- holder.saveOldPosition();
- }
- }
- }
-
- void clearOldPositions() {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
- if (!holder.shouldIgnore()) {
- holder.clearOldPosition();
- }
- }
- mRecycler.clearOldPositions();
- }
-
- void offsetPositionRecordsForMove(int from, int to) {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- final int start, end, inBetweenOffset;
- if (from < to) {
- start = from;
- end = to;
- inBetweenOffset = -1;
- } else {
- start = to;
- end = from;
- inBetweenOffset = 1;
- }
-
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
- if (holder == null || holder.mPosition < start || holder.mPosition > end) {
- continue;
- }
- if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForMove attached child " + i + " holder " +
- holder);
- }
- if (holder.mPosition == from) {
- holder.offsetPosition(to - from, false);
- } else {
- holder.offsetPosition(inBetweenOffset, false);
- }
-
- mState.mStructureChanged = true;
- }
- mRecycler.offsetPositionRecordsForMove(from, to);
- requestLayout();
- }
-
- void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
- if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
- if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForInsert attached child " + i + " holder " +
- holder + " now at position " + (holder.mPosition + itemCount));
- }
- holder.offsetPosition(itemCount, false);
- mState.mStructureChanged = true;
- }
- }
- mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
- requestLayout();
- }
-
- void offsetPositionRecordsForRemove(int positionStart, int itemCount,
- boolean applyToPreLayout) {
- final int positionEnd = positionStart + itemCount;
- final int childCount = mChildHelper.getUnfilteredChildCount();
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
- if (holder != null && !holder.shouldIgnore()) {
- if (holder.mPosition >= positionEnd) {
- if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
- " holder " + holder + " now at position " +
- (holder.mPosition - itemCount));
- }
- holder.offsetPosition(-itemCount, applyToPreLayout);
- mState.mStructureChanged = true;
- } else if (holder.mPosition >= positionStart) {
- if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForRemove attached child " + i +
- " holder " + holder + " now REMOVED");
- }
- holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
- applyToPreLayout);
- mState.mStructureChanged = true;
- }
- }
- }
- mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
- requestLayout();
- }
-
- /**
- * Rebind existing views for the given range, or create as needed.
- *
- * @param positionStart Adapter position to start at
- * @param itemCount Number of views that must explicitly be rebound
- */
- void viewRangeUpdate(int positionStart, int itemCount) {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- final int positionEnd = positionStart + itemCount;
-
- for (int i = 0; i < childCount; i++) {
- final View child = mChildHelper.getUnfilteredChildAt(i);
- final ViewHolder holder = getChildViewHolderInt(child);
- if (holder == null || holder.shouldIgnore()) {
- continue;
- }
- if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
- // We re-bind these view holders after pre-processing is complete so that
- // ViewHolders have their final positions assigned.
- holder.addFlags(ViewHolder.FLAG_UPDATE);
- if (supportsChangeAnimations()) {
- holder.addFlags(ViewHolder.FLAG_CHANGED);
- }
- // lp cannot be null since we get ViewHolder from it.
- ((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
- }
- }
- mRecycler.viewRangeUpdate(positionStart, itemCount);
- }
-
- void rebindUpdatedViewHolders() {
- final int childCount = mChildHelper.getChildCount();
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
- // validate type is correct
- if (holder == null || holder.shouldIgnore()) {
- continue;
- }
- if (holder.isRemoved() || holder.isInvalid()) {
- requestLayout();
- } else if (holder.needsUpdate()) {
- final int type = mAdapter.getItemViewType(holder.mPosition);
- if (holder.getItemViewType() == type) {
- // Binding an attached view will request a layout if needed.
- if (!holder.isChanged() || !supportsChangeAnimations()) {
- mAdapter.bindViewHolder(holder, holder.mPosition);
- } else {
- // Don't rebind changed holders if change animations are enabled.
- // We want the old contents for the animation and will get a new
- // holder for the new contents.
- requestLayout();
- }
- } else {
- // binding to a new view will need re-layout anyways. We can as well trigger
- // it here so that it happens during layout
- holder.addFlags(ViewHolder.FLAG_INVALID);
- requestLayout();
- }
- }
- }
- }
-
- /**
- * Mark all known views as invalid. Used in response to a, "the whole world might have changed"
- * data change event.
- */
- void markKnownViewsInvalid() {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
- if (holder != null && !holder.shouldIgnore()) {
- holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
- }
- }
- markItemDecorInsetsDirty();
- mRecycler.markKnownViewsInvalid();
- }
-
- /**
- * Invalidates all ItemDecorations. If RecyclerView has item decorations, calling this method
- * will trigger a {@link #requestLayout()} call.
- */
- public void invalidateItemDecorations() {
- if (mItemDecorations.size() == 0) {
- return;
- }
- if (mLayout != null) {
- mLayout.assertNotInLayoutOrScroll("Cannot invalidate item decorations during a scroll"
- + " or layout");
- }
- markItemDecorInsetsDirty();
- requestLayout();
- }
-
- /**
- * Retrieve the {@link ViewHolder} for the given child view.
- *
- * @param child Child of this RecyclerView to query for its ViewHolder
- * @return The child view's ViewHolder
- */
- public ViewHolder getChildViewHolder(View child) {
- final ViewParent parent = child.getParent();
- if (parent != null && parent != this) {
- throw new IllegalArgumentException("View " + child + " is not a direct child of " +
- this);
- }
- return getChildViewHolderInt(child);
- }
-
- static ViewHolder getChildViewHolderInt(View child) {
- if (child == null) {
- return null;
- }
- return ((LayoutParams) child.getLayoutParams()).mViewHolder;
- }
-
- /**
- * Return the adapter position that the given child view corresponds to.
- *
- * @param child Child View to query
- * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
- */
- public int getChildPosition(View child) {
- final ViewHolder holder = getChildViewHolderInt(child);
- return holder != null ? holder.getPosition() : NO_POSITION;
- }
-
- /**
- * Return the stable item id that the given child view corresponds to.
- *
- * @param child Child View to query
- * @return Item id corresponding to the given view or {@link #NO_ID}
- */
- public long getChildItemId(View child) {
- if (mAdapter == null || !mAdapter.hasStableIds()) {
- return NO_ID;
- }
- final ViewHolder holder = getChildViewHolderInt(child);
- return holder != null ? holder.getItemId() : NO_ID;
- }
-
- /**
- * Return the ViewHolder for the item in the given position of the data set.
- *
- * @param position The position of the item in the data set of the adapter
- * @return The ViewHolder at position
- */
- public ViewHolder findViewHolderForPosition(int position) {
- return findViewHolderForPosition(position, false);
- }
-
- ViewHolder findViewHolderForPosition(int position, boolean checkNewPosition) {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
- if (holder != null && !holder.isRemoved()) {
- if (checkNewPosition) {
- if (holder.mPosition == position) {
- return holder;
- }
- } else if (holder.getPosition() == position) {
- return holder;
- }
- }
- }
- // This method should not query cached views. It creates a problem during adapter updates
- // when we are dealing with already laid out views. Also, for the public method, it is more
- // reasonable to return null if position is not laid out.
- return null;
- }
-
- /**
- * Return the ViewHolder for the item with the given id. The RecyclerView must
- * use an Adapter with {@link Adapter#setHasStableIds(boolean) stableIds} to
- * return a non-null value.
- *
- * @param id The id for the requested item
- * @return The ViewHolder with the given id, of null if there
- * is no such item.
- */
- public ViewHolder findViewHolderForItemId(long id) {
- final int childCount = mChildHelper.getUnfilteredChildCount();
- for (int i = 0; i < childCount; i++) {
- final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
- if (holder != null && holder.getItemId() == id) {
- return holder;
- }
- }
- // this method should not query cached views. They are not children so they
- // should not be returned in this public method
- return null;
- }
-
- /**
- * Find the topmost view under the given point.
- *
- * @param x Horizontal position in pixels to search
- * @param y Vertical position in pixels to search
- * @return The child view under (x, y) or null if no matching child is found
- */
- public View findChildViewUnder(float x, float y) {
- final int count = mChildHelper.getChildCount();
- for (int i = count - 1; i >= 0; i--) {
- final View child = mChildHelper.getChildAt(i);
- final float translationX = ViewCompat.getTranslationX(child);
- final float translationY = ViewCompat.getTranslationY(child);
- if (x >= child.getLeft() + translationX &&
- x <= child.getRight() + translationX &&
- y >= child.getTop() + translationY &&
- y <= child.getBottom() + translationY) {
- return child;
- }
- }
- return null;
- }
-
- /**
- * Offset the bounds of all child views by dy pixels.
- * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
- *
- * @param dy Vertical pixel offset to apply to the bounds of all child views
- */
- public void offsetChildrenVertical(int dy) {
- final int childCount = mChildHelper.getChildCount();
- for (int i = 0; i < childCount; i++) {
- mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
- }
- }
-
- /**
- * Called when an item view is attached to this RecyclerView.
- *
- *
Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
- * of child views as they become attached. This will be called before a
- * {@link LayoutManager} measures or lays out the view and is a good time to perform these
- * changes.
- *
- * @param child Child view that is now attached to this RecyclerView and its associated window
- */
- public void onChildAttachedToWindow(View child) {
- }
-
- /**
- * Called when an item view is detached from this RecyclerView.
- *
- *
Subclasses of RecyclerView may want to perform extra bookkeeping or modifications
- * of child views as they become detached. This will be called as a
- * {@link LayoutManager} fully detaches the child view from the parent and its window.
- *
- * @param child Child view that is now detached from this RecyclerView and its associated window
- */
- public void onChildDetachedFromWindow(View child) {
- }
-
- /**
- * Offset the bounds of all child views by dx pixels.
- * Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
- *
- * @param dx Horizontal pixel offset to apply to the bounds of all child views
- */
- public void offsetChildrenHorizontal(int dx) {
- final int childCount = mChildHelper.getChildCount();
- for (int i = 0; i < childCount; i++) {
- mChildHelper.getChildAt(i).offsetLeftAndRight(dx);
- }
- }
-
- Rect getItemDecorInsetsForChild(View child) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (!lp.mInsetsDirty) {
- return lp.mDecorInsets;
- }
-
- final Rect insets = lp.mDecorInsets;
- insets.set(0, 0, 0, 0);
- final int decorCount = mItemDecorations.size();
- for (int i = 0; i < decorCount; i++) {
- mTempRect.set(0, 0, 0, 0);
- mItemDecorations.get(i).getItemOffsets(mTempRect, child, this, mState);
- insets.left += mTempRect.left;
- insets.top += mTempRect.top;
- insets.right += mTempRect.right;
- insets.bottom += mTempRect.bottom;
- }
- lp.mInsetsDirty = false;
- return insets;
- }
-
- private class ViewFlinger implements Runnable {
- private int mLastFlingX;
- private int mLastFlingY;
- private ScrollerCompat mScroller;
- private Interpolator mInterpolator = sQuinticInterpolator;
-
-
- // When set to true, postOnAnimation callbacks are delayed until the run method completes
- private boolean mEatRunOnAnimationRequest = false;
-
- // Tracks if postAnimationCallback should be re-attached when it is done
- private boolean mReSchedulePostAnimationCallback = false;
-
- public ViewFlinger() {
- mScroller = ScrollerCompat.create(getContext(), sQuinticInterpolator);
- }
-
- @Override
- public void run() {
- disableRunOnAnimationRequests();
- consumePendingUpdateOperations();
- // keep a local reference so that if it is changed during onAnimation method, it won't
- // cause unexpected behaviors
- final ScrollerCompat scroller = mScroller;
- final SmoothScroller smoothScroller = mLayout.mSmoothScroller;
- if (scroller.computeScrollOffset()) {
- final int x = scroller.getCurrX();
- final int y = scroller.getCurrY();
- final int dx = x - mLastFlingX;
- final int dy = y - mLastFlingY;
- int hresult = 0;
- int vresult = 0;
- mLastFlingX = x;
- mLastFlingY = y;
- int overscrollX = 0, overscrollY = 0;
- if (mAdapter != null) {
- eatRequestLayout();
- mRunningLayoutOrScroll = true;
- if (dx != 0) {
- hresult = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
- overscrollX = dx - hresult;
- }
- if (dy != 0) {
- vresult = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
- overscrollY = dy - vresult;
- }
- if (supportsChangeAnimations()) {
- // Fix up shadow views used by changing animations
- int count = mChildHelper.getChildCount();
- for (int i = 0; i < count; i++) {
- View view = mChildHelper.getChildAt(i);
- ViewHolder holder = getChildViewHolder(view);
- if (holder != null && holder.mShadowingHolder != null) {
- View shadowingView = holder.mShadowingHolder != null ?
- holder.mShadowingHolder.itemView : null;
- if (shadowingView != null) {
- int left = view.getLeft();
- int top = view.getTop();
- if (left != shadowingView.getLeft() ||
- top != shadowingView.getTop()) {
- shadowingView.layout(left, top,
- left + shadowingView.getWidth(),
- top + shadowingView.getHeight());
- }
- }
- }
- }
- }
-
- if (smoothScroller != null && !smoothScroller.isPendingInitialRun() &&
- smoothScroller.isRunning()) {
- final int adapterSize = mState.getItemCount();
- if (adapterSize == 0) {
- smoothScroller.stop();
- } else if (smoothScroller.getTargetPosition() >= adapterSize) {
- smoothScroller.setTargetPosition(adapterSize - 1);
- smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
- } else {
- smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
- }
- }
- mRunningLayoutOrScroll = false;
- resumeRequestLayout(false);
- }
- final boolean fullyConsumedScroll = dx == hresult && dy == vresult;
- if (!mItemDecorations.isEmpty()) {
- invalidate();
- }
- if (ViewCompat.getOverScrollMode(RecyclerView.this) !=
- ViewCompat.OVER_SCROLL_NEVER) {
- considerReleasingGlowsOnScroll(dx, dy);
- }
- if (overscrollX != 0 || overscrollY != 0) {
- final int vel = (int) scroller.getCurrVelocity();
-
- int velX = 0;
- if (overscrollX != x) {
- velX = overscrollX < 0 ? -vel : overscrollX > 0 ? vel : 0;
- }
-
- int velY = 0;
- if (overscrollY != y) {
- velY = overscrollY < 0 ? -vel : overscrollY > 0 ? vel : 0;
- }
-
- if (ViewCompat.getOverScrollMode(RecyclerView.this) !=
- ViewCompat.OVER_SCROLL_NEVER) {
- absorbGlows(velX, velY);
- }
- if ((velX != 0 || overscrollX == x || scroller.getFinalX() == 0) &&
- (velY != 0 || overscrollY == y || scroller.getFinalY() == 0)) {
- scroller.abortAnimation();
- }
- }
- if (hresult != 0 || vresult != 0) {
- // dummy values, View's implementation does not use these.
- onScrollChanged(0, 0, 0, 0);
- if (mScrollListener != null) {
- mScrollListener.onScrolled(RecyclerView.this, hresult, vresult);
- }
- }
-
- if (!awakenScrollBars()) {
- invalidate();
- }
-
- if (scroller.isFinished() || !fullyConsumedScroll) {
- setScrollState(SCROLL_STATE_IDLE); // setting state to idle will stop this.
- } else {
- postOnAnimation();
- }
- }
- // call this after the onAnimation is complete not to have inconsistent callbacks etc.
- if (smoothScroller != null && smoothScroller.isPendingInitialRun()) {
- smoothScroller.onAnimation(0, 0);
- }
- enableRunOnAnimationRequests();
- }
-
- private void disableRunOnAnimationRequests() {
- mReSchedulePostAnimationCallback = false;
- mEatRunOnAnimationRequest = true;
- }
-
- private void enableRunOnAnimationRequests() {
- mEatRunOnAnimationRequest = false;
- if (mReSchedulePostAnimationCallback) {
- postOnAnimation();
- }
- }
-
- void postOnAnimation() {
- if (mEatRunOnAnimationRequest) {
- mReSchedulePostAnimationCallback = true;
- } else {
- ViewCompat.postOnAnimation(RecyclerView.this, this);
- }
- }
-
- public void fling(int velocityX, int velocityY) {
- setScrollState(SCROLL_STATE_SETTLING);
- mLastFlingX = mLastFlingY = 0;
- mScroller.fling(0, 0, velocityX, velocityY,
- Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
- postOnAnimation();
- }
-
- public void smoothScrollBy(int dx, int dy) {
- smoothScrollBy(dx, dy, 0, 0);
- }
-
- public void smoothScrollBy(int dx, int dy, int vx, int vy) {
- smoothScrollBy(dx, dy, computeScrollDuration(dx, dy, vx, vy));
- }
-
- private float distanceInfluenceForSnapDuration(float f) {
- f -= 0.5f; // center the values about 0.
- f *= 0.3f * Math.PI / 2.0f;
- return (float) Math.sin(f);
- }
-
- private int computeScrollDuration(int dx, int dy, int vx, int vy) {
- final int absDx = Math.abs(dx);
- final int absDy = Math.abs(dy);
- final boolean horizontal = absDx > absDy;
- final int velocity = (int) Math.sqrt(vx * vx + vy * vy);
- final int delta = (int) Math.sqrt(dx * dx + dy * dy);
- final int containerSize = horizontal ? getWidth() : getHeight();
- final int halfContainerSize = containerSize / 2;
- final float distanceRatio = Math.min(1.f, 1.f * delta / containerSize);
- final float distance = halfContainerSize + halfContainerSize *
- distanceInfluenceForSnapDuration(distanceRatio);
-
- final int duration;
- if (velocity > 0) {
- duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- } else {
- float absDelta = (float) (horizontal ? absDx : absDy);
- duration = (int) (((absDelta / containerSize) + 1) * 300);
- }
- return Math.min(duration, MAX_SCROLL_DURATION);
- }
-
- public void smoothScrollBy(int dx, int dy, int duration) {
- smoothScrollBy(dx, dy, duration, sQuinticInterpolator);
- }
-
- public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) {
- if (mInterpolator != interpolator) {
- mInterpolator = interpolator;
- mScroller = ScrollerCompat.create(getContext(), interpolator);
- }
- setScrollState(SCROLL_STATE_SETTLING);
- mLastFlingX = mLastFlingY = 0;
- mScroller.startScroll(0, 0, dx, dy, duration);
- postOnAnimation();
- }
-
- public void stop() {
- removeCallbacks(this);
- mScroller.abortAnimation();
- }
-
- }
-
- private class RecyclerViewDataObserver extends AdapterDataObserver {
- @Override
- public void onChanged() {
- assertNotInLayoutOrScroll(null);
- if (mAdapter.hasStableIds()) {
- // TODO Determine what actually changed.
- // This is more important to implement now since this callback will disable all
- // animations because we cannot rely on positions.
- mState.mStructureChanged = true;
- mDataSetHasChangedAfterLayout = true;
- } else {
- mState.mStructureChanged = true;
- mDataSetHasChangedAfterLayout = true;
- }
- if (!mAdapterHelper.hasPendingUpdates()) {
- requestLayout();
- }
- }
-
- @Override
- public void onItemRangeChanged(int positionStart, int itemCount) {
- assertNotInLayoutOrScroll(null);
- if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount)) {
- triggerUpdateProcessor();
- }
- }
-
- @Override
- public void onItemRangeInserted(int positionStart, int itemCount) {
- assertNotInLayoutOrScroll(null);
- if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
- triggerUpdateProcessor();
- }
- }
-
- @Override
- public void onItemRangeRemoved(int positionStart, int itemCount) {
- assertNotInLayoutOrScroll(null);
- if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
- triggerUpdateProcessor();
- }
- }
-
- @Override
- public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
- assertNotInLayoutOrScroll(null);
- if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
- triggerUpdateProcessor();
- }
- }
-
- void triggerUpdateProcessor() {
- if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
- ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
- } else {
- mAdapterUpdateDuringMeasure = true;
- requestLayout();
- }
- }
- }
-
- /**
- * RecycledViewPool lets you share Views between multiple RecyclerViews.
- *
- * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
- * and use {@link RecyclerView#setRecycledViewPool(RecycledViewPool)}.
- *
- * RecyclerView automatically creates a pool for itself if you don't provide one.
- *
- */
- public static class RecycledViewPool {
- private SparseArray> mScrap =
- new SparseArray>();
- private SparseIntArray mMaxScrap = new SparseIntArray();
- private int mAttachCount = 0;
-
- private static final int DEFAULT_MAX_SCRAP = 5;
-
- public void clear() {
- mScrap.clear();
- }
-
- public void setMaxRecycledViews(int viewType, int max) {
- mMaxScrap.put(viewType, max);
- final ArrayList scrapHeap = mScrap.get(viewType);
- if (scrapHeap != null) {
- while (scrapHeap.size() > max) {
- scrapHeap.remove(scrapHeap.size() - 1);
- }
- }
- }
-
- public ViewHolder getRecycledView(int viewType) {
- final ArrayList scrapHeap = mScrap.get(viewType);
- if (scrapHeap != null && !scrapHeap.isEmpty()) {
- final int index = scrapHeap.size() - 1;
- final ViewHolder scrap = scrapHeap.get(index);
- scrapHeap.remove(index);
- return scrap;
- }
- return null;
- }
-
- int size() {
- int count = 0;
- for (int i = 0; i < mScrap.size(); i ++) {
- ArrayList viewHolders = mScrap.valueAt(i);
- if (viewHolders != null) {
- count += viewHolders.size();
- }
- }
- return count;
- }
-
- public void putRecycledView(ViewHolder scrap) {
- final int viewType = scrap.getItemViewType();
- final ArrayList scrapHeap = getScrapHeapForType(viewType);
- if (mMaxScrap.get(viewType) <= scrapHeap.size()) {
- return;
- }
- scrap.resetInternal();
- scrapHeap.add(scrap);
- }
-
- void attach(Adapter adapter) {
- mAttachCount++;
- }
-
- void detach() {
- mAttachCount--;
- }
-
-
- /**
- * Detaches the old adapter and attaches the new one.
- *
- * RecycledViewPool will clear its cache if it has only one adapter attached and the new
- * adapter uses a different ViewHolder than the oldAdapter.
- *
- * @param oldAdapter The previous adapter instance. Will be detached.
- * @param newAdapter The new adapter instance. Will be attached.
- * @param compatibleWithPrevious True if both oldAdapter and newAdapter are using the same
- * ViewHolder and view types.
- */
- void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
- boolean compatibleWithPrevious) {
- if (oldAdapter != null) {
- detach();
- }
- if (!compatibleWithPrevious && mAttachCount == 0) {
- clear();
- }
- if (newAdapter != null) {
- attach(newAdapter);
- }
- }
-
- private ArrayList getScrapHeapForType(int viewType) {
- ArrayList scrap = mScrap.get(viewType);
- if (scrap == null) {
- scrap = new ArrayList();
- mScrap.put(viewType, scrap);
- if (mMaxScrap.indexOfKey(viewType) < 0) {
- mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP);
- }
- }
- return scrap;
- }
- }
-
- /**
- * A Recycler is responsible for managing scrapped or detached item views for reuse.
- *
- *
A "scrapped" view is a view that is still attached to its parent RecyclerView but
- * that has been marked for removal or reuse.
- *
- *
Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
- * an adapter's data set representing the data at a given position or item ID.
- * If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
- * If not, the view can be quickly reused by the LayoutManager with no further work.
- * Clean views that have not {@link View#isLayoutRequested() requested layout}
- * may be repositioned by a LayoutManager without remeasurement.
- */
- public final class Recycler {
- final ArrayList mAttachedScrap = new ArrayList();
- private ArrayList mChangedScrap = null;
-
- final ArrayList mCachedViews = new ArrayList();
-
- private final List
- mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
-
- private int mViewCacheMax = DEFAULT_CACHE_SIZE;
-
- private RecycledViewPool mRecyclerPool;
-
- private ViewCacheExtension mViewCacheExtension;
-
- private static final int DEFAULT_CACHE_SIZE = 2;
-
- /**
- * Clear scrap views out of this recycler. Detached views contained within a
- * recycled view pool will remain.
- */
- public void clear() {
- mAttachedScrap.clear();
- recycleAndClearCachedViews();
- }
-
- /**
- * Set the maximum number of detached, valid views we should retain for later use.
- *
- * @param viewCount Number of views to keep before sending views to the shared pool
- */
- public void setViewCacheSize(int viewCount) {
- mViewCacheMax = viewCount;
- // first, try the views that can be recycled
- for (int i = mCachedViews.size() - 1; i >= 0 && mCachedViews.size() > viewCount; i--) {
- tryToRecycleCachedViewAt(i);
- }
- // if we could not recycle enough of them, remove some.
- while (mCachedViews.size() > viewCount) {
- mCachedViews.remove(mCachedViews.size() - 1);
- }
- }
-
- /**
- * Returns an unmodifiable list of ViewHolders that are currently in the scrap list.
- *
- * @return List of ViewHolders in the scrap list.
- */
- public List getScrapList() {
- return mUnmodifiableAttachedScrap;
- }
-
- /**
- * Helper method for getViewForPosition.
- *
- * Checks whether a given view holder can be used for the provided position.
- *
- * @param holder ViewHolder
- * @return true if ViewHolder matches the provided position, false otherwise
- */
- boolean validateViewHolderForOffsetPosition(ViewHolder holder) {
- // if it is a removed holder, nothing to verify since we cannot ask adapter anymore
- // if it is not removed, verify the type and id.
- if (holder.isRemoved()) {
- return true;
- }
- if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) {
- throw new IndexOutOfBoundsException("Inconsistency detected. Invalid view holder "
- + "adapter position" + holder);
- }
- if (!mState.isPreLayout()) {
- // don't check type if it is pre-layout.
- final int type = mAdapter.getItemViewType(holder.mPosition);
- if (type != holder.getItemViewType()) {
- return false;
- }
- }
- if (mAdapter.hasStableIds()) {
- return holder.getItemId() == mAdapter.getItemId(holder.mPosition);
- }
- return true;
- }
-
- /**
- * Binds the given View to the position. The View can be a View previously retrieved via
- * {@link #getViewForPosition(int)} or created by
- * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
- *
- * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
- * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
- * wants to handle its own recycling logic.
- *
- * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
- * you don't need to call this method unless you want to bind this View to another position.
- *
- * @param view The view to update.
- * @param position The position of the item to bind to this View.
- */
- public void bindViewToPosition(View view, int position) {
- ViewHolder holder = getChildViewHolderInt(view);
- if (holder == null) {
- throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
- + " pass arbitrary views to this method, they should be created by the "
- + "Adapter");
- }
- final int offsetPosition = mAdapterHelper.findPositionOffset(position);
- if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
- throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
- + "position " + position + "(offset:" + offsetPosition + ")."
- + "state:" + mState.getItemCount());
- }
- mAdapter.bindViewHolder(holder, offsetPosition);
- attachAccessibilityDelegate(view);
- if (mState.isPreLayout()) {
- holder.mPreLayoutPosition = position;
- }
-
- final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
- final LayoutParams rvLayoutParams;
- if (lp == null) {
- rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
- holder.itemView.setLayoutParams(rvLayoutParams);
- } else if (!checkLayoutParams(lp)) {
- rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
- holder.itemView.setLayoutParams(rvLayoutParams);
- } else {
- rvLayoutParams = (LayoutParams) lp;
- }
-
- rvLayoutParams.mInsetsDirty = true;
- rvLayoutParams.mViewHolder = holder;
- rvLayoutParams.mPendingInvalidate = holder.itemView.getParent() == null;
- }
-
- /**
- * RecyclerView provides artificial position range (item count) in pre-layout state and
- * automatically maps these positions to {@link Adapter} positions when
- * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.
- *
- * Usually, LayoutManager does not need to worry about this. However, in some cases, your
- * LayoutManager may need to call some custom component with item positions in which
- * case you need the actual adapter position instead of the pre layout position. You
- * can use this method to convert a pre-layout position to adapter (post layout) position.
- *
- * Note that if the provided position belongs to a deleted ViewHolder, this method will
- * return -1.
- *
- * Calling this method in post-layout state returns the same value back.
- *
- * @param position The pre-layout position to convert. Must be greater or equal to 0 and
- * less than {@link State#getItemCount()}.
- */
- public int convertPreLayoutPositionToPostLayout(int position) {
- if (position < 0 || position >= mState.getItemCount()) {
- throw new IndexOutOfBoundsException("invalid position " + position + ". State "
- + "item count is " + mState.getItemCount());
- }
- if (!mState.isPreLayout()) {
- return position;
- }
- return mAdapterHelper.findPositionOffset(position);
- }
-
- /**
- * Obtain a view initialized for the given position.
- *
- * This method should be used by {@link LayoutManager} implementations to obtain
- * views to represent data from an {@link Adapter}.
- *
- * The Recycler may reuse a scrap or detached view from a shared pool if one is
- * available for the correct view type. If the adapter has not indicated that the
- * data at the given position has changed, the Recycler will attempt to hand back
- * a scrap view that was previously initialized for that data without rebinding.
- *
- * @param position Position to obtain a view for
- * @return A view representing the data at position from adapter
- */
- public View getViewForPosition(int position) {
- return getViewForPosition(position, false);
- }
-
- View getViewForPosition(int position, boolean dryRun) {
- if (position < 0 || position >= mState.getItemCount()) {
- throw new IndexOutOfBoundsException("Invalid item position " + position
- + "(" + position + "). Item count:" + mState.getItemCount());
- }
- boolean fromScrap = false;
- ViewHolder holder = null;
- // 0) If there is a changed scrap, try to find from there
- if (mState.isPreLayout()) {
- holder = getChangedScrapViewForPosition(position);
- fromScrap = holder != null;
- }
- // 1) Find from scrap by position
- if (holder == null) {
- holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
- if (holder != null) {
- if (!validateViewHolderForOffsetPosition(holder)) {
- // recycle this scrap
- if (!dryRun) {
- // we would like to recycle this but need to make sure it is not used by
- // animation logic etc.
- holder.addFlags(ViewHolder.FLAG_INVALID);
- if (holder.isScrap()) {
- removeDetachedView(holder.itemView, false);
- holder.unScrap();
- } else if (holder.wasReturnedFromScrap()) {
- holder.clearReturnedFromScrapFlag();
- }
- recycleViewHolderInternal(holder);
- }
- holder = null;
- } else {
- fromScrap = true;
- }
- }
- }
- if (holder == null) {
- final int offsetPosition = mAdapterHelper.findPositionOffset(position);
-// final int offsetPosition = position;
-// Utils.log("offsetPosition position = " + position);
-// Utils.log("offsetPosition = " + offsetPosition);
-// Utils.log("offsetPosition count = " + mAdapter.getItemCount());
-// Utils.log("offsetPosition count = " + mState.getItemCount());
- if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
- throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
- + "position " + position + "(offset:" + offsetPosition + ")."
- + "state:" + mState.getItemCount()
- + "adpter:" + mAdapter.getClass().getName());
- }
-
- final int type = mAdapter.getItemViewType(offsetPosition);
- // 2) Find from scrap via stable ids, if exists
- if (mAdapter.hasStableIds()) {
- holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
- if (holder != null) {
- // update position
- holder.mPosition = offsetPosition;
- fromScrap = true;
- }
- }
- if (holder == null && mViewCacheExtension != null) {
- // We are NOT sending the offsetPosition because LayoutManager does not
- // know it.
- final View view = mViewCacheExtension
- .getViewForPositionAndType(this, position, type);
- if (view != null) {
- holder = getChildViewHolder(view);
- if (holder == null) {
- throw new IllegalArgumentException("getViewForPositionAndType returned"
- + " a view which does not have a ViewHolder");
- } else if (holder.shouldIgnore()) {
- throw new IllegalArgumentException("getViewForPositionAndType returned"
- + " a view that is ignored. You must call stopIgnoring before"
- + " returning this view.");
- }
- }
- }
- if (holder == null) { // fallback to recycler
- // try recycler.
- // Head to the shared pool.
- if (DEBUG) {
- Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
- + "pool");
- }
- holder = getRecycledViewPool()
- .getRecycledView(mAdapter.getItemViewType(offsetPosition));
- if (holder != null) {
- holder.resetInternal();
- if (FORCE_INVALIDATE_DISPLAY_LIST) {
- invalidateDisplayListInt(holder);
- }
- }
- }
- if (holder == null) {
- holder = mAdapter.createViewHolder(RecyclerView.this,
- mAdapter.getItemViewType(offsetPosition));
- if (DEBUG) {
- Log.d(TAG, "getViewForPosition created new ViewHolder");
- }
- }
- }
- boolean bound = false;
- if (mState.isPreLayout() && holder.isBound()) {
- // do not update unless we absolutely have to.
- holder.mPreLayoutPosition = position;
- } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
- if (DEBUG && holder.isRemoved()) {
- throw new IllegalStateException("Removed holder should be bound and it should"
- + " come here only in pre-layout. Holder: " + holder);
- }
- final int offsetPosition = mAdapterHelper.findPositionOffset(position);
- mAdapter.bindViewHolder(holder, offsetPosition);
- attachAccessibilityDelegate(holder.itemView);
- bound = true;
- if (mState.isPreLayout()) {
- holder.mPreLayoutPosition = position;
- }
- }
-
- final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
- final LayoutParams rvLayoutParams;
- if (lp == null) {
- rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
- holder.itemView.setLayoutParams(rvLayoutParams);
- } else if (!checkLayoutParams(lp)) {
- rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
- holder.itemView.setLayoutParams(rvLayoutParams);
- } else {
- rvLayoutParams = (LayoutParams) lp;
- }
- rvLayoutParams.mViewHolder = holder;
- rvLayoutParams.mPendingInvalidate = fromScrap && bound;
- return holder.itemView;
- }
-
- private void attachAccessibilityDelegate(View itemView) {
- if (mAccessibilityManager != null && mAccessibilityManager.isEnabled()) {
- if (ViewCompat.getImportantForAccessibility(itemView) ==
- ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- ViewCompat.setImportantForAccessibility(itemView,
- ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
- if (!ViewCompat.hasAccessibilityDelegate(itemView)) {
- ViewCompat.setAccessibilityDelegate(itemView,
- mAccessibilityDelegate.getItemDelegate());
- }
- }
- }
-
- private void invalidateDisplayListInt(ViewHolder holder) {
- if (holder.itemView instanceof ViewGroup) {
- invalidateDisplayListInt((ViewGroup) holder.itemView, false);
- }
- }
-
- private void invalidateDisplayListInt(ViewGroup viewGroup, boolean invalidateThis) {
- for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
- final View view = viewGroup.getChildAt(i);
- if (view instanceof ViewGroup) {
- invalidateDisplayListInt((ViewGroup) view, true);
- }
- }
- if (!invalidateThis) {
- return;
- }
- // we need to force it to become invisible
- if (viewGroup.getVisibility() == View.INVISIBLE) {
- viewGroup.setVisibility(View.VISIBLE);
- viewGroup.setVisibility(View.INVISIBLE);
- } else {
- final int visibility = viewGroup.getVisibility();
- viewGroup.setVisibility(View.INVISIBLE);
- viewGroup.setVisibility(visibility);
- }
- }
-
- /**
- * Recycle a detached view. The specified view will be added to a pool of views
- * for later rebinding and reuse.
- *
- *
A view must be fully detached before it may be recycled. If the View is scrapped,
- * it will be removed from scrap list.
- *
- * @param view Removed view for recycling
- * @see LayoutManager#removeAndRecycleView(View, Recycler)
- */
- public void recycleView(View view) {
- // This public recycle method tries to make view recycle-able since layout manager
- // intended to recycle this view (e.g. even if it is in scrap or change cache)
- ViewHolder holder = getChildViewHolderInt(view);
- if (holder.isScrap()) {
- holder.unScrap();
- } else if (holder.wasReturnedFromScrap()){
- holder.clearReturnedFromScrapFlag();
- }
- recycleViewHolderInternal(holder);
- }
-
- /**
- * Internally, use this method instead of {@link #recycleView(View)} to
- * catch potential bugs.
- * @param view
- */
- void recycleViewInternal(View view) {
- recycleViewHolderInternal(getChildViewHolderInt(view));
- }
-
- void recycleAndClearCachedViews() {
- final int count = mCachedViews.size();
- for (int i = count - 1; i >= 0; i--) {
- tryToRecycleCachedViewAt(i);
- }
- mCachedViews.clear();
- }
-
- /**
- * Tries to recyle a cached view and removes the view from the list if and only if it
- * is recycled.
- *
- * @param cachedViewIndex The index of the view in cached views list
- * @return True if item is recycled
- */
- boolean tryToRecycleCachedViewAt(int cachedViewIndex) {
- if (DEBUG) {
- Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
- }
- ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
- if (DEBUG) {
- Log.d(TAG, "CachedViewHolder to be recycled(if recycleable): " + viewHolder);
- }
- if (viewHolder.isRecyclable()) {
- getRecycledViewPool().putRecycledView(viewHolder);
- dispatchViewRecycled(viewHolder);
- mCachedViews.remove(cachedViewIndex);
- return true;
- }
- return false;
- }
-
- /**
- * internal implementation checks if view is scrapped or attached and throws an exception
- * if so.
- * Public version un-scraps before calling recycle.
- */
- void recycleViewHolderInternal(ViewHolder holder) {
- if (holder.isScrap() || holder.itemView.getParent() != null) {
- throw new IllegalArgumentException(
- "Scrapped or attached views may not be recycled. isScrap:"
- + holder.isScrap() + " isAttached:"
- + (holder.itemView.getParent() != null));
- }
-
- if (holder.shouldIgnore()) {
- throw new IllegalArgumentException("Trying to recycle an ignored view holder. You"
- + " should first call stopIgnoringView(view) before calling recycle.");
- }
- if (holder.isRecyclable()) {
- boolean cached = false;
- if (!holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved()) &&
- !holder.isChanged()) {
- // Retire oldest cached views first
- if (mCachedViews.size() == mViewCacheMax && !mCachedViews.isEmpty()) {
- for (int i = 0; i < mCachedViews.size(); i++) {
- if (tryToRecycleCachedViewAt(i)) {
- break;
- }
- }
- }
- if (mCachedViews.size() < mViewCacheMax) {
- mCachedViews.add(holder);
- cached = true;
- }
- }
- if (!cached) {
- getRecycledViewPool().putRecycledView(holder);
- dispatchViewRecycled(holder);
- }
- } else if (DEBUG) {
- Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
- + "re-visit here. We are stil removing it from animation lists");
- }
- // even if the holder is not removed, we still call this method so that it is removed
- // from view holder lists.
- mState.onViewRecycled(holder);
- }
-
- /**
- * Used as a fast path for unscrapping and recycling a view during a bulk operation.
- * The caller must call {@link #clearScrap()} when it's done to update the recycler's
- * internal bookkeeping.
- */
- void quickRecycleScrapView(View view) {
- final ViewHolder holder = getChildViewHolderInt(view);
- holder.mScrapContainer = null;
- holder.clearReturnedFromScrapFlag();
- recycleViewHolderInternal(holder);
- }
-
- /**
- * Mark an attached view as scrap.
- *
- *
"Scrap" views are still attached to their parent RecyclerView but are eligible
- * for rebinding and reuse. Requests for a view for a given position may return a
- * reused or rebound scrap view instance.
- *
- * @param view View to scrap
- */
- void scrapView(View view) {
- final ViewHolder holder = getChildViewHolderInt(view);
- holder.setScrapContainer(this);
- if (!holder.isChanged() || !supportsChangeAnimations()) {
- if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
- throw new IllegalArgumentException("Called scrap view with an invalid view."
- + " Invalid views cannot be reused from scrap, they should rebound from"
- + " recycler pool.");
- }
- mAttachedScrap.add(holder);
- } else {
- if (mChangedScrap == null) {
- mChangedScrap = new ArrayList();
- }
- mChangedScrap.add(holder);
- }
- }
-
- /**
- * Remove a previously scrapped view from the pool of eligible scrap.
- *
- *
This view will no longer be eligible for reuse until re-scrapped or
- * until it is explicitly removed and recycled.
- */
- void unscrapView(ViewHolder holder) {
- if (!holder.isChanged() || !supportsChangeAnimations() || mChangedScrap == null) {
- mAttachedScrap.remove(holder);
- } else {
- mChangedScrap.remove(holder);
- }
- holder.mScrapContainer = null;
- holder.clearReturnedFromScrapFlag();
- }
-
- int getScrapCount() {
- return mAttachedScrap.size();
- }
-
- View getScrapViewAt(int index) {
- return mAttachedScrap.get(index).itemView;
- }
-
- void clearScrap() {
- mAttachedScrap.clear();
- }
-
- ViewHolder getChangedScrapViewForPosition(int position) {
- // If pre-layout, check the changed scrap for an exact match.
- final int changedScrapSize;
- if (mChangedScrap == null || (changedScrapSize = mChangedScrap.size()) == 0) {
- return null;
- }
- // find by position
- for (int i = 0; i < changedScrapSize; i++) {
- final ViewHolder holder = mChangedScrap.get(i);
- if (!holder.wasReturnedFromScrap() && holder.getPosition() == position) {
- holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
- return holder;
- }
- }
- // find by id
- if (mAdapter.hasStableIds()) {
- final int offsetPosition = mAdapterHelper.findPositionOffset(position);
- if (offsetPosition > 0 && offsetPosition < mAdapter.getItemCount()) {
- final long id = mAdapter.getItemId(offsetPosition);
- for (int i = 0; i < changedScrapSize; i++) {
- final ViewHolder holder = mChangedScrap.get(i);
- if (!holder.wasReturnedFromScrap() && holder.getItemId() == id) {
- holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
- return holder;
- }
- }
- }
- }
- return null;
- }
-
- /**
- * Returns a scrap view for the position. If type is not INVALID_TYPE, it also checks if
- * ViewHolder's type matches the provided type.
- *
- * @param position Item position
- * @param type View type
- * @param dryRun Does a dry run, finds the ViewHolder but does not remove
- * @return a ViewHolder that can be re-used for this position.
- */
- ViewHolder getScrapViewForPosition(int position, int type, boolean dryRun) {
- final int scrapCount = mAttachedScrap.size();
-
- // Try first for an exact, non-invalid match from scrap.
- for (int i = 0; i < scrapCount; i++) {
- final ViewHolder holder = mAttachedScrap.get(i);
- if (!holder.wasReturnedFromScrap() && holder.getPosition() == position
- && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
- if (type != INVALID_TYPE && holder.getItemViewType() != type) {
- Log.e(TAG, "Scrap view for position " + position + " isn't dirty but has" +
- " wrong view type! (found " + holder.getItemViewType() +
- " but expected " + type + ")");
- break;
- }
- holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
- return holder;
- }
- }
-
- if (!dryRun) {
- View view = mChildHelper.findHiddenNonRemovedView(position, type);
- if (view != null) {
- // ending the animation should cause it to get recycled before we reuse it
- mItemAnimator.endAnimation(getChildViewHolder(view));
- }
- }
-
- // Search in our first-level recycled view cache.
- final int cacheSize = mCachedViews.size();
- for (int i = 0; i < cacheSize; i++) {
- final ViewHolder holder = mCachedViews.get(i);
- // invalid view holders may be in cache if adapter has stable ids as they can be
- // retrieved via getScrapViewForId
- if (!holder.isInvalid() && holder.getPosition() == position) {
- if (!dryRun) {
- mCachedViews.remove(i);
- }
- if (DEBUG) {
- Log.d(TAG, "getScrapViewForPosition(" + position + ", " + type +
- ") found match in cache: " + holder);
- }
- return holder;
- }
- }
- return null;
- }
-
- ViewHolder getScrapViewForId(long id, int type, boolean dryRun) {
- // Look in our attached views first
- final int count = mAttachedScrap.size();
- for (int i = count - 1; i >= 0; i--) {
- final ViewHolder holder = mAttachedScrap.get(i);
- if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
- if (type == holder.getItemViewType()) {
- holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
- if (holder.isRemoved()) {
- // this might be valid in two cases:
- // > item is removed but we are in pre-layout pass
- // >> do nothing. return as is. make sure we don't rebind
- // > item is removed then added to another position and we are in
- // post layout.
- // >> remove removed and invalid flags, add update flag to rebind
- // because item was invisible to us and we don't know what happened in
- // between.
- if (!mState.isPreLayout()) {
- holder.setFlags(ViewHolder.FLAG_UPDATE, ViewHolder.FLAG_UPDATE |
- ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED);
- }
- }
- return holder;
- } else if (!dryRun) {
- // Recycle this scrap. Type mismatch.
- mAttachedScrap.remove(i);
- removeDetachedView(holder.itemView, false);
- quickRecycleScrapView(holder.itemView);
- }
- }
- }
-
- // Search the first-level cache
- final int cacheSize = mCachedViews.size();
- for (int i = cacheSize - 1; i >= 0; i--) {
- final ViewHolder holder = mCachedViews.get(i);
- if (holder.getItemId() == id) {
- if (type == holder.getItemViewType()) {
- if (!dryRun) {
- mCachedViews.remove(i);
- }
- return holder;
- } else if (!dryRun) {
- tryToRecycleCachedViewAt(i);
- }
- }
- }
- return null;
- }
-
- void dispatchViewRecycled(ViewHolder holder) {
- if (mRecyclerListener != null) {
- mRecyclerListener.onViewRecycled(holder);
- }
- if (mAdapter != null) {
- mAdapter.onViewRecycled(holder);
- }
- if (mState != null) {
- mState.onViewRecycled(holder);
- }
- if (DEBUG) Log.d(TAG, "dispatchViewRecycled: " + holder);
- }
-
- void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
- boolean compatibleWithPrevious) {
- clear();
- getRecycledViewPool().onAdapterChanged(oldAdapter, newAdapter, compatibleWithPrevious);
- }
-
- void offsetPositionRecordsForMove(int from, int to) {
- final int start, end, inBetweenOffset;
- if (from < to) {
- start = from;
- end = to;
- inBetweenOffset = -1;
- } else {
- start = to;
- end = from;
- inBetweenOffset = 1;
- }
- final int cachedCount = mCachedViews.size();
- for (int i = 0; i < cachedCount; i++) {
- final ViewHolder holder = mCachedViews.get(i);
- if (holder == null || holder.mPosition < start || holder.mPosition > end) {
- continue;
- }
- if (holder.mPosition == from) {
- holder.offsetPosition(to - from, false);
- } else {
- holder.offsetPosition(inBetweenOffset, false);
- }
- if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForMove cached child " + i + " holder " +
- holder);
- }
- }
- }
-
- void offsetPositionRecordsForInsert(int insertedAt, int count) {
- final int cachedCount = mCachedViews.size();
- for (int i = 0; i < cachedCount; i++) {
- final ViewHolder holder = mCachedViews.get(i);
- if (holder != null && holder.getPosition() >= insertedAt) {
- if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
- holder + " now at position " + (holder.mPosition + count));
- }
- holder.offsetPosition(count, true);
- }
- }
- }
-
- /**
- * @param removedFrom Remove start index
- * @param count Remove count
- * @param applyToPreLayout If true, changes will affect ViewHolder's pre-layout position, if
- * false, they'll be applied before the second layout pass
- */
- void offsetPositionRecordsForRemove(int removedFrom, int count, boolean applyToPreLayout) {
- final int removedEnd = removedFrom + count;
- final int cachedCount = mCachedViews.size();
- for (int i = cachedCount - 1; i >= 0; i--) {
- final ViewHolder holder = mCachedViews.get(i);
- if (holder != null) {
- if (holder.getPosition() >= removedEnd) {
- if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
- " holder " + holder + " now at position " +
- (holder.mPosition - count));
- }
- holder.offsetPosition(-count, applyToPreLayout);
- } else if (holder.getPosition() >= removedFrom) {
- // Item for this view was removed. Dump it from the cache.
- if (!tryToRecycleCachedViewAt(i)) {
- // if we cannot recycle it, at least invalidate so that we won't return
- // it by position.
- holder.addFlags(ViewHolder.FLAG_INVALID);
- if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
- " holder " + holder + " now flagged as invalid because it "
- + "could not be recycled");
- }
- } else if (DEBUG) {
- Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
- " holder " + holder + " now placed in pool");
- }
- }
- }
- }
- }
-
- void setViewCacheExtension(ViewCacheExtension extension) {
- mViewCacheExtension = extension;
- }
-
- void setRecycledViewPool(RecycledViewPool pool) {
- if (mRecyclerPool != null) {
- mRecyclerPool.detach();
- }
- mRecyclerPool = pool;
- if (pool != null) {
- mRecyclerPool.attach(getAdapter());
- }
- }
-
- RecycledViewPool getRecycledViewPool() {
- if (mRecyclerPool == null) {
- mRecyclerPool = new RecycledViewPool();
- }
- return mRecyclerPool;
- }
-
- void viewRangeUpdate(int positionStart, int itemCount) {
- final int positionEnd = positionStart + itemCount;
- final int cachedCount = mCachedViews.size();
- for (int i = 0; i < cachedCount; i++) {
- final ViewHolder holder = mCachedViews.get(i);
- if (holder == null) {
- continue;
- }
-
- final int pos = holder.getPosition();
- if (pos >= positionStart && pos < positionEnd) {
- holder.addFlags(ViewHolder.FLAG_UPDATE);
- // cached views should not be flagged as changed because this will cause them
- // to animate when they are returned from cache.
- }
- }
- }
-
- void markKnownViewsInvalid() {
- if (mAdapter != null && mAdapter.hasStableIds()) {
- final int cachedCount = mCachedViews.size();
- for (int i = 0; i < cachedCount; i++) {
- final ViewHolder holder = mCachedViews.get(i);
- if (holder != null) {
- holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
- }
- }
- } else {
- // we cannot re-use cached views in this case. Recycle the ones we can and flag
- // the remaining as invalid so that they can be recycled later on (when their
- // animations end.)
- for (int i = mCachedViews.size() - 1; i >= 0; i--) {
- if (!tryToRecycleCachedViewAt(i)) {
- final ViewHolder holder = mCachedViews.get(i);
- holder.addFlags(ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
- }
- }
- }
-
- }
-
- void clearOldPositions() {
- final int cachedCount = mCachedViews.size();
- for (int i = 0; i < cachedCount; i++) {
- final ViewHolder holder = mCachedViews.get(i);
- holder.clearOldPosition();
- }
- final int scrapCount = mAttachedScrap.size();
- for (int i = 0; i < scrapCount; i++) {
- mAttachedScrap.get(i).clearOldPosition();
- }
- if (mChangedScrap != null) {
- final int changedScrapCount = mChangedScrap.size();
- for (int i = 0; i < changedScrapCount; i++) {
- mChangedScrap.get(i).clearOldPosition();
- }
- }
- }
-
- void markItemDecorInsetsDirty() {
- final int cachedCount = mCachedViews.size();
- for (int i = 0; i < cachedCount; i++) {
- final ViewHolder holder = mCachedViews.get(i);
- LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
- if (layoutParams != null) {
- layoutParams.mInsetsDirty = true;
- }
- }
- }
- }
-
- /**
- * ViewCacheExtension is a helper class to provide an additional layer of view caching that can
- * ben controlled by the developer.
- *
- * When {@link Recycler#getViewForPosition(int)} is called, Recycler checks attached scrap and
- * first level cache to find a matching View. If it cannot find a suitable View, Recycler will
- * call the {@link #getViewForPositionAndType(Recycler, int, int)} before checking
- * {@link RecycledViewPool}.
- *
- * Note that, Recycler never sends Views to this method to be cached. It is developers
- * responsibility to decide whether they want to keep their Views in this custom cache or let
- * the default recycling policy handle it.
- */
- public abstract static class ViewCacheExtension {
-
- /**
- * Returns a View that can be binded to the given Adapter position.
- *
- * This method should not create a new View. Instead, it is expected to return
- * an already created View that can be re-used for the given type and position.
- * If the View is marked as ignored, it should first call
- * {@link LayoutManager#stopIgnoringView(View)} before returning the View.
- *
- * RecyclerView will re-bind the returned View to the position if necessary.
- *
- * @param recycler The Recycler that can be used to bind the View
- * @param position The adapter position
- * @param type The type of the View, defined by adapter
- * @return A View that is bound to the given position or NULL if there is no View to re-use
- * @see LayoutManager#ignoreView(View)
- */
- abstract public View getViewForPositionAndType(Recycler recycler, int position, int type);
- }
-
- /**
- * Base class for an Adapter
- *
- *
Adapters provide a binding from an app-specific data set to views that are displayed
- * within a {@link RecyclerView}.
- */
- public static abstract class Adapter {
- private final AdapterDataObservable mObservable = new AdapterDataObservable();
- private boolean mHasStableIds = false;
-
- /**
- * Called when RecyclerView needs a new {@link ViewHolder} of the given type to represent
- * an item.
- *
- * This new ViewHolder should be constructed with a new View that can represent the items
- * of the given type. You can either create a new View manually or inflate it from an XML
- * layout file.
- *
- * The new ViewHolder will be used to display items of the adapter using
- * {@link #onBindViewHolder(ViewHolder, int)}. Since it will be re-used to display different
- * items in the data set, it is a good idea to cache references to sub views of the View to
- * avoid unnecessary {@link View#findViewById(int)} calls.
- *
- * @param parent The ViewGroup into which the new View will be added after it is bound to
- * an adapter position.
- * @param viewType The view type of the new View.
- *
- * @return A new ViewHolder that holds a View of the given view type.
- * @see #getItemViewType(int)
- * @see #onBindViewHolder(ViewHolder, int)
- */
- public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
-
- /**
- * Called by RecyclerView to display the data at the specified position. This method
- * should update the contents of the {@link ViewHolder#itemView} to reflect the item at
- * the given position.
- *
- * Note that unlike {@link android.widget.ListView}, RecyclerView will not call this
- * method again if the position of the item changes in the data set unless the item itself
- * is invalidated or the new position cannot be determined. For this reason, you should only
- * use the position parameter while acquiring the related data item inside this
- * method and should not keep a copy of it. If you need the position of an item later on
- * (e.g. in a click listener), use {@link ViewHolder#getPosition()} which will have the
- * updated position.
- *
- * @param holder The ViewHolder which should be updated to represent the contents of the
- * item at the given position in the data set.
- * @param position The position of the item within the adapter's data set.
- */
- public abstract void onBindViewHolder(VH holder, int position);
-
- /**
- * This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
- * {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
- *
- * @see #onCreateViewHolder(ViewGroup, int)
- */
- public final VH createViewHolder(ViewGroup parent, int viewType) {
- final VH holder = onCreateViewHolder(parent, viewType);
- holder.mItemViewType = viewType;
- return holder;
- }
-
- /**
- * This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
- * {@link ViewHolder} contents with the item at the given position and also sets up some
- * private fields to be used by RecyclerView.
- *
- * @see #onBindViewHolder(ViewHolder, int)
- */
- public final void bindViewHolder(VH holder, int position) {
- holder.mPosition = position;
- if (hasStableIds()) {
- holder.mItemId = getItemId(position);
- }
- onBindViewHolder(holder, position);
- holder.setFlags(ViewHolder.FLAG_BOUND,
- ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID);
- }
-
- /**
- * Return the view type of the item at position for the purposes
- * of view recycling.
- *
- *
The default implementation of this method returns 0, making the assumption of
- * a single view type for the adapter. Unlike ListView adapters, types need not
- * be contiguous. Consider using id resources to uniquely identify item view types.
- *
- * @param position position to query
- * @return integer value identifying the type of the view needed to represent the item at
- * position. Type codes need not be contiguous.
- */
- public int getItemViewType(int position) {
- return 0;
- }
-
- /**
- * Indicates whether each item in the data set can be represented with a unique identifier
- * of type {@link Long}.
- *
- * @param hasStableIds Whether items in data set have unique identifiers or not.
- * @see #hasStableIds()
- * @see #getItemId(int)
- */
- public void setHasStableIds(boolean hasStableIds) {
- if (hasObservers()) {
- throw new IllegalStateException("Cannot change whether this adapter has " +
- "stable IDs while the adapter has registered observers.");
- }
- mHasStableIds = hasStableIds;
- }
-
- /**
- * Return the stable ID for the item at position. If {@link #hasStableIds()}
- * would return false this method should return {@link #NO_ID}. The default implementation
- * of this method returns {@link #NO_ID}.
- *
- * @param position Adapter position to query
- * @return the stable ID of the item at position
- */
- public long getItemId(int position) {
- return NO_ID;
- }
-
- /**
- * Returns the total number of items in the data set hold by the adapter.
- *
- * @return The total number of items in this adapter.
- */
- public abstract int getItemCount();
-
- /**
- * Returns true if this adapter publishes a unique long value that can
- * act as a key for the item at a given position in the data set. If that item is relocated
- * in the data set, the ID returned for that item should be the same.
- *
- * @return true if this adapter's items have stable IDs
- */
- public final boolean hasStableIds() {
- return mHasStableIds;
- }
-
- /**
- * Called when a view created by this adapter has been recycled.
- *
- *
A view is recycled when a {@link LayoutManager} decides that it no longer
- * needs to be attached to its parent {@link RecyclerView}. This can be because it has
- * fallen out of visibility or a set of cached views represented by views still
- * attached to the parent RecyclerView. If an item view has large or expensive data
- * bound to it such as large bitmaps, this may be a good place to release those
- * resources.
- *
- * @param holder The ViewHolder for the view being recycled
- */
- public void onViewRecycled(VH holder) {
- }
-
- /**
- * Called when a view created by this adapter has been attached to a window.
- *
- *
This can be used as a reasonable signal that the view is about to be seen
- * by the user. If the adapter previously freed any resources in
- * {@link #onViewDetachedFromWindow(ViewHolder) onViewDetachedFromWindow}
- * those resources should be restored here.
- *
- * @param holder Holder of the view being attached
- */
- public void onViewAttachedToWindow(VH holder) {
- }
-
- /**
- * Called when a view created by this adapter has been detached from its window.
- *
- *
Becoming detached from the window is not necessarily a permanent condition;
- * the consumer of an Adapter's views may choose to cache views offscreen while they
- * are not visible, attaching an detaching them as appropriate.
- *
- * @param holder Holder of the view being detached
- */
- public void onViewDetachedFromWindow(VH holder) {
- }
-
- /**
- * Returns true if one or more observers are attached to this adapter.
- *
- * @return true if this adapter has observers
- */
- public final boolean hasObservers() {
- return mObservable.hasObservers();
- }
-
- /**
- * Register a new observer to listen for data changes.
- *
- *
The adapter may publish a variety of events describing specific changes.
- * Not all adapters may support all change types and some may fall back to a generic
- * {@link AdapterDataObserver#onChanged()
- * "something changed"} event if more specific data is not available.
- *
- *
Components registering observers with an adapter are responsible for
- * {@link #unregisterAdapterDataObserver(AdapterDataObserver)
- * unregistering} those observers when finished.
- *
- * @param observer Observer to register
- *
- * @see #unregisterAdapterDataObserver(AdapterDataObserver)
- */
- public void registerAdapterDataObserver(AdapterDataObserver observer) {
- mObservable.registerObserver(observer);
- }
-
- /**
- * Unregister an observer currently listening for data changes.
- *
- *
The unregistered observer will no longer receive events about changes
- * to the adapter.
- *
- * @param observer Observer to unregister
- *
- * @see #registerAdapterDataObserver(AdapterDataObserver)
- */
- public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
- mObservable.unregisterObserver(observer);
- }
-
- /**
- * Notify any registered observers that the data set has changed.
- *
- *
There are two different classes of data change events, item changes and structural
- * changes. Item changes are when a single item has its data updated but no positional
- * changes have occurred. Structural changes are when items are inserted, removed or moved
- * within the data set.
- *
- *
This event does not specify what about the data set has changed, forcing
- * any observers to assume that all existing items and structure may no longer be valid.
- * LayoutManagers will be forced to fully rebind and relayout all visible views.
- *
- *
RecyclerView will attempt to synthesize visible structural change events
- * for adapters that report that they have {@link #hasStableIds() stable IDs} when
- * this method is used. This can help for the purposes of animation and visual
- * object persistence but individual item views will still need to be rebound
- * and relaid out.
- *
- *
If you are writing an adapter it will always be more efficient to use the more
- * specific change events if you can. Rely on notifyDataSetChanged()
- * as a last resort.
- *
- * @see #notifyItemChanged(int)
- * @see #notifyItemInserted(int)
- * @see #notifyItemRemoved(int)
- * @see #notifyItemRangeChanged(int, int)
- * @see #notifyItemRangeInserted(int, int)
- * @see #notifyItemRangeRemoved(int, int)
- */
- public final void notifyDataSetChanged() {
- mObservable.notifyChanged();
- }
-
- /**
- * Notify any registered observers that the item at position has changed.
- *
- *
This is an item change event, not a structural change event. It indicates that any
- * reflection of the data at position is out of date and should be updated.
- * The item at position retains the same identity.
- *
- * @param position Position of the item that has changed
- *
- * @see #notifyItemRangeChanged(int, int)
- */
- public final void notifyItemChanged(int position) {
- mObservable.notifyItemRangeChanged(position, 1);
- }
-
- /**
- * Notify any registered observers that the itemCount items starting at
- * position positionStart have changed.
- *
- *
This is an item change event, not a structural change event. It indicates that
- * any reflection of the data in the given position range is out of date and should
- * be updated. The items in the given range retain the same identity.
- *
- * @param positionStart Position of the first item that has changed
- * @param itemCount Number of items that have changed
- *
- * @see #notifyItemChanged(int)
- */
- public final void notifyItemRangeChanged(int positionStart, int itemCount) {
- mObservable.notifyItemRangeChanged(positionStart, itemCount);
- }
-
- /**
- * Notify any registered observers that the item reflected at position
- * has been newly inserted. The item previously at position is now at
- * position position + 1.
- *
- *
This is a structural change event. Representations of other existing items in the
- * data set are still considered up to date and will not be rebound, though their
- * positions may be altered.
- *
- * @param position Position of the newly inserted item in the data set
- *
- * @see #notifyItemRangeInserted(int, int)
- */
- public final void notifyItemInserted(int position) {
- mObservable.notifyItemRangeInserted(position, 1);
- }
-
- /**
- * Notify any registered observers that the item reflected at fromPosition
- * has been moved to toPosition.
- *
- *
This is a structural change event. Representations of other existing items in the
- * data set are still considered up to date and will not be rebound, though their
- * positions may be altered.
- *
- * @param fromPosition Previous position of the item.
- * @param toPosition New position of the item.
- */
- public final void notifyItemMoved(int fromPosition, int toPosition) {
- mObservable.notifyItemMoved(fromPosition, toPosition);
- }
-
- /**
- * Notify any registered observers that the currently reflected itemCount
- * items starting at positionStart have been newly inserted. The items
- * previously located at positionStart and beyond can now be found starting
- * at position positionStart + itemCount.
- *
- *
This is a structural change event. Representations of other existing items in the
- * data set are still considered up to date and will not be rebound, though their positions
- * may be altered.
- *
- * @param positionStart Position of the first item that was inserted
- * @param itemCount Number of items inserted
- *
- * @see #notifyItemInserted(int)
- */
- public final void notifyItemRangeInserted(int positionStart, int itemCount) {
- mObservable.notifyItemRangeInserted(positionStart, itemCount);
- }
-
- /**
- * Notify any registered observers that the item previously located at position
- * has been removed from the data set. The items previously located at and after
- * position may now be found at oldPosition - 1.
- *
- *
This is a structural change event. Representations of other existing items in the
- * data set are still considered up to date and will not be rebound, though their positions
- * may be altered.
- *
- * @param position Position of the item that has now been removed
- *
- * @see #notifyItemRangeRemoved(int, int)
- */
- public final void notifyItemRemoved(int position) {
- mObservable.notifyItemRangeRemoved(position, 1);
- }
-
- /**
- * Notify any registered observers that the itemCount items previously
- * located at positionStart have been removed from the data set. The items
- * previously located at and after positionStart + itemCount may now be found
- * at oldPosition - itemCount.
- *
- *
This is a structural change event. Representations of other existing items in the data
- * set are still considered up to date and will not be rebound, though their positions
- * may be altered.
- *
- * @param positionStart Previous position of the first item that was removed
- * @param itemCount Number of items removed from the data set
- */
- public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
- mObservable.notifyItemRangeRemoved(positionStart, itemCount);
- }
- }
-
- private void dispatchChildDetached(View child) {
- if (mAdapter != null) {
- mAdapter.onViewDetachedFromWindow(getChildViewHolderInt(child));
- }
- onChildDetachedFromWindow(child);
- }
-
- private void dispatchChildAttached(View child) {
- if (mAdapter != null) {
- mAdapter.onViewAttachedToWindow(getChildViewHolderInt(child));
- }
- onChildAttachedToWindow(child);
- }
-
- /**
- * A LayoutManager is responsible for measuring and positioning item views
- * within a RecyclerView as well as determining the policy for when to recycle
- * item views that are no longer visible to the user. By changing the LayoutManager
- * a RecyclerView can be used to implement a standard vertically scrolling list,
- * a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
- * layout managers are provided for general use.
- */
- public static abstract class LayoutManager {
- ChildHelper mChildHelper;
- RecyclerView mRecyclerView;
-
- @Nullable
- SmoothScroller mSmoothScroller;
-
- private boolean mRequestedSimpleAnimations = false;
-
- void setRecyclerView(RecyclerView recyclerView) {
- if (recyclerView == null) {
- mRecyclerView = null;
- mChildHelper = null;
- } else {
- mRecyclerView = recyclerView;
- mChildHelper = recyclerView.mChildHelper;
- }
-
- }
-
- /**
- * Calls {@code RecyclerView#requestLayout} on the underlying RecyclerView
- */
- public void requestLayout() {
- if(mRecyclerView != null) {
- mRecyclerView.requestLayout();
- }
- }
-
- /**
- * Checks if RecyclerView is in the middle of a layout or scroll and throws an
- * {@link IllegalStateException} if it is not.
- *
- * @param message The message for the exception. Can be null.
- * @see #assertNotInLayoutOrScroll(String)
- */
- public void assertInLayoutOrScroll(String message) {
- if (mRecyclerView != null) {
- mRecyclerView.assertInLayoutOrScroll(message);
- }
- }
-
- /**
- * Checks if RecyclerView is in the middle of a layout or scroll and throws an
- * {@link IllegalStateException} if it is.
- *
- * @param message The message for the exception. Can be null.
- * @see #assertInLayoutOrScroll(String)
- */
- public void assertNotInLayoutOrScroll(String message) {
- if (mRecyclerView != null) {
- mRecyclerView.assertNotInLayoutOrScroll(message);
- }
- }
-
- /**
- * Returns whether this LayoutManager supports automatic item animations.
- * A LayoutManager wishing to support item animations should obey certain
- * rules as outlined in {@link #onLayoutChildren(Recycler, State)}.
- * The default return value is false, so subclasses of LayoutManager
- * will not get predictive item animations by default.
- *
- *
Whether item animations are enabled in a RecyclerView is determined both
- * by the return value from this method and the
- * {@link RecyclerView#setItemAnimator(ItemAnimator) ItemAnimator} set on the
- * RecyclerView itself. If the RecyclerView has a non-null ItemAnimator but this
- * method returns false, then simple item animations will be enabled, in which
- * views that are moving onto or off of the screen are simply faded in/out. If
- * the RecyclerView has a non-null ItemAnimator and this method returns true,
- * then there will be two calls to {@link #onLayoutChildren(Recycler, State)} to
- * setup up the information needed to more intelligently predict where appearing
- * and disappearing views should be animated from/to.
- *
- * @return true if predictive item animations should be enabled, false otherwise
- */
- public boolean supportsPredictiveItemAnimations() {
- return false;
- }
-
- /**
- * Called when this LayoutManager is both attached to a RecyclerView and that RecyclerView
- * is attached to a window.
- *
- *
Subclass implementations should always call through to the superclass implementation.
- *
- *
- * @param view The RecyclerView this LayoutManager is bound to
- */
- public void onAttachedToWindow(RecyclerView view) {
- }
-
- /**
- * @deprecated
- * override {@link #onDetachedFromWindow(RecyclerView, Recycler)}
- */
- @Deprecated
- public void onDetachedFromWindow(RecyclerView view) {
-
- }
-
- /**
- * Called when this LayoutManager is detached from its parent RecyclerView or when
- * its parent RecyclerView is detached from its window.
- *
- *
Subclass implementations should always call through to the superclass implementation.
- *
- *
- * @param view The RecyclerView this LayoutManager is bound to
- * @param recycler The recycler to use if you prefer to recycle your children instead of
- * keeping them around.
- */
- public void onDetachedFromWindow(RecyclerView view, Recycler recycler) {
- onDetachedFromWindow(view);
- }
-
- /**
- * Check if the RecyclerView is configured to clip child views to its padding.
- *
- * @return true if this RecyclerView clips children to its padding, false otherwise
- */
- public boolean getClipToPadding() {
- return mRecyclerView != null && mRecyclerView.mClipToPadding;
- }
-
- /**
- * Lay out all relevant child views from the given adapter.
- *
- * The LayoutManager is in charge of the behavior of item animations. By default,
- * RecyclerView has a non-null {@link #getItemAnimator() ItemAnimator}, and simple
- * item animations are enabled. This means that add/remove operations on the
- * adapter will result in animations to add new or appearing items, removed or
- * disappearing items, and moved items. If a LayoutManager returns false from
- * {@link #supportsPredictiveItemAnimations()}, which is the default, and runs a
- * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
- * RecyclerView will have enough information to run those animations in a simple
- * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
- * simple fade views in and out, whether they are actuall added/removed or whether
- * they are moved on or off the screen due to other add/remove operations.
- *
- *
A LayoutManager wanting a better item animation experience, where items can be
- * animated onto and off of the screen according to where the items exist when they
- * are not on screen, then the LayoutManager should return true from
- * {@link #supportsPredictiveItemAnimations()} and add additional logic to
- * {@link #onLayoutChildren(Recycler, State)}. Supporting predictive animations
- * means that {@link #onLayoutChildren(Recycler, State)} will be called twice;
- * once as a "pre" layout step to determine where items would have been prior to
- * a real layout, and again to do the "real" layout. In the pre-layout phase,
- * items will remember their pre-layout positions to allow them to be laid out
- * appropriately. Also, {@link LayoutParams#isItemRemoved() removed} items will
- * be returned from the scrap to help determine correct placement of other items.
- * These removed items should not be added to the child list, but should be used
- * to help calculate correct positioning of other views, including views that
- * were not previously onscreen (referred to as APPEARING views), but whose
- * pre-layout offscreen position can be determined given the extra
- * information about the pre-layout removed views.
- *
- *
The second layout pass is the real layout in which only non-removed views
- * will be used. The only additional requirement during this pass is, if
- * {@link #supportsPredictiveItemAnimations()} returns true, to note which
- * views exist in the child list prior to layout and which are not there after
- * layout (referred to as DISAPPEARING views), and to position/layout those views
- * appropriately, without regard to the actual bounds of the RecyclerView. This allows
- * the animation system to know the location to which to animate these disappearing
- * views.
- *
- *
The default LayoutManager implementations for RecyclerView handle all of these
- * requirements for animations already. Clients of RecyclerView can either use one
- * of these layout managers directly or look at their implementations of
- * onLayoutChildren() to see how they account for the APPEARING and
- * DISAPPEARING views.
- *
- * @param recycler Recycler to use for fetching potentially cached views for a
- * position
- * @param state Transient state of RecyclerView
- */
- public void onLayoutChildren(Recycler recycler, State state) {
- Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
- }
-
- /**
- * Create a default LayoutParams object for a child of the RecyclerView.
- *
- *
LayoutManagers will often want to use a custom LayoutParams type
- * to store extra information specific to the layout. Client code should subclass
- * {@link LayoutParams} for this purpose.
- *
- *
Important: if you use your own custom LayoutParams type
- * you must also override
- * {@link #checkLayoutParams(LayoutParams)},
- * {@link #generateLayoutParams(ViewGroup.LayoutParams)} and
- * {@link #generateLayoutParams(Context, AttributeSet)}.
- *
- * @return A new LayoutParams for a child view
- */
- public abstract LayoutParams generateDefaultLayoutParams();
-
- /**
- * Determines the validity of the supplied LayoutParams object.
- *
- *
This should check to make sure that the object is of the correct type
- * and all values are within acceptable ranges. The default implementation
- * returns true for non-null params.
- *
- * @param lp LayoutParams object to check
- * @return true if this LayoutParams object is valid, false otherwise
- */
- public boolean checkLayoutParams(LayoutParams lp) {
- return lp != null;
- }
-
- /**
- * Create a LayoutParams object suitable for this LayoutManager, copying relevant
- * values from the supplied LayoutParams object if possible.
- *
- *
Important: if you use your own custom LayoutParams type
- * you must also override
- * {@link #checkLayoutParams(LayoutParams)},
- * {@link #generateLayoutParams(ViewGroup.LayoutParams)} and
- * {@link #generateLayoutParams(Context, AttributeSet)}.
- *
- * @param lp Source LayoutParams object to copy values from
- * @return a new LayoutParams object
- */
- public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
- if (lp instanceof LayoutParams) {
- return new LayoutParams((LayoutParams) lp);
- } else if (lp instanceof MarginLayoutParams) {
- return new LayoutParams((MarginLayoutParams) lp);
- } else {
- return new LayoutParams(lp);
- }
- }
-
- /**
- * Create a LayoutParams object suitable for this LayoutManager from
- * an inflated layout resource.
- *
- *
Important: if you use your own custom LayoutParams type
- * you must also override
- * {@link #checkLayoutParams(LayoutParams)},
- * {@link #generateLayoutParams(ViewGroup.LayoutParams)} and
- * {@link #generateLayoutParams(Context, AttributeSet)}.
- *
- * @param c Context for obtaining styled attributes
- * @param attrs AttributeSet describing the supplied arguments
- * @return a new LayoutParams object
- */
- public LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
- return new LayoutParams(c, attrs);
- }
-
- /**
- * Scroll horizontally by dx pixels in screen coordinates and return the distance traveled.
- * The default implementation does nothing and returns 0.
- *
- * @param dx distance to scroll by in pixels. X increases as scroll position
- * approaches the right.
- * @param recycler Recycler to use for fetching potentially cached views for a
- * position
- * @param state Transient state of RecyclerView
- * @return The actual distance scrolled. The return value will be negative if dx was
- * negative and scrolling proceeeded in that direction.
- * Math.abs(result) may be less than dx if a boundary was reached.
- */
- public int scrollHorizontallyBy(int dx, Recycler recycler, State state) {
- return 0;
- }
-
- /**
- * Scroll vertically by dy pixels in screen coordinates and return the distance traveled.
- * The default implementation does nothing and returns 0.
- *
- * @param dy distance to scroll in pixels. Y increases as scroll position
- * approaches the bottom.
- * @param recycler Recycler to use for fetching potentially cached views for a
- * position
- * @param state Transient state of RecyclerView
- * @return The actual distance scrolled. The return value will be negative if dy was
- * negative and scrolling proceeeded in that direction.
- * Math.abs(result) may be less than dy if a boundary was reached.
- */
- public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
- return 0;
- }
-
- /**
- * Query if horizontal scrolling is currently supported. The default implementation
- * returns false.
- *
- * @return True if this LayoutManager can scroll the current contents horizontally
- */
- public boolean canScrollHorizontally() {
- return false;
- }
-
- /**
- * Query if vertical scrolling is currently supported. The default implementation
- * returns false.
- *
- * @return True if this LayoutManager can scroll the current contents vertically
- */
- public boolean canScrollVertically() {
- return false;
- }
-
- /**
- * Scroll to the specified adapter position.
- *
- * Actual position of the item on the screen depends on the LayoutManager implementation.
- * @param position Scroll to this adapter position.
- */
- public void scrollToPosition(int position) {
- if (DEBUG) {
- Log.e(TAG, "You MUST implement scrollToPosition. It will soon become abstract");
- }
- }
-
- /**
- *
Smooth scroll to the specified adapter position.
- *
To support smooth scrolling, override this method, create your {@link SmoothScroller}
- * instance and call {@link #startSmoothScroll(SmoothScroller)}.
- *
- * @param recyclerView The RecyclerView to which this layout manager is attached
- * @param state Current State of RecyclerView
- * @param position Scroll to this adapter position.
- */
- public void smoothScrollToPosition(RecyclerView recyclerView, State state,
- int position) {
- Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
- }
-
- /**
- *
Starts a smooth scroll using the provided SmoothScroller.
- *
Calling this method will cancel any previous smooth scroll request.
- * @param smoothScroller Unstance which defines how smooth scroll should be animated
- */
- public void startSmoothScroll(SmoothScroller smoothScroller) {
- if (mSmoothScroller != null && smoothScroller != mSmoothScroller
- && mSmoothScroller.isRunning()) {
- mSmoothScroller.stop();
- }
- mSmoothScroller = smoothScroller;
- mSmoothScroller.start(mRecyclerView, this);
- }
-
- /**
- * @return true if RecycylerView is currently in the state of smooth scrolling.
- */
- public boolean isSmoothScrolling() {
- return mSmoothScroller != null && mSmoothScroller.isRunning();
- }
-
-
- /**
- * Returns the resolved layout direction for this RecyclerView.
- *
- * @return {@link ViewCompat#LAYOUT_DIRECTION_RTL} if the layout
- * direction is RTL or returns
- * {@link ViewCompat#LAYOUT_DIRECTION_LTR} if the layout direction
- * is not RTL.
- */
- public int getLayoutDirection() {
- return ViewCompat.getLayoutDirection(mRecyclerView);
- }
-
- /**
- * Ends all animations on the view created by the {@link ItemAnimator}.
- *
- * @param view The View for which the animations should be ended.
- * @see ItemAnimator#endAnimations()
- */
- public void endAnimation(View view) {
- if (mRecyclerView.mItemAnimator != null) {
- mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
- }
- }
-
- /**
- * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
- * to the layout that is known to be going away, either because it has been
- * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
- * visible portion of the container but is being laid out in order to inform RecyclerView
- * in how to animate the item out of view.
- *
- * Views added via this method are going to be invisible to LayoutManager after the
- * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
- * or won't be included in {@link #getChildCount()} method.
- *
- * @param child View to add and then remove with animation.
- */
- public void addDisappearingView(View child) {
- addDisappearingView(child, -1);
- }
-
- /**
- * To be called only during {@link #onLayoutChildren(Recycler, State)} to add a view
- * to the layout that is known to be going away, either because it has been
- * {@link Adapter#notifyItemRemoved(int) removed} or because it is actually not in the
- * visible portion of the container but is being laid out in order to inform RecyclerView
- * in how to animate the item out of view.
- *
- * Views added via this method are going to be invisible to LayoutManager after the
- * dispatchLayout pass is complete. They cannot be retrieved via {@link #getChildAt(int)}
- * or won't be included in {@link #getChildCount()} method.
- *
- * @param child View to add and then remove with animation.
- * @param index Index of the view.
- */
- public void addDisappearingView(View child, int index) {
- addViewInt(child, index, true);
- }
-
- /**
- * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
- * use this method to add views obtained from a {@link Recycler} using
- * {@link Recycler#getViewForPosition(int)}.
- *
- * @param child View to add
- */
- public void addView(View child) {
- addView(child, -1);
- }
-
- /**
- * Add a view to the currently attached RecyclerView if needed. LayoutManagers should
- * use this method to add views obtained from a {@link Recycler} using
- * {@link Recycler#getViewForPosition(int)}.
- *
- * @param child View to add
- * @param index Index to add child at
- */
- public void addView(View child, int index) {
- addViewInt(child, index, false);
- }
-
- private void addViewInt(View child, int index, boolean disappearing) {
- final ViewHolder holder = getChildViewHolderInt(child);
- if (disappearing || holder.isRemoved()) {
- // these views will be hidden at the end of the layout pass.
- mRecyclerView.addToDisappearingList(child);
- } else {
- // This may look like unnecessary but may happen if layout manager supports
- // predictive layouts and adapter removed then re-added the same item.
- // In this case, added version will be visible in the post layout (because add is
- // deferred) but RV will still bind it to the same View.
- // So if a View re-appears in post layout pass, remove it from disappearing list.
- mRecyclerView.removeFromDisappearingList(child);
- }
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- if (holder.wasReturnedFromScrap() || holder.isScrap()) {
- if (holder.isScrap()) {
- holder.unScrap();
- } else {
- holder.clearReturnedFromScrapFlag();
- }
- mChildHelper.attachViewToParent(child, index, child.getLayoutParams(), false);
- if (DISPATCH_TEMP_DETACH) {
- ViewCompat.dispatchFinishTemporaryDetach(child);
- }
- } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
- // ensure in correct position
- int currentIndex = mChildHelper.indexOfChild(child);
- if (index == -1) {
- index = mChildHelper.getChildCount();
- }
- if (currentIndex == -1) {
- throw new IllegalStateException("Added View has RecyclerView as parent but"
- + " view is not a real child. Unfiltered index:"
- + mRecyclerView.indexOfChild(child));
- }
- if (currentIndex != index) {
- mRecyclerView.mLayout.moveView(currentIndex, index);
- }
- } else {
- mChildHelper.addView(child, index, false);
- lp.mInsetsDirty = true;
- if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
- mSmoothScroller.onChildAttachedToWindow(child);
- }
- }
- if (lp.mPendingInvalidate) {
- if (DEBUG) {
- Log.d(TAG, "consuming pending invalidate on child " + lp.mViewHolder);
- }
- holder.itemView.invalidate();
- lp.mPendingInvalidate = false;
- }
- }
-
- /**
- * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
- * use this method to completely remove a child view that is no longer needed.
- * LayoutManagers should strongly consider recycling removed views using
- * {@link Recycler#recycleView(View)}.
- *
- * @param child View to remove
- */
- public void removeView(View child) {
- mChildHelper.removeView(child);
- }
-
- /**
- * Remove a view from the currently attached RecyclerView if needed. LayoutManagers should
- * use this method to completely remove a child view that is no longer needed.
- * LayoutManagers should strongly consider recycling removed views using
- * {@link Recycler#recycleView(View)}.
- *
- * @param index Index of the child view to remove
- */
- public void removeViewAt(int index) {
- final View child = getChildAt(index);
- if (child != null) {
- mChildHelper.removeViewAt(index);
- }
- }
-
- /**
- * Remove all views from the currently attached RecyclerView. This will not recycle
- * any of the affected views; the LayoutManager is responsible for doing so if desired.
- */
- public void removeAllViews() {
- // Only remove non-animating views
- final int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- final View child = getChildAt(i);
- mChildHelper.removeViewAt(i);
- }
- }
-
- /**
- * Returns the adapter position of the item represented by the given View.
- *
- * @param view The view to query
- * @return The adapter position of the item which is rendered by this View.
- */
- public int getPosition(View view) {
- return ((LayoutParams) view.getLayoutParams()).getViewPosition();
- }
-
- /**
- * Returns the View type defined by the adapter.
- *
- * @param view The view to query
- * @return The type of the view assigned by the adapter.
- */
- public int getItemViewType(View view) {
- return getChildViewHolderInt(view).getItemViewType();
- }
-
- /**
- *
- * Finds the view which represents the given adapter position.
- *
- * This method traverses each child since it has no information about child order.
- * Override this method to improve performance if your LayoutManager keeps data about
- * child views.
- *
- * If a view is ignored via {@link #ignoreView(View)}, it is also ignored by this method.
- *
- * @param position Position of the item in adapter
- * @return The child view that represents the given position or null if the position is not
- * visible
- */
- public View findViewByPosition(int position) {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- ViewHolder vh = getChildViewHolderInt(child);
- if (vh == null) {
- continue;
- }
- if (vh.getPosition() == position && !vh.shouldIgnore() &&
- (mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
- return child;
- }
- }
- return null;
- }
-
- /**
- * Temporarily detach a child view.
- *
- *
LayoutManagers may want to perform a lightweight detach operation to rearrange
- * views currently attached to the RecyclerView. Generally LayoutManager implementations
- * will want to use {@link #detachAndScrapView(View, Recycler)}
- * so that the detached view may be rebound and reused.
- *
- *
If a LayoutManager uses this method to detach a view, it must
- * {@link #attachView(View, int, LayoutParams) reattach}
- * or {@link #removeDetachedView(View) fully remove} the detached view
- * before the LayoutManager entry point method called by RecyclerView returns.
- *
- * @param child Child to detach
- */
- public void detachView(View child) {
- final int ind = mChildHelper.indexOfChild(child);
- if (ind >= 0) {
- detachViewInternal(ind, child);
- }
- }
-
- /**
- * Temporarily detach a child view.
- *
- *
LayoutManagers may want to perform a lightweight detach operation to rearrange
- * views currently attached to the RecyclerView. Generally LayoutManager implementations
- * will want to use {@link #detachAndScrapView(View, Recycler)}
- * so that the detached view may be rebound and reused.
- *
- *
If a LayoutManager uses this method to detach a view, it must
- * {@link #attachView(View, int, LayoutParams) reattach}
- * or {@link #removeDetachedView(View) fully remove} the detached view
- * before the LayoutManager entry point method called by RecyclerView returns.
- *
- * @param index Index of the child to detach
- */
- public void detachViewAt(int index) {
- detachViewInternal(index, getChildAt(index));
- }
-
- private void detachViewInternal(int index, View view) {
- if (DISPATCH_TEMP_DETACH) {
- ViewCompat.dispatchStartTemporaryDetach(view);
- }
- mChildHelper.detachViewFromParent(index);
- }
-
- /**
- * Reattach a previously {@link #detachView(View) detached} view.
- * This method should not be used to reattach views that were previously
- * {@link #detachAndScrapView(View, Recycler)} scrapped}.
- *
- * @param child Child to reattach
- * @param index Intended child index for child
- * @param lp LayoutParams for child
- */
- public void attachView(View child, int index, LayoutParams lp) {
- ViewHolder vh = getChildViewHolderInt(child);
- if (vh.isRemoved()) {
- mRecyclerView.addToDisappearingList(child);
- } else {
- mRecyclerView.removeFromDisappearingList(child);
- }
- mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
- if (DISPATCH_TEMP_DETACH) {
- ViewCompat.dispatchFinishTemporaryDetach(child);
- }
- }
-
- /**
- * Reattach a previously {@link #detachView(View) detached} view.
- * This method should not be used to reattach views that were previously
- * {@link #detachAndScrapView(View, Recycler)} scrapped}.
- *
- * @param child Child to reattach
- * @param index Intended child index for child
- */
- public void attachView(View child, int index) {
- attachView(child, index, (LayoutParams) child.getLayoutParams());
- }
-
- /**
- * Reattach a previously {@link #detachView(View) detached} view.
- * This method should not be used to reattach views that were previously
- * {@link #detachAndScrapView(View, Recycler)} scrapped}.
- *
- * @param child Child to reattach
- */
- public void attachView(View child) {
- attachView(child, -1);
- }
-
- /**
- * Finish removing a view that was previously temporarily
- * {@link #detachView(View) detached}.
- *
- * @param child Detached child to remove
- */
- public void removeDetachedView(View child) {
- mRecyclerView.removeDetachedView(child, false);
- }
-
- /**
- * Moves a View from one position to another.
- *
- * @param fromIndex The View's initial index
- * @param toIndex The View's target index
- */
- public void moveView(int fromIndex, int toIndex) {
- View view = getChildAt(fromIndex);
- if (view == null) {
- throw new IllegalArgumentException("Cannot move a child from non-existing index:"
- + fromIndex);
- }
- detachViewAt(fromIndex);
- attachView(view, toIndex);
- }
-
- /**
- * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
- *
- *
Scrapping a view allows it to be rebound and reused to show updated or
- * different data.
- *
- * @param child Child to detach and scrap
- * @param recycler Recycler to deposit the new scrap view into
- */
- public void detachAndScrapView(View child, Recycler recycler) {
- int index = mChildHelper.indexOfChild(child);
- scrapOrRecycleView(recycler, index, child);
- }
-
- /**
- * Detach a child view and add it to a {@link Recycler Recycler's} scrap heap.
- *
- *
Scrapping a view allows it to be rebound and reused to show updated or
- * different data.
- *
- * @param index Index of child to detach and scrap
- * @param recycler Recycler to deposit the new scrap view into
- */
- public void detachAndScrapViewAt(int index, Recycler recycler) {
- final View child = getChildAt(index);
- scrapOrRecycleView(recycler, index, child);
- }
-
- /**
- * Remove a child view and recycle it using the given Recycler.
- *
- * @param child Child to remove and recycle
- * @param recycler Recycler to use to recycle child
- */
- public void removeAndRecycleView(View child, Recycler recycler) {
- removeView(child);
- recycler.recycleView(child);
- }
-
- /**
- * Remove a child view and recycle it using the given Recycler.
- *
- * @param index Index of child to remove and recycle
- * @param recycler Recycler to use to recycle child
- */
- public void removeAndRecycleViewAt(int index, Recycler recycler) {
- final View view = getChildAt(index);
- removeViewAt(index);
- recycler.recycleView(view);
- }
-
- /**
- * Return the current number of child views attached to the parent RecyclerView.
- * This does not include child views that were temporarily detached and/or scrapped.
- *
- * @return Number of attached children
- */
- public int getChildCount() {
- return mChildHelper != null ? mChildHelper.getChildCount() : 0;
- }
-
- /**
- * Return the child view at the given index
- * @param index Index of child to return
- * @return Child view at index
- */
- public View getChildAt(int index) {
- return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
- }
-
- /**
- * Return the width of the parent RecyclerView
- *
- * @return Width in pixels
- */
- public int getWidth() {
- return mRecyclerView != null ? mRecyclerView.getWidth() : 0;
- }
-
- /**
- * Return the height of the parent RecyclerView
- *
- * @return Height in pixels
- */
- public int getHeight() {
- return mRecyclerView != null ? mRecyclerView.getHeight() : 0;
- }
-
- /**
- * Return the left padding of the parent RecyclerView
- *
- * @return Padding in pixels
- */
- public int getPaddingLeft() {
- return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
- }
-
- /**
- * Return the top padding of the parent RecyclerView
- *
- * @return Padding in pixels
- */
- public int getPaddingTop() {
- return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
- }
-
- /**
- * Return the right padding of the parent RecyclerView
- *
- * @return Padding in pixels
- */
- public int getPaddingRight() {
- return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
- }
-
- /**
- * Return the bottom padding of the parent RecyclerView
- *
- * @return Padding in pixels
- */
- public int getPaddingBottom() {
- return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
- }
-
- /**
- * Return the start padding of the parent RecyclerView
- *
- * @return Padding in pixels
- */
- public int getPaddingStart() {
- return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView) : 0;
- }
-
- /**
- * Return the end padding of the parent RecyclerView
- *
- * @return Padding in pixels
- */
- public int getPaddingEnd() {
- return mRecyclerView != null ? ViewCompat.getPaddingEnd(mRecyclerView) : 0;
- }
-
- /**
- * Returns true if the RecyclerView this LayoutManager is bound to has focus.
- *
- * @return True if the RecyclerView has focus, false otherwise.
- * @see View#isFocused()
- */
- public boolean isFocused() {
- return mRecyclerView != null && mRecyclerView.isFocused();
- }
-
- /**
- * Returns true if the RecyclerView this LayoutManager is bound to has or contains focus.
- *
- * @return true if the RecyclerView has or contains focus
- * @see View#hasFocus()
- */
- public boolean hasFocus() {
- return mRecyclerView != null && mRecyclerView.hasFocus();
- }
-
- /**
- * Returns the item View which has or contains focus.
- *
- * @return A direct child of RecyclerView which has focus or contains the focused child.
- */
- public View getFocusedChild() {
- if (mRecyclerView == null) {
- return null;
- }
- final View focused = mRecyclerView.getFocusedChild();
- if (focused == null || mChildHelper.isHidden(focused)) {
- return null;
- }
- return focused;
- }
-
- /**
- * Returns the number of items in the adapter bound to the parent RecyclerView.
- *
- * Note that this number is not necessarily equal to {@link State#getItemCount()}. In
- * methods where State is available, you should use {@link State#getItemCount()} instead.
- * For more details, check the documentation for {@link State#getItemCount()}.
- *
- * @return The number of items in the bound adapter
- * @see State#getItemCount()
- */
- public int getItemCount() {
- final Adapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
- return a != null ? a.getItemCount() : 0;
- }
-
- /**
- * Offset all child views attached to the parent RecyclerView by dx pixels along
- * the horizontal axis.
- *
- * @param dx Pixels to offset by
- */
- public void offsetChildrenHorizontal(int dx) {
- if (mRecyclerView != null) {
- mRecyclerView.offsetChildrenHorizontal(dx);
- }
- }
-
- /**
- * Offset all child views attached to the parent RecyclerView by dy pixels along
- * the vertical axis.
- *
- * @param dy Pixels to offset by
- */
- public void offsetChildrenVertical(int dy) {
- if (mRecyclerView != null) {
- mRecyclerView.offsetChildrenVertical(dy);
- }
- }
-
- /**
- * Flags a view so that it will not be scrapped or recycled.
- *
- * Scope of ignoring a child is strictly restricted to position tracking, scrapping and
- * recyling. Methods like {@link #removeAndRecycleAllViews(Recycler)} will ignore the child
- * whereas {@link #removeAllViews()} or {@link #offsetChildrenHorizontal(int)} will not
- * ignore the child.
- *
- * Before this child can be recycled again, you have to call
- * {@link #stopIgnoringView(View)}.
- *
- * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
- *
- * @param view View to ignore.
- * @see #stopIgnoringView(View)
- */
- public void ignoreView(View view) {
- if (view.getParent() != mRecyclerView || mRecyclerView.indexOfChild(view) == -1) {
- // checking this because calling this method on a recycled or detached view may
- // cause loss of state.
- throw new IllegalArgumentException("View should be fully attached to be ignored");
- }
- final ViewHolder vh = getChildViewHolderInt(view);
- vh.addFlags(ViewHolder.FLAG_IGNORE);
- mRecyclerView.mState.onViewIgnored(vh);
- }
-
- /**
- * View can be scrapped and recycled again.
- *
- * Note that calling this method removes all information in the view holder.
- *
- * You can call this method only if your LayoutManger is in onLayout or onScroll callback.
- *
- * @param view View to ignore.
- */
- public void stopIgnoringView(View view) {
- final ViewHolder vh = getChildViewHolderInt(view);
- vh.stopIgnoring();
- vh.resetInternal();
- vh.addFlags(ViewHolder.FLAG_INVALID);
- }
-
- /**
- * Temporarily detach and scrap all currently attached child views. Views will be scrapped
- * into the given Recycler. The Recycler may prefer to reuse scrap views before
- * other views that were previously recycled.
- *
- * @param recycler Recycler to scrap views into
- */
- public void detachAndScrapAttachedViews(Recycler recycler) {
- final int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- final View v = getChildAt(i);
- scrapOrRecycleView(recycler, i, v);
- }
- }
-
- private void scrapOrRecycleView(Recycler recycler, int index, View view) {
- final ViewHolder viewHolder = getChildViewHolderInt(view);
- if (viewHolder.shouldIgnore()) {
- if (DEBUG) {
- Log.d(TAG, "ignoring view " + viewHolder);
- }
- return;
- }
- if (viewHolder.isInvalid() && !viewHolder.isRemoved() && !viewHolder.isChanged() &&
- !mRecyclerView.mAdapter.hasStableIds()) {
- removeViewAt(index);
- recycler.recycleViewHolderInternal(viewHolder);
- } else {
- detachViewAt(index);
- recycler.scrapView(view);
- }
- }
-
- /**
- * Recycles the scrapped views.
- *
- * When a view is detached and removed, it does not trigger a ViewGroup invalidate. This is
- * the expected behavior if scrapped views are used for animations. Otherwise, we need to
- * call remove and invalidate RecyclerView to ensure UI update.
- *
- * @param recycler Recycler
- * @param remove Whether scrapped views should be removed from ViewGroup or not. This
- * method will invalidate RecyclerView if it removes any scrapped child.
- */
- void removeAndRecycleScrapInt(Recycler recycler, boolean remove) {
- final int scrapCount = recycler.getScrapCount();
- for (int i = 0; i < scrapCount; i++) {
- final View scrap = recycler.getScrapViewAt(i);
- if (getChildViewHolderInt(scrap).shouldIgnore()) {
- continue;
- }
- if (remove) {
- mRecyclerView.removeDetachedView(scrap, false);
- }
- recycler.quickRecycleScrapView(scrap);
- }
- recycler.clearScrap();
- if (remove && scrapCount > 0) {
- mRecyclerView.invalidate();
- }
- }
-
-
- /**
- * Measure a child view using standard measurement policy, taking the padding
- * of the parent RecyclerView and any added item decorations into account.
- *
- *
If the RecyclerView can be scrolled in either dimension the caller may
- * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.
- *
- * @param child Child view to measure
- * @param widthUsed Width in pixels currently consumed by other views, if relevant
- * @param heightUsed Height in pixels currently consumed by other views, if relevant
- */
- public void measureChild(View child, int widthUsed, int heightUsed) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
- widthUsed += insets.left + insets.right;
- heightUsed += insets.top + insets.bottom;
-
- final int widthSpec = getChildMeasureSpec(getWidth(),
- getPaddingLeft() + getPaddingRight() + widthUsed, lp.width,
- canScrollHorizontally());
- final int heightSpec = getChildMeasureSpec(getHeight(),
- getPaddingTop() + getPaddingBottom() + heightUsed, lp.height,
- canScrollVertically());
- child.measure(widthSpec, heightSpec);
- }
-
- /**
- * Measure a child view using standard measurement policy, taking the padding
- * of the parent RecyclerView, any added item decorations and the child margins
- * into account.
- *
- *
If the RecyclerView can be scrolled in either dimension the caller may
- * pass 0 as the widthUsed or heightUsed parameters as they will be irrelevant.
- *
- * @param child Child view to measure
- * @param widthUsed Width in pixels currently consumed by other views, if relevant
- * @param heightUsed Height in pixels currently consumed by other views, if relevant
- */
- public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
- widthUsed += insets.left + insets.right;
- heightUsed += insets.top + insets.bottom;
-
- final int widthSpec = getChildMeasureSpec(getWidth(),
- getPaddingLeft() + getPaddingRight() +
- lp.leftMargin + lp.rightMargin + widthUsed, lp.width,
- canScrollHorizontally());
- final int heightSpec = getChildMeasureSpec(getHeight(),
- getPaddingTop() + getPaddingBottom() +
- lp.topMargin + lp.bottomMargin + heightUsed, lp.height,
- canScrollVertically());
- child.measure(widthSpec, heightSpec);
- }
-
- /**
- * Calculate a MeasureSpec value for measuring a child view in one dimension.
- *
- * @param parentSize Size of the parent view where the child will be placed
- * @param padding Total space currently consumed by other elements of parent
- * @param childDimension Desired size of the child view, or MATCH_PARENT/WRAP_CONTENT.
- * Generally obtained from the child view's LayoutParams
- * @param canScroll true if the parent RecyclerView can scroll in this dimension
- *
- * @return a MeasureSpec value for the child view
- */
- public static int getChildMeasureSpec(int parentSize, int padding, int childDimension,
- boolean canScroll) {
- int size = Math.max(0, parentSize - padding);
- int resultSize = 0;
- int resultMode = 0;
-
- if (canScroll) {
- if (childDimension >= 0) {
- resultSize = childDimension;
- resultMode = MeasureSpec.EXACTLY;
- } else {
- // MATCH_PARENT can't be applied since we can scroll in this dimension, wrap
- // instead using UNSPECIFIED.
- resultSize = 0;
- resultMode = MeasureSpec.UNSPECIFIED;
- }
- } else {
- if (childDimension >= 0) {
- resultSize = childDimension;
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.FILL_PARENT) {
- resultSize = size;
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.WRAP_CONTENT) {
- resultSize = size;
- resultMode = MeasureSpec.AT_MOST;
- }
- }
- return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
- }
-
- /**
- * Returns the measured width of the given child, plus the additional size of
- * any insets applied by {@link ItemDecoration ItemDecorations}.
- *
- * @param child Child view to query
- * @return child's measured width plus ItemDecoration insets
- *
- * @see View#getMeasuredWidth()
- */
- public int getDecoratedMeasuredWidth(View child) {
- final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
- return child.getMeasuredWidth() + insets.left + insets.right;
- }
-
- /**
- * Returns the measured height of the given child, plus the additional size of
- * any insets applied by {@link ItemDecoration ItemDecorations}.
- *
- * @param child Child view to query
- * @return child's measured height plus ItemDecoration insets
- *
- * @see View#getMeasuredHeight()
- */
- public int getDecoratedMeasuredHeight(View child) {
- final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
- return child.getMeasuredHeight() + insets.top + insets.bottom;
- }
-
- /**
- * Lay out the given child view within the RecyclerView using coordinates that
- * include any current {@link ItemDecoration ItemDecorations}.
- *
- *
LayoutManagers should prefer working in sizes and coordinates that include
- * item decoration insets whenever possible. This allows the LayoutManager to effectively
- * ignore decoration insets within measurement and layout code. See the following
- * methods:
- *
- *
{@link #measureChild(View, int, int)}
- *
{@link #measureChildWithMargins(View, int, int)}
- *
{@link #getDecoratedLeft(View)}
- *
{@link #getDecoratedTop(View)}
- *
{@link #getDecoratedRight(View)}
- *
{@link #getDecoratedBottom(View)}
- *
{@link #getDecoratedMeasuredWidth(View)}
- *
{@link #getDecoratedMeasuredHeight(View)}
- *
- *
- * @param child Child to lay out
- * @param left Left edge, with item decoration insets included
- * @param top Top edge, with item decoration insets included
- * @param right Right edge, with item decoration insets included
- * @param bottom Bottom edge, with item decoration insets included
- *
- * @see View#layout(int, int, int, int)
- */
- public void layoutDecorated(View child, int left, int top, int right, int bottom) {
- final Rect insets = ((LayoutParams) child.getLayoutParams()).mDecorInsets;
- child.layout(left + insets.left, top + insets.top, right - insets.right,
- bottom - insets.bottom);
- }
-
- /**
- * Returns the left edge of the given child view within its parent, offset by any applied
- * {@link ItemDecoration ItemDecorations}.
- *
- * @param child Child to query
- * @return Child left edge with offsets applied
- * @see #getLeftDecorationWidth(View)
- */
- public int getDecoratedLeft(View child) {
- return child.getLeft() - getLeftDecorationWidth(child);
- }
-
- /**
- * Returns the top edge of the given child view within its parent, offset by any applied
- * {@link ItemDecoration ItemDecorations}.
- *
- * @param child Child to query
- * @return Child top edge with offsets applied
- * @see #getTopDecorationHeight(View)
- */
- public int getDecoratedTop(View child) {
- return child.getTop() - getTopDecorationHeight(child);
- }
-
- /**
- * Returns the right edge of the given child view within its parent, offset by any applied
- * {@link ItemDecoration ItemDecorations}.
- *
- * @param child Child to query
- * @return Child right edge with offsets applied
- * @see #getRightDecorationWidth(View)
- */
- public int getDecoratedRight(View child) {
- return child.getRight() + getRightDecorationWidth(child);
- }
-
- /**
- * Returns the bottom edge of the given child view within its parent, offset by any applied
- * {@link ItemDecoration ItemDecorations}.
- *
- * @param child Child to query
- * @return Child bottom edge with offsets applied
- * @see #getBottomDecorationHeight(View)
- */
- public int getDecoratedBottom(View child) {
- return child.getBottom() + getBottomDecorationHeight(child);
- }
-
- /**
- * Calculates the item decor insets applied to the given child and updates the provided
- * Rect instance with the inset values.
- *
- *
The Rect's left is set to the total width of left decorations.
- *
The Rect's top is set to the total height of top decorations.
- *
The Rect's right is set to the total width of right decorations.
- *
The Rect's bottom is set to total height of bottom decorations.
- *
- *
- * Note that item decorations are automatically calculated when one of the LayoutManager's
- * measure child methods is called. If you need to measure the child with custom specs via
- * {@link View#measure(int, int)}, you can use this method to get decorations.
- *
- * @param child The child view whose decorations should be calculated
- * @param outRect The Rect to hold result values
- */
- public void calculateItemDecorationsForChild(View child, Rect outRect) {
- if (mRecyclerView == null) {
- outRect.set(0, 0, 0, 0);
- return;
- }
- Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
- outRect.set(insets);
- }
-
- /**
- * Returns the total height of item decorations applied to child's top.
- *
- * Note that this value is not updated until the View is measured or
- * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
- *
- * @param child Child to query
- * @return The total height of item decorations applied to the child's top.
- * @see #getDecoratedTop(View)
- * @see #calculateItemDecorationsForChild(View, Rect)
- */
- public int getTopDecorationHeight(View child) {
- return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
- }
-
- /**
- * Returns the total height of item decorations applied to child's bottom.
- *
- * Note that this value is not updated until the View is measured or
- * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
- *
- * @param child Child to query
- * @return The total height of item decorations applied to the child's bottom.
- * @see #getDecoratedBottom(View)
- * @see #calculateItemDecorationsForChild(View, Rect)
- */
- public int getBottomDecorationHeight(View child) {
- return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
- }
-
- /**
- * Returns the total width of item decorations applied to child's left.
- *
- * Note that this value is not updated until the View is measured or
- * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
- *
- * @param child Child to query
- * @return The total width of item decorations applied to the child's left.
- * @see #getDecoratedLeft(View)
- * @see #calculateItemDecorationsForChild(View, Rect)
- */
- public int getLeftDecorationWidth(View child) {
- return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
- }
-
- /**
- * Returns the total width of item decorations applied to child's right.
- *
- * Note that this value is not updated until the View is measured or
- * {@link #calculateItemDecorationsForChild(View, Rect)} is called.
- *
- * @param child Child to query
- * @return The total width of item decorations applied to the child's right.
- * @see #getDecoratedRight(View)
- * @see #calculateItemDecorationsForChild(View, Rect)
- */
- public int getRightDecorationWidth(View child) {
- return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
- }
-
- /**
- * Called when searching for a focusable view in the given direction has failed
- * for the current content of the RecyclerView.
- *
- *
This is the LayoutManager's opportunity to populate views in the given direction
- * to fulfill the request if it can. The LayoutManager should attach and return
- * the view to be focused. The default implementation returns null.
- *
- * @param focused The currently focused view
- * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
- * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
- * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
- * or 0 for not applicable
- * @param recycler The recycler to use for obtaining views for currently offscreen items
- * @param state Transient state of RecyclerView
- * @return The chosen view to be focused
- */
- public View onFocusSearchFailed(View focused, int direction, Recycler recycler,
- State state) {
- return null;
- }
-
- /**
- * This method gives a LayoutManager an opportunity to intercept the initial focus search
- * before the default behavior of {@link FocusFinder} is used. If this method returns
- * null FocusFinder will attempt to find a focusable child view. If it fails
- * then {@link #onFocusSearchFailed(View, int, Recycler, State)}
- * will be called to give the LayoutManager an opportunity to add new views for items
- * that did not have attached views representing them. The LayoutManager should not add
- * or remove views from this method.
- *
- * @param focused The currently focused view
- * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
- * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
- * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
- * @return A descendant view to focus or null to fall back to default behavior.
- * The default implementation returns null.
- */
- public View onInterceptFocusSearch(View focused, int direction) {
- return null;
- }
-
- /**
- * Called when a child of the RecyclerView wants a particular rectangle to be positioned
- * onto the screen. See {@link ViewParent#requestChildRectangleOnScreen(View,
- * Rect, boolean)} for more details.
- *
- *
The base implementation will attempt to perform a standard programmatic scroll
- * to bring the given rect into view, within the padded area of the RecyclerView.
- *
- * @param child The direct child making the request.
- * @param rect The rectangle in the child's coordinates the child
- * wishes to be on the screen.
- * @param immediate True to forbid animated or delayed scrolling,
- * false otherwise
- * @return Whether the group scrolled to handle the operation
- */
- public boolean requestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
- boolean immediate) {
- final int parentLeft = getPaddingLeft();
- final int parentTop = getPaddingTop();
- final int parentRight = getWidth() - getPaddingRight();
- final int parentBottom = getHeight() - getPaddingBottom();
- final int childLeft = child.getLeft() + rect.left;
- final int childTop = child.getTop() + rect.top;
- final int childRight = childLeft + rect.right;
- final int childBottom = childTop + rect.bottom;
-
- final int offScreenLeft = Math.min(0, childLeft - parentLeft);
- final int offScreenTop = Math.min(0, childTop - parentTop);
- final int offScreenRight = Math.max(0, childRight - parentRight);
- final int offScreenBottom = Math.max(0, childBottom - parentBottom);
-
- // Favor the "start" layout direction over the end when bringing one side or the other
- // of a large rect into view.
- final int dx;
- if (ViewCompat.getLayoutDirection(parent) == ViewCompat.LAYOUT_DIRECTION_RTL) {
- dx = offScreenRight != 0 ? offScreenRight : offScreenLeft;
- } else {
- dx = offScreenLeft != 0 ? offScreenLeft : offScreenRight;
- }
-
- // Favor bringing the top into view over the bottom
- final int dy = offScreenTop;
-// final int dy = offScreenTop != 0 ? offScreenTop : offScreenBottom;
- if (dx != 0 || dy != 0) {
- if (immediate) {
- parent.scrollBy(dx, dy);
- } else {
-// parent.smoothScrollBy(dx, dy);
- }
- return true;
- }
- return false;
- }
-
- /**
- * @deprecated Use {@link #onRequestChildFocus(RecyclerView, State, View, View)}
- */
- @Deprecated
- public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
- return false;
- }
-
- /**
- * Called when a descendant view of the RecyclerView requests focus.
- *
- *
A LayoutManager wishing to keep focused views aligned in a specific
- * portion of the view may implement that behavior in an override of this method.
- *
- *
If the LayoutManager executes different behavior that should override the default
- * behavior of scrolling the focused child on screen instead of running alongside it,
- * this method should return true.
- *
- * @param parent The RecyclerView hosting this LayoutManager
- * @param state Current state of RecyclerView
- * @param child Direct child of the RecyclerView containing the newly focused view
- * @param focused The newly focused view. This may be the same view as child or it may be
- * null
- * @return true if the default scroll behavior should be suppressed
- */
- public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
- View focused) {
- return onRequestChildFocus(parent, child, focused);
- }
-
- /**
- * Called if the RecyclerView this LayoutManager is bound to has a different adapter set.
- * The LayoutManager may use this opportunity to clear caches and configure state such
- * that it can relayout appropriately with the new data and potentially new view types.
- *
- *
The default implementation removes all currently attached views.
- *
- * @param oldAdapter The previous adapter instance. Will be null if there was previously no
- * adapter.
- * @param newAdapter The new adapter instance. Might be null if
- * {@link #setAdapter(Adapter)} is called with {@code null}.
- */
- public void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter) {
- }
-
- /**
- * Called to populate focusable views within the RecyclerView.
- *
- *
The LayoutManager implementation should return true if the default
- * behavior of {@link ViewGroup#addFocusables(ArrayList, int)} should be
- * suppressed.
- *
- *
The default implementation returns false to trigger RecyclerView
- * to fall back to the default ViewGroup behavior.
- *
- * @param recyclerView The RecyclerView hosting this LayoutManager
- * @param views List of output views. This method should add valid focusable views
- * to this list.
- * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
- * {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
- * {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
- * @param focusableMode The type of focusables to be added.
- *
- * @return true to suppress the default behavior, false to add default focusables after
- * this method returns.
- *
- * @see #FOCUSABLES_ALL
- * @see #FOCUSABLES_TOUCH_MODE
- */
- public boolean onAddFocusables(RecyclerView recyclerView, ArrayList views,
- int direction, int focusableMode) {
- return false;
- }
-
- /**
- * Called when {@link Adapter#notifyDataSetChanged()} is triggered instead of giving
- * detailed information on what has actually changed.
- *
- * @param recyclerView
- */
- public void onItemsChanged(RecyclerView recyclerView) {
- }
-
- /**
- * Called when items have been added to the adapter. The LayoutManager may choose to
- * requestLayout if the inserted items would require refreshing the currently visible set
- * of child views. (e.g. currently empty space would be filled by appended items, etc.)
- *
- * @param recyclerView
- * @param positionStart
- * @param itemCount
- */
- public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
- }
-
- /**
- * Called when items have been removed from the adapter.
- *
- * @param recyclerView
- * @param positionStart
- * @param itemCount
- */
- public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
- }
-
- /**
- * Called when items have been changed in the adapter.
- *
- * @param recyclerView
- * @param positionStart
- * @param itemCount
- */
- public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
- }
-
- /**
- * Called when an item is moved withing the adapter.
- *
- * Note that, an item may also change position in response to another ADD/REMOVE/MOVE
- * operation. This callback is only called if and only if {@link Adapter#notifyItemMoved}
- * is called.
- *
- * @param recyclerView
- * @param from
- * @param to
- * @param itemCount
- */
- public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
-
- }
-
-
- /**
- *
Override this method if you want to support scroll bars.
- *
- *
Read {@link RecyclerView#computeHorizontalScrollExtent()} for details.
- *
- *
Default implementation returns 0.
- *
- * @param state Current state of RecyclerView
- * @return The horizontal extent of the scrollbar's thumb
- * @see RecyclerView#computeHorizontalScrollExtent()
- */
- public int computeHorizontalScrollExtent(State state) {
- return 0;
- }
-
- /**
- *
Override this method if you want to support scroll bars.
- *
- *
Read {@link RecyclerView#computeHorizontalScrollOffset()} for details.
- *
- *
Default implementation returns 0.
- *
- * @param state Current State of RecyclerView where you can find total item count
- * @return The horizontal offset of the scrollbar's thumb
- * @see RecyclerView#computeHorizontalScrollOffset()
- */
- public int computeHorizontalScrollOffset(State state) {
- return 0;
- }
-
- /**
- *
Override this method if you want to support scroll bars.
- *
- *
Read {@link RecyclerView#computeHorizontalScrollRange()} for details.
- *
- *
Default implementation returns 0.
- *
- * @param state Current State of RecyclerView where you can find total item count
- * @return The total horizontal range represented by the vertical scrollbar
- * @see RecyclerView#computeHorizontalScrollRange()
- */
- public int computeHorizontalScrollRange(State state) {
- return 0;
- }
-
- /**
- *
Override this method if you want to support scroll bars.
- *
- *
Read {@link RecyclerView#computeVerticalScrollExtent()} for details.
- *
- *
Default implementation returns 0.
- *
- * @param state Current state of RecyclerView
- * @return The vertical extent of the scrollbar's thumb
- * @see RecyclerView#computeVerticalScrollExtent()
- */
- public int computeVerticalScrollExtent(State state) {
- return 0;
- }
-
- /**
- *
Override this method if you want to support scroll bars.
- *
- *
Read {@link RecyclerView#computeVerticalScrollOffset()} for details.
- *
- *
Default implementation returns 0.
- *
- * @param state Current State of RecyclerView where you can find total item count
- * @return The vertical offset of the scrollbar's thumb
- * @see RecyclerView#computeVerticalScrollOffset()
- */
- public int computeVerticalScrollOffset(State state) {
- return 0;
- }
-
- /**
- *
Override this method if you want to support scroll bars.
- *
- *
Read {@link RecyclerView#computeVerticalScrollRange()} for details.
- *
- *
Default implementation returns 0.
- *
- * @param state Current State of RecyclerView where you can find total item count
- * @return The total vertical range represented by the vertical scrollbar
- * @see RecyclerView#computeVerticalScrollRange()
- */
- public int computeVerticalScrollRange(State state) {
- return 0;
- }
-
- /**
- * Measure the attached RecyclerView. Implementations must call
- * {@link #setMeasuredDimension(int, int)} before returning.
- *
- *
The default implementation will handle EXACTLY measurements and respect
- * the minimum width and height properties of the host RecyclerView if measured
- * as UNSPECIFIED. AT_MOST measurements will be treated as EXACTLY and the RecyclerView
- * will consume all available space.
- *
- * @param recycler Recycler
- * @param state Transient state of RecyclerView
- * @param widthSpec Width {@link MeasureSpec}
- * @param heightSpec Height {@link MeasureSpec}
- */
- public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
- final int widthMode = MeasureSpec.getMode(widthSpec);
- final int heightMode = MeasureSpec.getMode(heightSpec);
- final int widthSize = MeasureSpec.getSize(widthSpec);
- final int heightSize = MeasureSpec.getSize(heightSpec);
-
- int width = 0;
- int height = 0;
-
- switch (widthMode) {
- case MeasureSpec.EXACTLY:
- case MeasureSpec.AT_MOST:
- width = widthSize;
- break;
- case MeasureSpec.UNSPECIFIED:
- default:
- width = getMinimumWidth();
- break;
- }
-
- switch (heightMode) {
- case MeasureSpec.EXACTLY:
- case MeasureSpec.AT_MOST:
- height = heightSize;
- break;
- case MeasureSpec.UNSPECIFIED:
- default:
- height = getMinimumHeight();
- break;
- }
-
- setMeasuredDimension(width, height);
- }
-
- /**
- * {@link View#setMeasuredDimension(int, int) Set the measured dimensions} of the
- * host RecyclerView.
- *
- * @param widthSize Measured width
- * @param heightSize Measured height
- */
- public void setMeasuredDimension(int widthSize, int heightSize) {
- mRecyclerView.setMeasuredDimension(widthSize, heightSize);
- }
-
- /**
- * @return The host RecyclerView's {@link View#getMinimumWidth()}
- */
- public int getMinimumWidth() {
- return ViewCompat.getMinimumWidth(mRecyclerView);
- }
-
- /**
- * @return The host RecyclerView's {@link View#getMinimumHeight()}
- */
- public int getMinimumHeight() {
- return ViewCompat.getMinimumHeight(mRecyclerView);
- }
- /**
- *
Called when the LayoutManager should save its state. This is a good time to save your
- * scroll position, configuration and anything else that may be required to restore the same
- * layout state if the LayoutManager is recreated.
- *
RecyclerView does NOT verify if the LayoutManager has changed between state save and
- * restore. This will let you share information between your LayoutManagers but it is also
- * your responsibility to make sure they use the same parcelable class.
- *
- * @return Necessary information for LayoutManager to be able to restore its state
- */
- public Parcelable onSaveInstanceState() {
- return null;
- }
-
-
- public void onRestoreInstanceState(Parcelable state) {
-
- }
-
- void stopSmoothScroller() {
- if (mSmoothScroller != null) {
- mSmoothScroller.stop();
- }
- }
-
- private void onSmoothScrollerStopped(SmoothScroller smoothScroller) {
- if (mSmoothScroller == smoothScroller) {
- mSmoothScroller = null;
- }
- }
-
- /**
- * RecyclerView calls this method to notify LayoutManager that scroll state has changed.
- *
- * @param state The new scroll state for RecyclerView
- */
- public void onScrollStateChanged(int state) {
- }
-
- /**
- * Removes all views and recycles them using the given recycler.
- *
- * If you want to clean cached views as well, you should call {@link Recycler#clear()} too.
- *
- * If a View is marked as "ignored", it is not removed nor recycled.
- *
- * @param recycler Recycler to use to recycle children
- * @see #removeAndRecycleView(View, Recycler)
- * @see #removeAndRecycleViewAt(int, Recycler)
- * @see #ignoreView(View)
- */
- public void removeAndRecycleAllViews(Recycler recycler) {
- for (int i = getChildCount() - 1; i >= 0; i--) {
- final View view = getChildAt(i);
- if (!getChildViewHolderInt(view).shouldIgnore()) {
- removeAndRecycleViewAt(i, recycler);
- }
- }
- }
-
- // called by accessibility delegate
- void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
- onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler, mRecyclerView.mState,
- info);
- }
-
- /**
- * Called by the AccessibilityDelegate when the information about the current layout should
- * be populated.
- *
- * You should override
- * {@link #getRowCountForAccessibility(Recycler, State)},
- * {@link #getColumnCountForAccessibility(Recycler, State)},
- * {@link #isLayoutHierarchical(Recycler, State)} and
- * {@link #getSelectionModeForAccessibility(Recycler, State)} for
- * more accurate accessibility information.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @param info The info that should be filled by the LayoutManager
- * @see View#onInitializeAccessibilityNodeInfo(
- *android.view.accessibility.AccessibilityNodeInfo)
- * @see #getRowCountForAccessibility(Recycler, State)
- * @see #getColumnCountForAccessibility(Recycler, State)
- * @see #isLayoutHierarchical(Recycler, State)
- * @see #getSelectionModeForAccessibility(Recycler, State)
- */
- public void onInitializeAccessibilityNodeInfo(Recycler recycler, State state,
- AccessibilityNodeInfoCompat info) {
- info.setClassName(RecyclerView.class.getName());
- if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
- ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
- info.setScrollable(true);
- }
- if (ViewCompat.canScrollVertically(mRecyclerView, 1) ||
- ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
- info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
- info.setScrollable(true);
- }
- final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
- = AccessibilityNodeInfoCompat.CollectionInfoCompat
- .obtain(getRowCountForAccessibility(recycler, state),
- getColumnCountForAccessibility(recycler, state),
- isLayoutHierarchical(recycler, state),
- getSelectionModeForAccessibility(recycler, state));
- info.setCollectionInfo(collectionInfo);
- }
-
- // called by accessibility delegate
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- onInitializeAccessibilityEvent(mRecyclerView.mRecycler, mRecyclerView.mState, event);
- }
-
- /**
- * Called by the accessibility delegate to initialize an accessibility event.
- *
- * Default implementation adds item count and scroll information to the event.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @param event The event instance to initialize
- * @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
- */
- public void onInitializeAccessibilityEvent(Recycler recycler, State state,
- AccessibilityEvent event) {
- final AccessibilityRecordCompat record = AccessibilityEventCompat
- .asRecord(event);
- if (mRecyclerView == null || record == null) {
- return;
- }
- record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1)
- || ViewCompat.canScrollVertically(mRecyclerView, -1)
- || ViewCompat.canScrollHorizontally(mRecyclerView, -1)
- || ViewCompat.canScrollHorizontally(mRecyclerView, 1));
-
- if (mRecyclerView.mAdapter != null) {
- record.setItemCount(mRecyclerView.mAdapter.getItemCount());
- }
- }
-
- // called by accessibility delegate
- void onInitializeAccessibilityNodeInfoForItem(View host,
- AccessibilityNodeInfoCompat info) {
- onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
- host, info);
- }
-
- /**
- * Called by the AccessibilityDelegate when the accessibility information for a specific
- * item should be populated.
- *
- * Default implementation adds basic positioning information about the item.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @param host The child for which accessibility node info should be populated
- * @param info The info to fill out about the item
- * @see android.widget.AbsListView#onInitializeAccessibilityNodeInfoForItem(View, int,
- * android.view.accessibility.AccessibilityNodeInfo)
- */
- public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
- View host, AccessibilityNodeInfoCompat info) {
- int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
- int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
- final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo
- = AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
- columnIndexGuess, 1, false, false);
- info.setCollectionItemInfo(itemInfo);
- }
-
- /**
- * A LayoutManager can call this method to force RecyclerView to run simple animations in
- * the next layout pass, even if there is not any trigger to do so. (e.g. adapter data
- * change).
- *
- * Note that, calling this method will not guarantee that RecyclerView will run animations
- * at all. For example, if there is not any {@link ItemAnimator} set, RecyclerView will
- * not run any animations but will still clear this flag after the layout is complete.
- *
- */
- public void requestSimpleAnimationsInNextLayout() {
- mRequestedSimpleAnimations = true;
- }
-
- /**
- * Returns the selection mode for accessibility. Should be
- * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE},
- * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_SINGLE} or
- * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_MULTIPLE}.
- *
- * Default implementation returns
- * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @return Selection mode for accessibility. Default implementation returns
- * {@link AccessibilityNodeInfoCompat.CollectionInfoCompat#SELECTION_MODE_NONE}.
- */
- public int getSelectionModeForAccessibility(Recycler recycler, State state) {
- return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
- }
-
- /**
- * Returns the number of rows for accessibility.
- *
- * Default implementation returns the number of items in the adapter if LayoutManager
- * supports vertical scrolling or 1 if LayoutManager does not support vertical
- * scrolling.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @return The number of rows in LayoutManager for accessibility.
- */
- public int getRowCountForAccessibility(Recycler recycler, State state) {
- if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
- return 1;
- }
- return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() : 1;
- }
-
- /**
- * Returns the number of columns for accessibility.
- *
- * Default implementation returns the number of items in the adapter if LayoutManager
- * supports horizontal scrolling or 1 if LayoutManager does not support horizontal
- * scrolling.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @return The number of rows in LayoutManager for accessibility.
- */
- public int getColumnCountForAccessibility(Recycler recycler, State state) {
- if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
- return 1;
- }
- return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() : 1;
- }
-
- /**
- * Returns whether layout is hierarchical or not to be used for accessibility.
- *
- * Default implementation returns false.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @return True if layout is hierarchical.
- */
- public boolean isLayoutHierarchical(Recycler recycler, State state) {
- return false;
- }
-
- // called by accessibility delegate
- boolean performAccessibilityAction(int action, Bundle args) {
- return performAccessibilityAction(mRecyclerView.mRecycler, mRecyclerView.mState,
- action, args);
- }
-
- /**
- * Called by AccessibilityDelegate when an action is requested from the RecyclerView.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @param action The action to perform
- * @param args Optional action arguments
- * @see View#performAccessibilityAction(int, Bundle)
- */
- public boolean performAccessibilityAction(Recycler recycler, State state, int action,
- Bundle args) {
- if (mRecyclerView == null) {
- return false;
- }
- int vScroll = 0, hScroll = 0;
- switch (action) {
- case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
- if (ViewCompat.canScrollVertically(mRecyclerView, -1)) {
- vScroll = -(getHeight() - getPaddingTop() - getPaddingBottom());
- }
- if (ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
- hScroll = -(getWidth() - getPaddingLeft() - getPaddingRight());
- }
- break;
- case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
- if (ViewCompat.canScrollVertically(mRecyclerView, 1)) {
- vScroll = getHeight() - getPaddingTop() - getPaddingBottom();
- }
- if (ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
- hScroll = getWidth() - getPaddingLeft() - getPaddingRight();
- }
- break;
- }
- if (vScroll == 0 && hScroll == 0) {
- return false;
- }
- mRecyclerView.scrollBy(hScroll, vScroll);
- return true;
- }
-
- // called by accessibility delegate
- boolean performAccessibilityActionForItem(View view, int action, Bundle args) {
- return performAccessibilityActionForItem(mRecyclerView.mRecycler, mRecyclerView.mState,
- view, action, args);
- }
-
- /**
- * Called by AccessibilityDelegate when an accessibility action is requested on one of the
- * chidren of LayoutManager.
- *
- * Default implementation does not do anything.
- *
- * @param recycler The Recycler that can be used to convert view positions into adapter
- * positions
- * @param state The current state of RecyclerView
- * @param view The child view on which the action is performed
- * @param action The action to perform
- * @param args Optional action arguments
- * @return true if action is handled
- * @see View#performAccessibilityAction(int, Bundle)
- */
- public boolean performAccessibilityActionForItem(Recycler recycler, State state, View view,
- int action, Bundle args) {
- return false;
- }
- }
-
- private void removeFromDisappearingList(View child) {
- mDisappearingViewsInLayoutPass.remove(child);
- }
-
- private void addToDisappearingList(View child) {
- if (!mDisappearingViewsInLayoutPass.contains(child)) {
- mDisappearingViewsInLayoutPass.add(child);
- }
- }
-
- /**
- * An ItemDecoration allows the application to add a special drawing and layout offset
- * to specific item views from the adapter's data set. This can be useful for drawing dividers
- * between items, highlights, visual grouping boundaries and more.
- *
- *
All ItemDecorations are drawn in the order they were added, before the item
- * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, State) onDraw()}
- * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView,
- * State)}.
- */
- public static abstract class ItemDecoration {
- /**
- * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
- * Any content drawn by this method will be drawn before the item views are drawn,
- * and will thus appear underneath the views.
- *
- * @param c Canvas to draw into
- * @param parent RecyclerView this ItemDecoration is drawing into
- * @param state The current state of RecyclerView
- */
- public void onDraw(Canvas c, RecyclerView parent, State state) {
- onDraw(c, parent);
- }
-
- /**
- * @deprecated
- * Override {@link #onDraw(Canvas, RecyclerView, State)}
- */
- @Deprecated
- public void onDraw(Canvas c, RecyclerView parent) {
- }
-
- /**
- * Draw any appropriate decorations into the Canvas supplied to the RecyclerView.
- * Any content drawn by this method will be drawn after the item views are drawn
- * and will thus appear over the views.
- *
- * @param c Canvas to draw into
- * @param parent RecyclerView this ItemDecoration is drawing into
- * @param state The current state of RecyclerView.
- */
- public void onDrawOver(Canvas c, RecyclerView parent, State state) {
- onDrawOver(c, parent);
- }
-
- /**
- * @deprecated
- * Override {@link #onDrawOver(Canvas, RecyclerView, State)}
- */
- @Deprecated
- public void onDrawOver(Canvas c, RecyclerView parent) {
- }
-
-
- /**
- * @deprecated
- * Use {@link #getItemOffsets(Rect, View, RecyclerView, State)}
- */
- @Deprecated
- public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
- outRect.set(0, 0, 0, 0);
- }
-
- /**
- * Retrieve any offsets for the given item. Each field of outRect specifies
- * the number of pixels that the item view should be inset by, similar to padding or margin.
- * The default implementation sets the bounds of outRect to 0 and returns.
- *
- *
If this ItemDecoration does not affect the positioning of item views it should set
- * all four fields of outRect (left, top, right, bottom) to zero
- * before returning.
- *
- * @param outRect Rect to receive the output.
- * @param view The child view to decorate
- * @param parent RecyclerView this ItemDecoration is decorating
- * @param state The current state of RecyclerView.
- */
- public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
- getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewPosition(),
- parent);
- }
- }
-
- /**
- * An OnItemTouchListener allows the application to intercept touch events in progress at the
- * view hierarchy level of the RecyclerView before those touch events are considered for
- * RecyclerView's own scrolling behavior.
- *
- *
This can be useful for applications that wish to implement various forms of gestural
- * manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
- * a touch interaction already in progress even if the RecyclerView is already handling that
- * gesture stream itself for the purposes of scrolling.
- */
- public interface OnItemTouchListener {
- /**
- * Silently observe and/or take over touch events sent to the RecyclerView
- * before they are handled by either the RecyclerView itself or its child views.
- *
- *
The onInterceptTouchEvent methods of each attached OnItemTouchListener will be run
- * in the order in which each listener was added, before any other touch processing
- * by the RecyclerView itself or child views occurs.
- *
- * @param e MotionEvent describing the touch event. All coordinates are in
- * the RecyclerView's coordinate system.
- * @return true if this OnItemTouchListener wishes to begin intercepting touch events, false
- * to continue with the current behavior and continue observing future events in
- * the gesture.
- */
- public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);
-
- /**
- * Process a touch event as part of a gesture that was claimed by returning true from
- * a previous call to {@link #onInterceptTouchEvent}.
- *
- * @param e MotionEvent describing the touch event. All coordinates are in
- * the RecyclerView's coordinate system.
- */
- public void onTouchEvent(RecyclerView rv, MotionEvent e);
- }
-
- /**
- * An OnScrollListener can be set on a RecyclerView to receive messages
- * when a scrolling event has occurred on that RecyclerView.
- *
- * @see RecyclerView#setOnScrollListener(OnScrollListener)
- */
- abstract static public class OnScrollListener {
- /**
- * Callback method to be invoked when RecyclerView's scroll state changes.
- *
- * @param recyclerView The RecyclerView whose scroll state has changed.
- * @param newState The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
- * {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
- */
- public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
-
- /**
- * Callback method to be invoked when the RecyclerView has been scrolled. This will be
- * called after the scroll has completed.
- *
- * @param recyclerView The RecyclerView which scrolled.
- * @param dx The amount of horizontal scroll.
- * @param dy The amount of vertical scroll.
- */
- public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
- }
-
- /**
- * A RecyclerListener can be set on a RecyclerView to receive messages whenever
- * a view is recycled.
- *
- * @see RecyclerView#setRecyclerListener(RecyclerListener)
- */
- public interface RecyclerListener {
-
- /**
- * This method is called whenever the view in the ViewHolder is recycled.
- *
- * @param holder The ViewHolder containing the view that was recycled
- */
- public void onViewRecycled(ViewHolder holder);
- }
-
- /**
- * A ViewHolder describes an item view and metadata about its place within the RecyclerView.
- *
- *
{@link Adapter} implementations should subclass ViewHolder and add fields for caching
- * potentially expensive {@link View#findViewById(int)} results.
- *
- *
While {@link LayoutParams} belong to the {@link LayoutManager},
- * {@link ViewHolder ViewHolders} belong to the adapter. Adapters should feel free to use
- * their own custom ViewHolder implementations to store data that makes binding view contents
- * easier. Implementations should assume that individual item views will hold strong references
- * to ViewHolder objects and that RecyclerView instances may hold
- * strong references to extra off-screen item views for caching purposes
- */
- public static abstract class ViewHolder {
- public final View itemView;
- int mPosition = NO_POSITION;
- int mOldPosition = NO_POSITION;
- long mItemId = NO_ID;
- int mItemViewType = INVALID_TYPE;
- int mPreLayoutPosition = NO_POSITION;
-
- // The item that this holder is shadowing during an item change event/animation
- ViewHolder mShadowedHolder = null;
- // The item that is shadowing this holder during an item change event/animation
- ViewHolder mShadowingHolder = null;
-
- /**
- * This ViewHolder has been bound to a position; mPosition, mItemId and mItemViewType
- * are all valid.
- */
- static final int FLAG_BOUND = 1 << 0;
-
- /**
- * The data this ViewHolder's view reflects is stale and needs to be rebound
- * by the adapter. mPosition and mItemId are consistent.
- */
- static final int FLAG_UPDATE = 1 << 1;
-
- /**
- * This ViewHolder's data is invalid. The identity implied by mPosition and mItemId
- * are not to be trusted and may no longer match the item view type.
- * This ViewHolder must be fully rebound to different data.
- */
- static final int FLAG_INVALID = 1 << 2;
-
- /**
- * This ViewHolder points at data that represents an item previously removed from the
- * data set. Its view may still be used for things like outgoing animations.
- */
- static final int FLAG_REMOVED = 1 << 3;
-
- /**
- * This ViewHolder should not be recycled. This flag is set via setIsRecyclable()
- * and is intended to keep views around during animations.
- */
- static final int FLAG_NOT_RECYCLABLE = 1 << 4;
-
- /**
- * This ViewHolder is returned from scrap which means we are expecting an addView call
- * for this itemView. When returned from scrap, ViewHolder stays in the scrap list until
- * the end of the layout pass and then recycled by RecyclerView if it is not added back to
- * the RecyclerView.
- */
- static final int FLAG_RETURNED_FROM_SCRAP = 1 << 5;
-
- /**
- * This ViewHolder's contents have changed. This flag is used as an indication that
- * change animations may be used, if supported by the ItemAnimator.
- */
- static final int FLAG_CHANGED = 1 << 6;
-
- /**
- * This ViewHolder is fully managed by the LayoutManager. We do not scrap, recycle or remove
- * it unless LayoutManager is replaced.
- * It is still fully visible to the LayoutManager.
- */
- static final int FLAG_IGNORE = 1 << 7;
-
- private int mFlags;
-
- private int mIsRecyclableCount = 0;
-
- // If non-null, view is currently considered scrap and may be reused for other data by the
- // scrap container.
- private Recycler mScrapContainer = null;
-
- public ViewHolder(View itemView) {
- if (itemView == null) {
- throw new IllegalArgumentException("itemView may not be null");
- }
- this.itemView = itemView;
- }
-
- void flagRemovedAndOffsetPosition(int mNewPosition, int offset, boolean applyToPreLayout) {
- addFlags(ViewHolder.FLAG_REMOVED);
- offsetPosition(offset, applyToPreLayout);
- mPosition = mNewPosition;
- }
-
- void offsetPosition(int offset, boolean applyToPreLayout) {
- if (mOldPosition == NO_POSITION) {
- mOldPosition = mPosition;
- }
- if (mPreLayoutPosition == NO_POSITION) {
- mPreLayoutPosition = mPosition;
- }
- if (applyToPreLayout) {
- mPreLayoutPosition += offset;
- }
- mPosition += offset;
- if (itemView.getLayoutParams() != null) {
- ((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;
- }
- }
-
- void clearOldPosition() {
- mOldPosition = NO_POSITION;
- mPreLayoutPosition = NO_POSITION;
- }
-
- void saveOldPosition() {
- if (mOldPosition == NO_POSITION) {
- mOldPosition = mPosition;
- }
- }
-
- boolean shouldIgnore() {
- return (mFlags & FLAG_IGNORE) != 0;
- }
-
- public final int getPosition() {
- return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
- }
-
- /**
- * When LayoutManager supports animations, RecyclerView tracks 3 positions for ViewHolders
- * to perform animations.
- *
- * If a ViewHolder was laid out in the previous onLayout call, old position will keep its
- * adapter index in the previous layout.
- *
- * @return The previous adapter index of the Item represented by this ViewHolder or
- * {@link #NO_POSITION} if old position does not exists or cleared (pre-layout is
- * complete).
- */
- public final int getOldPosition() {
- return mOldPosition;
- }
-
- /**
- * Returns The itemId represented by this ViewHolder.
- *
- * @return The the item's id if adapter has stable ids, {@link RecyclerView#NO_ID}
- * otherwise
- */
- public final long getItemId() {
- return mItemId;
- }
-
- /**
- * @return The view type of this ViewHolder.
- */
- public final int getItemViewType() {
- return mItemViewType;
- }
-
- boolean isScrap() {
- return mScrapContainer != null;
- }
-
- void unScrap() {
- mScrapContainer.unscrapView(this);
- }
-
- boolean wasReturnedFromScrap() {
- return (mFlags & FLAG_RETURNED_FROM_SCRAP) != 0;
- }
-
- void clearReturnedFromScrapFlag() {
- mFlags = mFlags & ~FLAG_RETURNED_FROM_SCRAP;
- }
-
- void stopIgnoring() {
- mFlags = mFlags & ~FLAG_IGNORE;
- }
-
- void setScrapContainer(Recycler recycler) {
- mScrapContainer = recycler;
- }
-
- boolean isInvalid() {
- return (mFlags & FLAG_INVALID) != 0;
- }
-
- boolean needsUpdate() {
- return (mFlags & FLAG_UPDATE) != 0;
- }
-
- boolean isChanged() {
- return (mFlags & FLAG_CHANGED) != 0;
- }
-
- boolean isBound() {
- return (mFlags & FLAG_BOUND) != 0;
- }
-
- boolean isRemoved() {
- return (mFlags & FLAG_REMOVED) != 0;
- }
-
- void setFlags(int flags, int mask) {
- mFlags = (mFlags & ~mask) | (flags & mask);
- }
-
- void addFlags(int flags) {
- mFlags |= flags;
- }
-
- void resetInternal() {
- mFlags = 0;
- mPosition = NO_POSITION;
- mOldPosition = NO_POSITION;
- mItemId = NO_ID;
- mPreLayoutPosition = NO_POSITION;
- mIsRecyclableCount = 0;
- mShadowedHolder = null;
- mShadowingHolder = null;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("ViewHolder{" +
- Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId +
- ", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
- if (isScrap()) sb.append(" scrap");
- if (isInvalid()) sb.append(" invalid");
- if (!isBound()) sb.append(" unbound");
- if (needsUpdate()) sb.append(" update");
- if (isRemoved()) sb.append(" removed");
- if (shouldIgnore()) sb.append(" ignored");
- if (isChanged()) sb.append(" changed");
- if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
- if (itemView.getParent() == null) sb.append(" no parent");
- sb.append("}");
- return sb.toString();
- }
-
- /**
- * Informs the recycler whether this item can be recycled. Views which are not
- * recyclable will not be reused for other items until setIsRecyclable() is
- * later set to true. Calls to setIsRecyclable() should always be paired (one
- * call to setIsRecyclabe(false) should always be matched with a later call to
- * setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally
- * reference-counted.
- *
- * @param recyclable Whether this item is available to be recycled. Default value
- * is true.
- */
- public final void setIsRecyclable(boolean recyclable) {
- mIsRecyclableCount = recyclable ? mIsRecyclableCount - 1 : mIsRecyclableCount + 1;
- if (mIsRecyclableCount < 0) {
- mIsRecyclableCount = 0;
- if (DEBUG) {
- throw new RuntimeException("isRecyclable decremented below 0: " +
- "unmatched pair of setIsRecyable() calls for " + this);
- }
- Log.e(VIEW_LOG_TAG, "isRecyclable decremented below 0: " +
- "unmatched pair of setIsRecyable() calls for " + this);
- } else if (!recyclable && mIsRecyclableCount == 1) {
- mFlags |= FLAG_NOT_RECYCLABLE;
- } else if (recyclable && mIsRecyclableCount == 0) {
- mFlags &= ~FLAG_NOT_RECYCLABLE;
- }
- if (DEBUG) {
- Log.d(TAG, "setIsRecyclable val:" + recyclable + ":" + this);
- }
- }
-
- /**
- * @see {@link #setIsRecyclable(boolean)}
- *
- * @return true if this item is available to be recycled, false otherwise.
- */
- public final boolean isRecyclable() {
- return (mFlags & FLAG_NOT_RECYCLABLE) == 0 &&
- !ViewCompat.hasTransientState(itemView);
- }
- }
-
- /**
- * {@link MarginLayoutParams LayoutParams} subclass for children of
- * {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
- * to create their own subclass of this LayoutParams class
- * to store any additional required per-child view metadata about the layout.
- */
- public static class LayoutParams extends MarginLayoutParams {
- ViewHolder mViewHolder;
- final Rect mDecorInsets = new Rect();
- boolean mInsetsDirty = true;
- // Flag is set to true if the view is bound while it is detached from RV.
- // In this case, we need to manually call invalidate after view is added to guarantee that
- // invalidation is populated through the View hierarchy
- boolean mPendingInvalidate = false;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(MarginLayoutParams source) {
- super(source);
- }
-
- public LayoutParams(ViewGroup.LayoutParams source) {
- super(source);
- }
-
- public LayoutParams(LayoutParams source) {
- super((ViewGroup.LayoutParams) source);
- }
-
- /**
- * Returns true if the view this LayoutParams is attached to needs to have its content
- * updated from the corresponding adapter.
- *
- * @return true if the view should have its content updated
- */
- public boolean viewNeedsUpdate() {
- return mViewHolder.needsUpdate();
- }
-
- /**
- * Returns true if the view this LayoutParams is attached to is now representing
- * potentially invalid data. A LayoutManager should scrap/recycle it.
- *
- * @return true if the view is invalid
- */
- public boolean isViewInvalid() {
- return mViewHolder.isInvalid();
- }
-
- /**
- * Returns true if the adapter data item corresponding to the view this LayoutParams
- * is attached to has been removed from the data set. A LayoutManager may choose to
- * treat it differently in order to animate its outgoing or disappearing state.
- *
- * @return true if the item the view corresponds to was removed from the data set
- */
- public boolean isItemRemoved() {
- return mViewHolder.isRemoved();
- }
-
- /**
- * Returns true if the adapter data item corresponding to the view this LayoutParams
- * is attached to has been changed in the data set. A LayoutManager may choose to
- * treat it differently in order to animate its changing state.
- *
- * @return true if the item the view corresponds to was changed in the data set
- */
- public boolean isItemChanged() {
- return mViewHolder.isChanged();
- }
-
- /**
- * Returns the position that the view this LayoutParams is attached to corresponds to.
- *
- * @return the adapter position this view was bound from
- */
- public int getViewPosition() {
- return mViewHolder.getPosition();
- }
- }
-
- /**
- * Observer base class for watching changes to an {@link Adapter}.
- * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
- */
- public static abstract class AdapterDataObserver {
- public void onChanged() {
- // Do nothing
- }
-
- public void onItemRangeChanged(int positionStart, int itemCount) {
- // do nothing
- }
-
- public void onItemRangeInserted(int positionStart, int itemCount) {
- // do nothing
- }
-
- public void onItemRangeRemoved(int positionStart, int itemCount) {
- // do nothing
- }
-
- public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
- // do nothing
- }
- }
-
- /**
- *
Base class for smooth scrolling. Handles basic tracking of the target view position and
- * provides methods to trigger a programmatic scroll.
- *
- * @see LinearSmoothScroller
- */
- public static abstract class SmoothScroller {
-
- private int mTargetPosition = RecyclerView.NO_POSITION;
-
- private RecyclerView mRecyclerView;
-
- private LayoutManager mLayoutManager;
-
- private boolean mPendingInitialRun;
-
- private boolean mRunning;
-
- private View mTargetView;
-
- private final Action mRecyclingAction;
-
- public SmoothScroller() {
- mRecyclingAction = new Action(0, 0);
- }
-
- /**
- * Starts a smooth scroll for the given target position.
- *
In each animation step, {@link RecyclerView} will check
- * for the target view and call either
- * {@link #onTargetFound(View, State, Action)} or
- * {@link #onSeekTargetStep(int, int, State, Action)} until
- * SmoothScroller is stopped.
- *
- *
Note that if RecyclerView finds the target view, it will automatically stop the
- * SmoothScroller. This does not mean that scroll will stop, it only means it will
- * stop calling SmoothScroller in each animation step.
- */
- void start(RecyclerView recyclerView, LayoutManager layoutManager) {
- mRecyclerView = recyclerView;
- mLayoutManager = layoutManager;
- if (mTargetPosition == RecyclerView.NO_POSITION) {
- throw new IllegalArgumentException("Invalid target position");
- }
- mRecyclerView.mState.mTargetPosition = mTargetPosition;
- mRunning = true;
- mPendingInitialRun = true;
- mTargetView = findViewByPosition(getTargetPosition());
- onStart();
- mRecyclerView.mViewFlinger.postOnAnimation();
- }
-
- public void setTargetPosition(int targetPosition) {
- mTargetPosition = targetPosition;
- }
-
- /**
- * @return The LayoutManager to which this SmoothScroller is attached
- */
- public LayoutManager getLayoutManager() {
- return mLayoutManager;
- }
-
- /**
- * Stops running the SmoothScroller in each animation callback. Note that this does not
- * cancel any existing {@link Action} updated by
- * {@link #onTargetFound(View, State, Action)} or
- * {@link #onSeekTargetStep(int, int, State, Action)}.
- */
- final protected void stop() {
- if (!mRunning) {
- return;
- }
- onStop();
- mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
- mTargetView = null;
- mTargetPosition = RecyclerView.NO_POSITION;
- mPendingInitialRun = false;
- mRunning = false;
- // trigger a cleanup
- mLayoutManager.onSmoothScrollerStopped(this);
- // clear references to avoid any potential leak by a custom smooth scroller
- mLayoutManager = null;
- mRecyclerView = null;
- }
-
- /**
- * Returns true if SmoothScroller has been started but has not received the first
- * animation
- * callback yet.
- *
- * @return True if this SmoothScroller is waiting to start
- */
- public boolean isPendingInitialRun() {
- return mPendingInitialRun;
- }
-
-
- /**
- * @return True if SmoothScroller is currently active
- */
- public boolean isRunning() {
- return mRunning;
- }
-
- /**
- * Returns the adapter position of the target item
- *
- * @return Adapter position of the target item or
- * {@link RecyclerView#NO_POSITION} if no target view is set.
- */
- public int getTargetPosition() {
- return mTargetPosition;
- }
-
- private void onAnimation(int dx, int dy) {
- if (!mRunning || mTargetPosition == RecyclerView.NO_POSITION) {
- stop();
- }
- mPendingInitialRun = false;
- if (mTargetView != null) {
- // verify target position
- if (getChildPosition(mTargetView) == mTargetPosition) {
- onTargetFound(mTargetView, mRecyclerView.mState, mRecyclingAction);
- mRecyclingAction.runIfNecessary(mRecyclerView);
- stop();
- } else {
- Log.e(TAG, "Passed over target position while smooth scrolling.");
- mTargetView = null;
- }
- }
- if (mRunning) {
- onSeekTargetStep(dx, dy, mRecyclerView.mState, mRecyclingAction);
- mRecyclingAction.runIfNecessary(mRecyclerView);
- }
- }
-
- /**
- * @see RecyclerView#getChildPosition(View)
- */
- public int getChildPosition(View view) {
- return mRecyclerView.getChildPosition(view);
- }
-
- /**
- * @see LayoutManager#getChildCount()
- */
- public int getChildCount() {
- return mRecyclerView.mLayout.getChildCount();
- }
-
- /**
- * @see LayoutManager#findViewByPosition(int)
- */
- public View findViewByPosition(int position) {
- return mRecyclerView.mLayout.findViewByPosition(position);
- }
-
- /**
- * @see RecyclerView#scrollToPosition(int)
- */
- public void instantScrollToPosition(int position) {
- mRecyclerView.scrollToPosition(position);
- }
-
- protected void onChildAttachedToWindow(View child) {
- if (getChildPosition(child) == getTargetPosition()) {
- mTargetView = child;
- if (DEBUG) {
- Log.d(TAG, "smooth scroll target view has been attached");
- }
- }
- }
-
- /**
- * Normalizes the vector.
- * @param scrollVector The vector that points to the target scroll position
- */
- protected void normalize(PointF scrollVector) {
- final double magnitute = Math.sqrt(scrollVector.x * scrollVector.x + scrollVector.y *
- scrollVector.y);
- scrollVector.x /= magnitute;
- scrollVector.y /= magnitute;
- }
-
- /**
- * Called when smooth scroll is started. This might be a good time to do setup.
- */
- abstract protected void onStart();
-
- /**
- * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
- * @see #stop()
- */
- abstract protected void onStop();
-
- /**
- *
RecyclerView will call this method each time it scrolls until it can find the target
- * position in the layout.
- *
SmoothScroller should check dx, dy and if scroll should be changed, update the
- * provided {@link Action} to define the next scroll.
- *
- * @param dx Last scroll amount horizontally
- * @param dy Last scroll amount verticaully
- * @param state Transient state of RecyclerView
- * @param action If you want to trigger a new smooth scroll and cancel the previous one,
- * update this object.
- */
- abstract protected void onSeekTargetStep(int dx, int dy, State state, Action action);
-
- /**
- * Called when the target position is laid out. This is the last callback SmoothScroller
- * will receive and it should update the provided {@link Action} to define the scroll
- * details towards the target view.
- * @param targetView The view element which render the target position.
- * @param state Transient state of RecyclerView
- * @param action Action instance that you should update to define final scroll action
- * towards the targetView
- * @return An {@link Action} to finalize the smooth scrolling
- */
- abstract protected void onTargetFound(View targetView, State state, Action action);
-
- /**
- * Holds information about a smooth scroll request by a {@link SmoothScroller}.
- */
- public static class Action {
-
- public static final int UNDEFINED_DURATION = Integer.MIN_VALUE;
-
- private int mDx;
-
- private int mDy;
-
- private int mDuration;
-
- private Interpolator mInterpolator;
-
- private boolean changed = false;
-
- // we track this variable to inform custom implementer if they are updating the action
- // in every animation callback
- private int consecutiveUpdates = 0;
-
- /**
- * @param dx Pixels to scroll horizontally
- * @param dy Pixels to scroll vertically
- */
- public Action(int dx, int dy) {
- this(dx, dy, UNDEFINED_DURATION, null);
- }
-
- /**
- * @param dx Pixels to scroll horizontally
- * @param dy Pixels to scroll vertically
- * @param duration Duration of the animation in milliseconds
- */
- public Action(int dx, int dy, int duration) {
- this(dx, dy, duration, null);
- }
-
- /**
- * @param dx Pixels to scroll horizontally
- * @param dy Pixels to scroll vertically
- * @param duration Duration of the animation in milliseconds
- * @param interpolator Interpolator to be used when calculating scroll position in each
- * animation step
- */
- public Action(int dx, int dy, int duration, Interpolator interpolator) {
- mDx = dx;
- mDy = dy;
- mDuration = duration;
- mInterpolator = interpolator;
- }
- private void runIfNecessary(RecyclerView recyclerView) {
- if (changed) {
- validate();
- if (mInterpolator == null) {
- if (mDuration == UNDEFINED_DURATION) {
- recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy);
- } else {
- recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration);
- }
- } else {
- recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
- }
- consecutiveUpdates ++;
- if (consecutiveUpdates > 10) {
- // A new action is being set in every animation step. This looks like a bad
- // implementation. Inform developer.
- Log.e(TAG, "Smooth Scroll action is being updated too frequently. Make sure"
- + " you are not changing it unless necessary");
- }
- changed = false;
- } else {
- consecutiveUpdates = 0;
- }
- }
-
- private void validate() {
- if (mInterpolator != null && mDuration < 1) {
- throw new IllegalStateException("If you provide an interpolator, you must"
- + " set a positive duration");
- } else if (mDuration < 1) {
- throw new IllegalStateException("Scroll duration must be a positive number");
- }
- }
-
- public int getDx() {
- return mDx;
- }
-
- public void setDx(int dx) {
- changed = true;
- mDx = dx;
- }
-
- public int getDy() {
- return mDy;
- }
-
- public void setDy(int dy) {
- changed = true;
- mDy = dy;
- }
-
- public int getDuration() {
- return mDuration;
- }
-
- public void setDuration(int duration) {
- changed = true;
- mDuration = duration;
- }
-
- public Interpolator getInterpolator() {
- return mInterpolator;
- }
-
- /**
- * Sets the interpolator to calculate scroll steps
- * @param interpolator The interpolator to use. If you specify an interpolator, you must
- * also set the duration.
- * @see #setDuration(int)
- */
- public void setInterpolator(Interpolator interpolator) {
- changed = true;
- mInterpolator = interpolator;
- }
-
- /**
- * Updates the action with given parameters.
- * @param dx Pixels to scroll horizontally
- * @param dy Pixels to scroll vertically
- * @param duration Duration of the animation in milliseconds
- * @param interpolator Interpolator to be used when calculating scroll position in each
- * animation step
- */
- public void update(int dx, int dy, int duration, Interpolator interpolator) {
- mDx = dx;
- mDy = dy;
- mDuration = duration;
- mInterpolator = interpolator;
- changed = true;
- }
- }
- }
-
- static class AdapterDataObservable extends Observable {
- public boolean hasObservers() {
- return !mObservers.isEmpty();
- }
-
- public void notifyChanged() {
- // since onChanged() is implemented by the app, it could do anything, including
- // removing itself from {@link mObservers} - and that could cause problems if
- // an iterator is used on the ArrayList {@link mObservers}.
- // to avoid such problems, just march thru the list in the reverse order.
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onChanged();
- }
- }
-
- public void notifyItemRangeChanged(int positionStart, int itemCount) {
- // since onItemRangeChanged() is implemented by the app, it could do anything, including
- // removing itself from {@link mObservers} - and that could cause problems if
- // an iterator is used on the ArrayList {@link mObservers}.
- // to avoid such problems, just march thru the list in the reverse order.
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onItemRangeChanged(positionStart, itemCount);
- }
- }
-
- public void notifyItemRangeInserted(int positionStart, int itemCount) {
- // since onItemRangeInserted() is implemented by the app, it could do anything,
- // including removing itself from {@link mObservers} - and that could cause problems if
- // an iterator is used on the ArrayList {@link mObservers}.
- // to avoid such problems, just march thru the list in the reverse order.
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
- }
- }
-
- public void notifyItemRangeRemoved(int positionStart, int itemCount) {
- // since onItemRangeRemoved() is implemented by the app, it could do anything, including
- // removing itself from {@link mObservers} - and that could cause problems if
- // an iterator is used on the ArrayList {@link mObservers}.
- // to avoid such problems, just march thru the list in the reverse order.
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
- }
- }
-
- public void notifyItemMoved(int fromPosition, int toPosition) {
- for (int i = mObservers.size() - 1; i >= 0; i--) {
- mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
- }
- }
- }
-
- static class SavedState extends BaseSavedState {
-
- Parcelable mLayoutState;
-
- /**
- * called by CREATOR
- */
- SavedState(Parcel in) {
- super(in);
- mLayoutState = in.readParcelable(LayoutManager.class.getClassLoader());
- }
-
- /**
- * Called by onSaveInstanceState
- */
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeParcelable(mLayoutState, 0);
- }
-
- private void copyFrom(SavedState other) {
- mLayoutState = other.mLayoutState;
- }
-
- public static final Creator CREATOR
- = new Creator() {
- @Override
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- @Override
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
- /**
- *
Contains useful information about the current RecyclerView state like target scroll
- * position or view focus. State object can also keep arbitrary data, identified by resource
- * ids.
- *
Often times, RecyclerView components will need to pass information between each other.
- * To provide a well defined data bus between components, RecyclerView passes the same State
- * object to component callbacks and these components can use it to exchange data.
- *
If you implement custom components, you can use State's put/get/remove methods to pass
- * data between your components without needing to manage their lifecycles.
- */
- public static class State {
-
- private int mTargetPosition = RecyclerView.NO_POSITION;
- ArrayMap mPreLayoutHolderMap =
- new ArrayMap();
- ArrayMap mPostLayoutHolderMap =
- new ArrayMap();
- // nullable
- ArrayMap mOldChangedHolders = new ArrayMap();
-
- private SparseArray