How to find percentage of each visible item in recycleview
You need to write addOnScrollListener()
for recyclerview
and inside that write following code where you will get position of firstVisibleItem
and then you can get dimension of that item on each scroll. Just save previous dimension in global variable and compare it with the current dimension and calculate percentage on every scroll change. When you get your desired percentage then perform your operations.
int position = linearLayoutManager.findFirstVisibleItemPosition();
Rect rect = new Rect();
linearLayoutManager.findViewByPosition(position).getGlobalVisibleRect(rect);
Here is an improved (more effective, less memory usage, less code, updates ViewHolder
with the current extent of its visibility) Kotlin
version of Vishal Sanghani's answer:
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
val layoutManager = recycler.layoutManager as LinearLayoutManager
val firstPosition = layoutManager.findFirstVisibleItemPosition()
val lastPosition = layoutManager.findLastVisibleItemPosition()
val globalVisibleRect = Rect()
val itemVisibleRect = Rect()
recycler.getGlobalVisibleRect(globalVisibleRect)
for (pos in firstPosition..lastPosition) {
val view = layoutManager.findViewByPosition(pos)
if (view != null && view.height > 0 && view.getGlobalVisibleRect(itemVisibleRect)) {
val visibilityExtent = if (itemVisibleRect.bottom >= globalVisibleRect.bottom) {
val visibleHeight = globalVisibleRect.bottom - itemVisibleRect.top
Math.min(visibleHeight.toFloat() / view.height, 1f)
} else {
val visibleHeight = itemVisibleRect.bottom - globalVisibleRect.top
Math.min(visibleHeight.toFloat() / view.height, 1f)
}
val viewHolder = recycler.findViewHolderForAdapterPosition(pos) as ViewHolder
viewHolder.setVisibilityExtent(visibilityExtent)
// if percentage is needed...
val percentage = visibilityExtent * 100
}
}
}
})
Override the ScrollChangedListener of recyclerview and add below code in onScollerdChanged, to get the visibility of your row.
if (rvPercentage != null){
LinearLayoutManager layoutManager = ((LinearLayoutManager) rvPercentage.getLayoutManager());
final int firstPosition = layoutManager.findFirstVisibleItemPosition();
final int lastPosition = layoutManager.findLastVisibleItemPosition();
Rect rvRect = new Rect();
rvPercentage.getGlobalVisibleRect(rvRect);
for (int i = firstPosition; i <= lastPosition; i++) {
Rect rowRect = new Rect();
layoutManager.findViewByPosition(i).getGlobalVisibleRect(rowRect);
int percentFirst;
if (rowRect.bottom >= rvRect.bottom){
int visibleHeightFirst =rvRect.bottom - rowRect.top;
percentFirst = (visibleHeightFirst * 100) / layoutManager.findViewByPosition(i).getHeight();
}else {
int visibleHeightFirst = rowRect.bottom - rvRect.top;
percentFirst = (visibleHeightFirst * 100) / layoutManager.findViewByPosition(i).getHeight();
}
if (percentFirst>100)
percentFirst = 100;
mData.get(i).setPercentage(percentFirst);
mAdapter.notifyItemChanged(i);
}
}
It gives you below output:
Here is more improved and less complex (more effective, less memory usage, less code) in kotlin.
No need do compare item rect with recycler view rect
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
val layoutManager = recycler.layoutManager as LinearLayoutManager
val firstPosition = layoutManager.findFirstVisibleItemPosition()
val lastPosition = layoutManager.findLastVisibleItemPosition()
val globalVisibleRect = Rect()
recycler.getGlobalVisibleRect(globalVisibleRect)
for (pos in firstPosition..lastPosition) {
val view = layoutManager.findViewByPosition(pos)
if (view != null) {
val percentage = getVisibleHeightPercentage(view)
}
}
}
//Method to calculate how much of the view is visible
private fun getVisibleHeightPercentage(view: View): Double {
val itemRect = Rect()
val isParentViewEmpty = view.getLocalVisibleRect(itemRect)
// Find the height of the item.
val visibleHeight = itemRect.height().toDouble()
val height = view.getMeasuredHeight()
val viewVisibleHeightPercentage = visibleHeight / height * 100
if(isParentViewEmpty){
return viewVisibleHeightPercentage
}else{
return 0.0
}
}
})