在不影响当前可见视图的情况下从 RecyclerView 适配器中删除数据 children
Remove data children from RecyclerView adapter without affecting currently visible view
我正在使用 Recycler 视图构建无限水平滚动列表。
1-2 children 在任何时候都可以在屏幕上看到(因为它们的宽度大于屏幕视口,所以我们永远不会有超过 2 children 可见)。
初始状态:
当用户滚动时(他们可以向前和向后滚动 - 但为了这个例子的动摇,我们假设他们正在滚动 BACKWARDS),我正在获取更多数据,我将它们添加到我的适配器数据中。
然后我会打电话给 notifyItemRangeInserted
- 到目前为止一切顺利。
前置后:
剪辑时间:
同时我添加所有这些 children 我检查我们现在是否在数据源中得到 多于 MAX_DATASOURCE_CNT
项。
让我们假设 MAX_DATASOURCE_CNT
在这个例子中是 6。
如果我们这样做,我会从数据源的末尾删除不必要的额外 children,然后调用 notifyItemRangeRemoved
。
我这样做是因为我不想用未使用的数据淹没设备。我现在过度简化了示例,但实际上我的视图可以在用户滚动时累积数千个未使用的数据项,我想 trim 动态地 trim 我的数据源的另一端。
问题:
前置过程发生后,如果用户滚动速度太快,有时 onBindViewHolder(RecyclerView.ViewHolder holder, int position)
会被调用 position == 0
(也 holder.getAdapterPosition() == 0
)
这完全打乱了我的观点,就好像我的观点跳到了一个完全不相关的 child 并显示了那个。
当前解决方法:
我注意到,如果我删除基本上是这些代码行的整个剪辑过程:
if (itemCountAfterPrepend > MAX_DATASOURCE_CNT) {
int extraItemCnt = itemCountAfterPrepend - MAX_DATASOURCE_CNT;
// remove every extra item from the end of the array
removeRange(MAX_DATASOURCE_CNT, itemCountAfterPrepend);
// and notify our adapter about the removal
notifyItemRangeRemoved(MAX_DATASOURCE_CNT, extraItemCnt);
}
一切正常。但是我真的很想 trim 我的数据。
我能做什么?
谢谢
好的,我没有找到具体的答案,所以这就是我所做的。
由于问题是 notifyItemRangeRemoved
运行 在绑定视图时,并且由于用户仍在滚动而绑定了视图,因此我从 prepend/append 函数。
相反,我将剪辑过程作为一个独立的函数 (clipExtraDataIfNecessary
)。
这只会在 onScrollStateChanged(RecyclerView recyclerView, int newState)
中调用,并且只会在 newState == SCROLL_STATE_IDLE
时调用,即用户停止 滚动:
数据裁剪方法现在如下所示:
/**
* make sure we never surpass the MAX_DATASOURCE_CNT limit
* Clips the data ArrayList from the ending (right) side
*/
public void clipExtraEndData() {
int itemCountAfterPrepend = this.data.size();
// if we went above our max data source count limit
if (itemCountAfterPrepend > MAX_DATASOURCE_CNT) {
int extraItemCnt = itemCountAfterPrepend - MAX_DATASOURCE_CNT;
synchronized (dataMutationLock) {
// remove every extra item from the end of the array
removeRange(MAX_DATASOURCE_CNT, itemCountAfterPrepend);
// and notify our adapter about the removal
notifyItemRangeRemoved(MAX_DATASOURCE_CNT, extraItemCnt);
}
}
}
/**
* make sure we never surpass the MAX_DATASOURCE_CNT limit
* Clips the data ArrayList from the beginning (left) side
*/
public void clipExtraStartData() {
int itemCountAfterAppend = this.data.size();
// if we went above our max data-source count limit
if (itemCountAfterAppend > MAX_DATASOURCE_CNT) {
int extraItemCnt = itemCountAfterAppend - MAX_DATASOURCE_CNT;
synchronized (dataMutationLock) {
// remove every extra item from the beginning of the array
removeRange(0, extraItemCnt);
// and notify our adapter about the removal
notifyItemRangeRemoved(0, extraItemCnt);
}
}
}
/**
* Removes all the extra data that we have accumulated by prepending/appending
* as the user was scrolling
*
* CAUTION: MAKE SURE YOU RUN THIS at a time where the user is NOT scrolling,
* otherwise we run the risk of bindViewRow being called with preLayout position
* values (a.k.a 0)
* @param curVisiblePosition
*/
public void clipExtraDataIfNecessary(int curVisiblePosition) {
int initialItemCnt = size();
if (initialItemCnt > MAX_DATASOURCE_CNT) {
if (curVisiblePosition < initialItemCnt - curVisiblePosition) {
// we have extra children on the end of our data
clipExtraEndData();
} else {
// we have extra children on the beginning of our data
clipExtraStartData();
}
}
}
我正在使用 Recycler 视图构建无限水平滚动列表。 1-2 children 在任何时候都可以在屏幕上看到(因为它们的宽度大于屏幕视口,所以我们永远不会有超过 2 children 可见)。
初始状态:
当用户滚动时(他们可以向前和向后滚动 - 但为了这个例子的动摇,我们假设他们正在滚动 BACKWARDS),我正在获取更多数据,我将它们添加到我的适配器数据中。
然后我会打电话给 notifyItemRangeInserted
- 到目前为止一切顺利。
前置后:
剪辑时间:
同时我添加所有这些 children 我检查我们现在是否在数据源中得到 多于 MAX_DATASOURCE_CNT
项。
让我们假设 MAX_DATASOURCE_CNT
在这个例子中是 6。
如果我们这样做,我会从数据源的末尾删除不必要的额外 children,然后调用 notifyItemRangeRemoved
。
我这样做是因为我不想用未使用的数据淹没设备。我现在过度简化了示例,但实际上我的视图可以在用户滚动时累积数千个未使用的数据项,我想 trim 动态地 trim 我的数据源的另一端。
问题:
前置过程发生后,如果用户滚动速度太快,有时 onBindViewHolder(RecyclerView.ViewHolder holder, int position)
会被调用 position == 0
(也 holder.getAdapterPosition() == 0
)
这完全打乱了我的观点,就好像我的观点跳到了一个完全不相关的 child 并显示了那个。
当前解决方法:
我注意到,如果我删除基本上是这些代码行的整个剪辑过程:
if (itemCountAfterPrepend > MAX_DATASOURCE_CNT) {
int extraItemCnt = itemCountAfterPrepend - MAX_DATASOURCE_CNT;
// remove every extra item from the end of the array
removeRange(MAX_DATASOURCE_CNT, itemCountAfterPrepend);
// and notify our adapter about the removal
notifyItemRangeRemoved(MAX_DATASOURCE_CNT, extraItemCnt);
}
一切正常。但是我真的很想 trim 我的数据。 我能做什么?
谢谢
好的,我没有找到具体的答案,所以这就是我所做的。
由于问题是 notifyItemRangeRemoved
运行 在绑定视图时,并且由于用户仍在滚动而绑定了视图,因此我从 prepend/append 函数。
相反,我将剪辑过程作为一个独立的函数 (clipExtraDataIfNecessary
)。
这只会在 onScrollStateChanged(RecyclerView recyclerView, int newState)
中调用,并且只会在 newState == SCROLL_STATE_IDLE
时调用,即用户停止 滚动:
数据裁剪方法现在如下所示:
/**
* make sure we never surpass the MAX_DATASOURCE_CNT limit
* Clips the data ArrayList from the ending (right) side
*/
public void clipExtraEndData() {
int itemCountAfterPrepend = this.data.size();
// if we went above our max data source count limit
if (itemCountAfterPrepend > MAX_DATASOURCE_CNT) {
int extraItemCnt = itemCountAfterPrepend - MAX_DATASOURCE_CNT;
synchronized (dataMutationLock) {
// remove every extra item from the end of the array
removeRange(MAX_DATASOURCE_CNT, itemCountAfterPrepend);
// and notify our adapter about the removal
notifyItemRangeRemoved(MAX_DATASOURCE_CNT, extraItemCnt);
}
}
}
/**
* make sure we never surpass the MAX_DATASOURCE_CNT limit
* Clips the data ArrayList from the beginning (left) side
*/
public void clipExtraStartData() {
int itemCountAfterAppend = this.data.size();
// if we went above our max data-source count limit
if (itemCountAfterAppend > MAX_DATASOURCE_CNT) {
int extraItemCnt = itemCountAfterAppend - MAX_DATASOURCE_CNT;
synchronized (dataMutationLock) {
// remove every extra item from the beginning of the array
removeRange(0, extraItemCnt);
// and notify our adapter about the removal
notifyItemRangeRemoved(0, extraItemCnt);
}
}
}
/**
* Removes all the extra data that we have accumulated by prepending/appending
* as the user was scrolling
*
* CAUTION: MAKE SURE YOU RUN THIS at a time where the user is NOT scrolling,
* otherwise we run the risk of bindViewRow being called with preLayout position
* values (a.k.a 0)
* @param curVisiblePosition
*/
public void clipExtraDataIfNecessary(int curVisiblePosition) {
int initialItemCnt = size();
if (initialItemCnt > MAX_DATASOURCE_CNT) {
if (curVisiblePosition < initialItemCnt - curVisiblePosition) {
// we have extra children on the end of our data
clipExtraEndData();
} else {
// we have extra children on the beginning of our data
clipExtraStartData();
}
}
}