Home > Enterprise >  Set item opacity in RecyclerView depending if item is in center
Set item opacity in RecyclerView depending if item is in center

Time:08-29

I want to display items in a horizontal list using RecyclerView. At a time, only 3 items will be displayed. 1 in the middle and the other 2 on the side, below is an image of what I'm trying to achieve:

enter image description here

I'm using LinearSnapHelper which centers an item all of the time. When an item is moved away from the center I would like the opacity to progessively change from 1f to 0.5f.

Here is the below code which I've written to help:

class CustomRecyclerView(context: Context, attrs: AttributeSet) : RecyclerView(context, attrs) {
    
    private var itemBoundsRect: Rect? = null

    init {
        itemBoundsRect = Rect()
        addOnScrollListener(object : OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                calculateVisibility()
            }
        })
    }

    private fun calculateVisibility() {
        val linearLayoutManger: LinearLayoutManager = layoutManager as LinearLayoutManager
        val firstVisibleItem = linearLayoutManger.findFirstVisibleItemPosition()
        val lastVisibleItem = linearLayoutManger.findLastVisibleItemPosition()
        var indexes: MutableList<Int> = mutableListOf()
        for (i in firstVisibleItem..lastVisibleItem) {
            indexes.add(i)
            val item: View = layoutManager?.findViewByPosition(i) ?: continue
            item.getGlobalVisibleRect(itemBoundsRect)
            var itemSize = layoutManager!!.findViewByPosition(i)!!.width
            
            var visibleSize = 0
            if (indexes.size == 1) {
                visibleSize = itemBoundsRect!!.right
            } else {
                visibleSize = itemBoundsRect!!.right - itemBoundsRect!!.left
            }



            var visibilty = visibleSize * 100 / itemSize
            if (visibilty > 0) {
                visibilty = 100 - visibilty
            }

            val viewHolder = findViewHolderForLayoutPosition(i)
            viewHolder!!.itemView.alpha = (100 - visibilty).toFloat() / 100f
        }
    }
}

It doesn't work as expected as the opacity changes at the wrong time. The image below demonstrates this better. I expect the opacity to progressively begin to change when the item edges come out of the red box. However, it only starts when the item reaches the yellow edges.

enter image description here

Is there a way to achieve this effect?

Thank you :)

CodePudding user response:

Your code for calculateVisibility() is looking at global position when looking at the relative position within the RecyclerView is sufficient. Maybe there is more to the code than you posted, but try the following. This code looks at the x position of each visible view and calculates the alpha value as a function of displacement from the center of the RecyclerView. Comments are in the code.

private fun calculateVisibility(recycler: RecyclerView) {
    val midRecycler = recycler.width / 2
    val linearLayoutManger: LinearLayoutManager = recycler.layoutManager as LinearLayoutManager
    val firstVisibleItem = linearLayoutManger.findFirstVisibleItemPosition()
    val lastVisibleItem = linearLayoutManger.findLastVisibleItemPosition()
    for (i in firstVisibleItem..lastVisibleItem) {
        val viewHolder = recycler.findViewHolderForLayoutPosition(i)
        viewHolder?.itemView?.apply {
            // This is the end of the view in the parent's coordinates
            val viewEnd = x   width
            // This is the maximum pixels the view can slide left or right until it disappears.
            val maxSlide = (midRecycler   width / 2).toFloat()
            // Alpha is determined by the percentage of the maximum slide the view has moved.
            // This assumes a linear fade but can be adjusted to fade in alternate ways.
            alpha = 1f - abs(maxSlide - viewEnd) / maxSlide
            Log.d("Applog", String.format("pos=%d alpha=%f", i, alpha))
        }
    }
}

The foregoing assumes that sizes remain constant.

CodePudding user response:

if you need the center View, you can call

 View view =  snapHelper.findSnapView(layoutManagaer);

once you have the View, you should be able to get the position on the dataset for that View. For instance using

int pos = adapter.getChildAdapterPosition(view)

And then you can update the center View opacity and invoke

adapter.notifyItemChanged(pos);
  • Related