1e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar/* 2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project 3e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 4e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * you may not use this file except in compliance with the License. 6e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * You may obtain a copy of the License at 7e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 8e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 10e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * See the License for the specific language governing permissions and 14e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * limitations under the License. 15e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 16ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.recyclerview.widget; 17e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 18ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR; 19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_AND_DISAPPEAR; 20ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR_PRE_AND_POST; 21ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED; 22ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_POST; 23ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_PRE; 24ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_PRE_AND_POST; 251e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas 26ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull; 27ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable; 28ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.VisibleForTesting; 29ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.collection.ArrayMap; 30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.collection.LongSparseArray; 31ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.util.Pools; 32e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar/** 33c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette * This class abstracts all tracking for Views to run animations. 34e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 35e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyarclass ViewInfoStore { 36e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 37e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private static final boolean DEBUG = false; 38e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 39e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 40e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * View data records for pre-layout 41e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 42e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar @VisibleForTesting 43ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas final ArrayMap<RecyclerView.ViewHolder, InfoRecord> mLayoutHolderMap = new ArrayMap<>(); 44e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 45e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar @VisibleForTesting 46ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas final LongSparseArray<RecyclerView.ViewHolder> mOldChangedHolders = new LongSparseArray<>(); 47e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 48e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 49e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Clears the state and all existing tracking data 50e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 51e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar void clear() { 52e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mLayoutHolderMap.clear(); 53e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mOldChangedHolders.clear(); 54e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 55e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 56e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 57e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Adds the item information to the prelayout tracking 58e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param holder The ViewHolder whose information is being saved 59e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param info The information to save 60e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 61ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void addToPreLayout(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) { 62e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord record = mLayoutHolderMap.get(holder); 63e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (record == null) { 64e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record = InfoRecord.obtain(); 65e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mLayoutHolderMap.put(holder, record); 66e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 67e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.preInfo = info; 68e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.flags |= FLAG_PRE; 69e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 70e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 71ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas boolean isDisappearing(RecyclerView.ViewHolder holder) { 7213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar final InfoRecord record = mLayoutHolderMap.get(holder); 7313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar return record != null && ((record.flags & FLAG_DISAPPEARED) != 0); 7413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 7513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar 76e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 77e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Finds the ItemHolderInfo for the given ViewHolder in preLayout list and removes it. 7813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar * 79e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param vh The ViewHolder whose information is being queried 80e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @return The ItemHolderInfo for the given ViewHolder or null if it does not exist 81e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 82e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar @Nullable 83ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas RecyclerView.ItemAnimator.ItemHolderInfo popFromPreLayout(RecyclerView.ViewHolder vh) { 8413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar return popFromLayoutStep(vh, FLAG_PRE); 8513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 8613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar 8713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar /** 8813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar * Finds the ItemHolderInfo for the given ViewHolder in postLayout list and removes it. 8913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar * 9013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar * @param vh The ViewHolder whose information is being queried 9113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar * @return The ItemHolderInfo for the given ViewHolder or null if it does not exist 9213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar */ 9313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar @Nullable 94ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas RecyclerView.ItemAnimator.ItemHolderInfo popFromPostLayout(RecyclerView.ViewHolder vh) { 9513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar return popFromLayoutStep(vh, FLAG_POST); 9613a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 9713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar 98ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas private RecyclerView.ItemAnimator.ItemHolderInfo popFromLayoutStep(RecyclerView.ViewHolder vh, int flag) { 99e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar int index = mLayoutHolderMap.indexOfKey(vh); 100e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (index < 0) { 101e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return null; 102e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 103e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final InfoRecord record = mLayoutHolderMap.valueAt(index); 10413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar if (record != null && (record.flags & flag) != 0) { 10513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar record.flags &= ~flag; 106ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas final RecyclerView.ItemAnimator.ItemHolderInfo info; 10713a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar if (flag == FLAG_PRE) { 10813a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar info = record.preInfo; 10913a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } else if (flag == FLAG_POST) { 11013a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar info = record.postInfo; 11113a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } else { 11213a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar throw new IllegalArgumentException("Must provide flag PRE or POST"); 11313a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar } 11413a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar // if not pre-post flag is left, clear. 11513a0acc0df34344c0b4fd4b494a64e7dcf195b56Yigit Boyar if ((record.flags & (FLAG_PRE | FLAG_POST)) == 0) { 116e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mLayoutHolderMap.removeAt(index); 117e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord.recycle(record); 118e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 119e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return info; 120e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 121e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return null; 122e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 123e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 124e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 125e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Adds the given ViewHolder to the oldChangeHolders list 126e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param key The key to identify the ViewHolder. 127e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param holder The ViewHolder to store 128e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 129ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void addToOldChangeHolders(long key, RecyclerView.ViewHolder holder) { 130e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mOldChangedHolders.put(key, holder); 131e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 132e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 133e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 134e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Adds the given ViewHolder to the appeared in pre layout list. These are Views added by the 135e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * LayoutManager during a pre-layout pass. We distinguish them from other views that were 136e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * already in the pre-layout so that ItemAnimator can choose to run a different animation for 137e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * them. 138e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 139e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param holder The ViewHolder to store 140e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param info The information to save 141e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 142ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void addToAppearedInPreLayoutHolders(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) { 143e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord record = mLayoutHolderMap.get(holder); 144e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (record == null) { 145e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record = InfoRecord.obtain(); 146e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mLayoutHolderMap.put(holder, record); 147e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 148e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.flags |= FLAG_APPEAR; 149e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.preInfo = info; 150e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 151e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 152e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 153e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Checks whether the given ViewHolder is in preLayout list 154e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param viewHolder The ViewHolder to query 155e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 156e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @return True if the ViewHolder is present in preLayout, false otherwise 157e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 158ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas boolean isInPreLayout(RecyclerView.ViewHolder viewHolder) { 159e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final InfoRecord record = mLayoutHolderMap.get(viewHolder); 160e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return record != null && (record.flags & FLAG_PRE) != 0; 161e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 162e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 163e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 164e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Queries the oldChangeHolder list for the given key. If they are not tracked, simply returns 165e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * null. 166e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param key The key to be used to find the ViewHolder. 167e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 168e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @return A ViewHolder if exists or null if it does not exist. 169e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 170ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas RecyclerView.ViewHolder getFromOldChangeHolders(long key) { 171e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return mOldChangedHolders.get(key); 172e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 173e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 174e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 175e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Adds the item information to the post layout list 176e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param holder The ViewHolder whose information is being saved 177e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param info The information to save 178e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 179ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void addToPostLayout(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) { 180e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord record = mLayoutHolderMap.get(holder); 181e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (record == null) { 182e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record = InfoRecord.obtain(); 183e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mLayoutHolderMap.put(holder, record); 184e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 185e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.postInfo = info; 186e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.flags |= FLAG_POST; 187e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 188e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 189e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 190e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * A ViewHolder might be added by the LayoutManager just to animate its disappearance. 191e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * This list holds such items so that we can animate / recycle these ViewHolders properly. 192e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * 193e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param holder The ViewHolder which disappeared during a layout. 194e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 195ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void addToDisappearedInLayout(RecyclerView.ViewHolder holder) { 196e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord record = mLayoutHolderMap.get(holder); 197e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (record == null) { 198e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record = InfoRecord.obtain(); 199e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mLayoutHolderMap.put(holder, record); 200e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 201e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.flags |= FLAG_DISAPPEARED; 202e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 203e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 204e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 205e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Removes a ViewHolder from disappearing list. 206e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param holder The ViewHolder to be removed from the disappearing list. 207e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 208ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void removeFromDisappearedInLayout(RecyclerView.ViewHolder holder) { 209e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord record = mLayoutHolderMap.get(holder); 210e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (record == null) { 211e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return; 212e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 213e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.flags &= ~FLAG_DISAPPEARED; 214e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 215e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 216e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar void process(ProcessCallback callback) { 2171e827caa16440590647019b9c1338f6309c5be7aAurimas Liutikas for (int index = mLayoutHolderMap.size() - 1; index >= 0; index--) { 218ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas final RecyclerView.ViewHolder viewHolder = mLayoutHolderMap.keyAt(index); 219e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final InfoRecord record = mLayoutHolderMap.removeAt(index); 220e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) { 221e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // Appeared then disappeared. Not useful for animations. 222e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar callback.unused(viewHolder); 223e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } else if ((record.flags & FLAG_DISAPPEARED) != 0) { 224e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // Set as "disappeared" by the LayoutManager (addDisappearingView) 225a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar if (record.preInfo == null) { 226a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar // similar to appear disappear but happened between different layout passes. 227a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar // this can happen when the layout manager is using auto-measure 228a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar callback.unused(viewHolder); 229a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar } else { 230a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar callback.processDisappeared(viewHolder, record.preInfo, record.postInfo); 231a7d7e30042bb6fbe2944b752a24fd57fd31d285fYigit Boyar } 232e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } else if ((record.flags & FLAG_APPEAR_PRE_AND_POST) == FLAG_APPEAR_PRE_AND_POST) { 233e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // Appeared in the layout but not in the adapter (e.g. entered the viewport) 234e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar callback.processAppeared(viewHolder, record.preInfo, record.postInfo); 235e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) { 236e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // Persistent in both passes. Animate persistence 237e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar callback.processPersistent(viewHolder, record.preInfo, record.postInfo); 238e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } else if ((record.flags & FLAG_PRE) != 0) { 239e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // Was in pre-layout, never been added to post layout 240e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar callback.processDisappeared(viewHolder, record.preInfo, null); 241e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } else if ((record.flags & FLAG_POST) != 0) { 242e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // Was not in pre-layout, been added to post layout 243e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar callback.processAppeared(viewHolder, record.preInfo, record.postInfo); 244e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } else if ((record.flags & FLAG_APPEAR) != 0) { 245e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // Scrap view. RecyclerView will handle removing/recycling this. 246e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } else if (DEBUG) { 247e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar throw new IllegalStateException("record without any reasonable flag combination:/"); 248e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 249e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord.recycle(record); 250e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 251e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 252e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 253e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar /** 254e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * Removes the ViewHolder from all list 255e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar * @param holder The ViewHolder which we should stop tracking 256e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar */ 257ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void removeViewHolder(RecyclerView.ViewHolder holder) { 258e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar for (int i = mOldChangedHolders.size() - 1; i >= 0; i--) { 259e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (holder == mOldChangedHolders.valueAt(i)) { 260e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar mOldChangedHolders.removeAt(i); 261e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar break; 262e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 263e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 264e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar final InfoRecord info = mLayoutHolderMap.remove(holder); 265e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar if (info != null) { 266e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord.recycle(info); 267e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 268e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 269e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 270e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar void onDetach() { 271e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord.drainCache(); 272e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 273e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 274ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas public void onViewDetached(RecyclerView.ViewHolder viewHolder) { 2754143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar removeFromDisappearedInLayout(viewHolder); 2764143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar } 2774143554adb9b31b700b6876a251a64419e6111e2Yigit Boyar 278e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar interface ProcessCallback { 279ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void processDisappeared(RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ItemAnimator.ItemHolderInfo preInfo, 280ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas @Nullable RecyclerView.ItemAnimator.ItemHolderInfo postInfo); 281ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void processAppeared(RecyclerView.ViewHolder viewHolder, @Nullable RecyclerView.ItemAnimator.ItemHolderInfo preInfo, 282ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas RecyclerView.ItemAnimator.ItemHolderInfo postInfo); 283ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void processPersistent(RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ItemAnimator.ItemHolderInfo preInfo, 284ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas @NonNull RecyclerView.ItemAnimator.ItemHolderInfo postInfo); 285ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas void unused(RecyclerView.ViewHolder holder); 286e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 287e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 288e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static class InfoRecord { 289e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // disappearing list 290e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static final int FLAG_DISAPPEARED = 1; 291e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // appear in pre layout list 292e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static final int FLAG_APPEAR = 1 << 1; 293e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // pre layout, this is necessary to distinguish null item info 294e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static final int FLAG_PRE = 1 << 2; 295e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar // post layout, this is necessary to distinguish null item info 296e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static final int FLAG_POST = 1 << 3; 297e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static final int FLAG_APPEAR_AND_DISAPPEAR = FLAG_APPEAR | FLAG_DISAPPEARED; 298e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static final int FLAG_PRE_AND_POST = FLAG_PRE | FLAG_POST; 299e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static final int FLAG_APPEAR_PRE_AND_POST = FLAG_APPEAR | FLAG_PRE | FLAG_POST; 300e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar int flags; 301ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas @Nullable 302ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas RecyclerView.ItemAnimator.ItemHolderInfo preInfo; 303ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas @Nullable 304ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas RecyclerView.ItemAnimator.ItemHolderInfo postInfo; 305e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static Pools.Pool<InfoRecord> sPool = new Pools.SimplePool<>(20); 306e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 307e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar private InfoRecord() { 308e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 309e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 310e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static InfoRecord obtain() { 311e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar InfoRecord record = sPool.acquire(); 312e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar return record == null ? new InfoRecord() : record; 313e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 314e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 315e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static void recycle(InfoRecord record) { 316e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.flags = 0; 317e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.preInfo = null; 318e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar record.postInfo = null; 319e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar sPool.release(record); 320e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 321e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar 322e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar static void drainCache() { 323e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar //noinspection StatementWithEmptyBody 324e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar while (sPool.acquire() != null); 325e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 326e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar } 327e09e0b4ea04b6b6b0ef6c62979e8abdead0bf378Yigit Boyar} 328