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:

enter image description here


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
        }
     }
})