just_user
just_user

Reputation: 12059

Full width footer or last item in RecyclerView using StaggeredGridLayout

So this is pretty tricky. The closest thing I have found to get this working is a modified version of this answer. Where I make the last item the same height as the view I want to be full width and invisible. Then I add an itemdecoration and draw it where the last item in the list should be visible like this:

class StickyDecoration (private val stickyView: View) : ItemDecoration() {

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)

        // layout basically just gets drawn on the reserved space on top of the first view
        stickyView.layout(parent.left, 0, parent.right, stickyView.measuredHeight)
        for (i in 0 until parent.childCount) {
            val view: View = parent.getChildAt(i)
            if (parent.getChildAdapterPosition(view) == parent.childCount - 1) {
                c.save()

                val rectf = Rect()
                view.getGlobalVisibleRect(rectf)
                c.translate(0f, (rectf.top).toFloat())

                stickyView.draw(c)
                c.restore()
                break
            }
        }
    }

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        outRect.setEmpty()
    }
}

The problem with this approach is when items in the recyclerview above the last one disappears from view, then the value of rectf.top or view.top or any measurement I can think of make a leap and displaces the last item.

Does anyone know of a way to add a full width last item or footer to a recyclerview with a StaggeredGridLayoutManager? I know it works with Gridlayout but its not an option in my case.

Or if you know of a value which gets to correct position of the last item on the screen without any jump in values?

EDIT:

More details of what I'd like to achieve.

enter image description here

It should be a StaggeredGridLayout with a footer below the last item, which is full width and scrolls with the rest of the content.

Upvotes: 1

Views: 880

Answers (3)

Doilio Matsinhe
Doilio Matsinhe

Reputation: 2280

Use isFullSpan in the onBindViewHolder .

override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
        holder.bind(loadState)
        val layoutParams = holder.itemView.layoutParams as StaggeredGridLayoutManager.LayoutParams
        layoutParams.isFullSpan = true
    }

Upvotes: 1

Rajesh Satvara
Rajesh Satvara

Reputation: 3964

Into adapter you can add condition for footer UI like this :

@Override
    public int getItemViewType(int position) {
        if (// normal UI) {
            return VIEW_TYPE_NORMAL;
        } else {
            return VIEW_TYPE_FOOTER;
        }
    }

And into onBindViewHolder you can add like this for VIEW_TYPE_FOOTER.

StaggeredGridLayoutManager.LayoutParams layoutParams =
                        (StaggeredGridLayoutManager.LayoutParams) ((YourViewHolder) holderType).itemView.getLayoutParams();
                /*
                 * to make View to occupy full width of the parent
                 */
                layoutParams.setFullSpan(true);

Upvotes: 1

just_user
just_user

Reputation: 12059

To make a full width footer in a recyclerView using StaggeredGridLayoutManager you can do the following. Create a custom ItemDecoration:

class FooterDecoration (private val footerView: View) : ItemDecoration() {

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)

        footerView.layout(parent.left, 0, parent.right, footerView.measuredHeight)
        val count = parent.adapter!!.itemCount

        val view: View = parent.getChildAt(parent.childCount - 1)
        if (parent.getChildAdapterPosition(view) == count - 1) {
            c.save()

            val rect = Rect()
            view.getGlobalVisibleRect(rect)
            c.translate(0f, (rect.top).toFloat())

            footerView.draw(c)
            c.restore()
        }
    }
}

Then in your recyclerView inflate the view you want to use and add it as an item decoration:

recyclerView.apply {
    val footerView: View = LayoutInflater.from(context).inflate(R.layout.item_footer_placeholder, this, false)
    val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    footerView.measure(measureSpec, measureSpec)
    addItemDecoration(ServicesFooterDecoration(footerView))

}

Upvotes: 0

Related Questions