【发布时间】:2014-09-22 10:58:49
【问题描述】:
我需要知道我的 RecyclerView 中当前显示了哪些元素。 ListViews 上没有与OnScrollListener.onScroll(...) 方法等效的方法。我尝试与 View.getGlobalVisibleRect(...) 合作,但这种 hack 太丑陋了,而且并不总是有效。
有人有什么想法吗?
【问题讨论】:
标签: android android-recyclerview
我需要知道我的 RecyclerView 中当前显示了哪些元素。 ListViews 上没有与OnScrollListener.onScroll(...) 方法等效的方法。我尝试与 View.getGlobalVisibleRect(...) 合作,但这种 hack 太丑陋了,而且并不总是有效。
有人有什么想法吗?
【问题讨论】:
标签: android android-recyclerview
第一个/最后一个可见的孩子取决于LayoutManager。
如果你使用LinearLayoutManager或GridLayoutManager,你可以使用
int findFirstVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();
例如:
GridLayoutManager layoutManager = ((GridLayoutManager)mRecyclerView.getLayoutManager());
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
对于LinearLayoutManager,第一个/最后一个取决于适配器顺序。不要从RecyclerView 查询孩子; LayoutManager 可能更喜欢布局比可见的缓存更多的项目。
【讨论】:
getItemCount 可以在同一个调用中返回 1 和 findFirstVisibleItemPosition -1。
对于那些在 RecyclerView 适配器中实现逻辑的人,您仍然可以使用 @ernesto 方法与 on scrollListener 结合来获取 what you want,因为咨询了 RecyclerView。
在适配器内部,您将拥有如下内容:
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if(manager instanceof LinearLayoutManager && getItemCount() > 0) {
LinearLayoutManager llm = (LinearLayoutManager) manager;
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visiblePosition = llm.findFirstCompletelyVisibleItemPosition();
if(visiblePosition > -1) {
View v = llm.findViewByPosition(visiblePosition);
//do something
v.setBackgroundColor(Color.parseColor("#777777"));
}
}
});
}
}
【讨论】:
llm 变量不需要是最终的,以便匿名类“认识”他吗?
llm,而是为了确保何时调用回调,llm 的值与实例化匿名类以避免不一致时相同,如果我错了,请纠正我llm 的值从未改变过,当我发布我的答案时,我没有收到任何警告或编译器错误。如果我错了或遗漏了什么,请告诉我
最后,我从适配器中的 onBindViewHolder 事件中找到了知道当前项是否可见的解决方案。
关键是LayoutManager的方法isViewPartiallyVisible。
在您的适配器中,您可以从 RecyclerView 中获取 LayoutManager,它是您从 onAttachedToRecyclerView 事件中获取的参数。
【讨论】:
您可以使用recyclerView.getChildAt() 来获取每个可见的孩子,并在适配器代码中的这些视图上设置一些标签convertview.setTag(index) 将帮助您将其与适配器数据相关联。
【讨论】:
recyclerView.getChildAt() 获得可见项目,这就是RecyclerView 的一般工作方式。 RecyclerView 将尝试仅保留少数当前可见的子视图(即;在屏幕范围内,而不是 Visibility as GONE、INVISIBLE),并在用户滚动时尝试回收这些视图。
附录:
建议的函数 findLast...Position() 确实不在工具栏展开而工具栏折叠的场景中正常工作。
似乎回收器视图具有固定的高度,并且在工具栏展开时,回收器向下移动,部分移出屏幕。因此,建议的函数的结果太高了。示例:最后一个可见项目被告知是 #9,但实际上项目 #7 是屏幕上的最后一个。
这种行为也是我的视图经常无法滚动到正确位置的原因,即 scrollToPosition() 无法正常工作(我最终以编程方式折叠了工具栏)。
【讨论】:
以下Linear / Grid LayoutManager方法可用于检查哪些项目是visible
int findFirstVisibleItemPosition();
int findLastVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();
如果您想跟踪is item visible on screen 的某个阈值,那么您可以参考以下博客。
https://proandroiddev.com/detecting-list-items-perceived-by-user-8f164dfb1d05
【讨论】:
对于StaggeredGridLayoutManager,请执行以下操作:
RecyclerView rv = findViewById(...);
StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(...);
rv.setLayoutManager(lm);
并获得可见的项目视图:
int[] viewsIds = lm.findFirstCompletelyVisibleItemPositions(null);
ViewHolder firstViewHolder = rvPlantios.findViewHolderForLayoutPosition(viewsIds[0]);
View itemView = viewHolder.itemView;
记得检查是否为空。
【讨论】:
viewsIds 是一个数组,可能为空。
上面的每个答案都是正确的,我还想从我的工作代码中添加一个快照。
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//some code when initially scrollState changes
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//Some code while the list is scrolling
LinearLayoutManager lManager = (LinearLayoutManager) recycler.getLayoutManager();
int firstElementPosition = lManager.findFirstVisibleItemPosition();
}
});
【讨论】:
您可以找到回收视图的第一个和最后一个可见子项,并检查您要查找的视图是否在范围内:
var visibleChild: View = rv.getChildAt(0)
val firstChild: Int = rv.getChildAdapterPosition(visibleChild)
visibleChild = rv.getChildAt(rv.childCount - 1)
val lastChild: Int = rv.getChildAdapterPosition(visibleChild)
println("first visible child is: $firstChild")
println("last visible child is: $lastChild")
【讨论】:
对于那些在 Kotlin 中寻找答案的人 -
fun getVisibleItem(recyclerView : RecyclerView) {
recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
if(newState == RecyclerView.SCROLL_STATE_IDLE) {
val index = (recyclerView.layoutManager.findFirstVisibleItemPosition
//use this index for any operation you want to perform on the item visible on screen. eg. log(arrayList[index])
}
}
})
}
您可以根据您的用例探索其他获取位置的方法。
int findFirstCompletelyVisibleItemPosition()
int findLastVisibleItemPosition()
int findLastCompletelyVisibleItemPosition()
【讨论】:
我希望下面的代码可以帮助某人定义int a 以上方法。如果在项目位置吐司消息之前可见项目位置不同,则会在屏幕上显示
myRecyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
LinearLayoutManager manager= (LinearLayoutManager) myRecyclerview.getLayoutManager();
assert manager != null;
int visiblePosition = manager.findLastCompletelyVisibleItemPosition();
if(visiblePosition > -1&&a!=visiblePosition) {
Toast.makeText(context,String.valueOf(visiblePosition),Toast.LENGTH_SHORT).show();
//do something
a=visiblePosition;
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//Some code while the list is scrolling
}
});
【讨论】: