Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView recalculate WRAP_CONTENT

With version 23.2 we can now use WRAP_CONTENT for recyclerView height, which is great. I am doing this, however I want to recalculate the height after an item is added (or removed) to the adapter (thus increasing or decreasing the height).

My particular RecyclerView is starting with 1 item, and then adding items as the user makes selection. So I need the RecyclerView layout to increase in height, up to a point. Ideally this would happen with a smooth animation when the list increases or decreases.

How can we make it WRAP_CONTENT after it has been laid out?

Tried:

recyclerview.requestLayout();

recyclerview.invalidate();
like image 363
Patrick Avatar asked Mar 15 '16 19:03

Patrick


4 Answers

I would expect it to work with View.invalidate().

If that does not work, try to call either requestLayout or invalidate on the parent view.

like image 190
F43nd1r Avatar answered Oct 18 '22 23:10

F43nd1r


How RecyclerView Resize itself based on with the new LayoutManger

The RecyclerView widget provides an advanced and flexible base for creating lists and grids as well as supporting animations. This release brings an exciting new feature to the LayoutManager API: auto-measurement! This allows a RecyclerView to size itself based on the size of its contents. This means that previously unavailable scenarios, such as using WRAP_CONTENT for a dimension of the RecyclerView, are now possible. You’ll find all built in LayoutManagers now support auto-measurement.

Due to this change, make sure to double check the layout parameters of your item views: previously ignored layout parameters (such as MATCH_PARENT in the scroll direction) will now be fully respected.

If you have a custom LayoutManager that does not extend one of the built in LayoutManagers, this is an opt-in API - you’ll be required to call setAutoMeasureEnabled(true) as well as make some minor changes as detailed in the Javadoc of the method.

Note that although RecyclerView animates its children, it does not animate its own bounds changes. If you would like to animate the RecyclerView bounds as they change, you can use the Transition APIs.

Please read this

like image 43
Carlos Avatar answered Oct 19 '22 01:10

Carlos


Option I

Did you see this answer?

It's not using the recyclerView's WRAP_CONTENT, but it might work.

You can also create your own custom recyclerView (extends RecyclerView) and override the onMeasure() method there instead using the layoutManager in the link.

Option II

Try to set the layout params before drawing the view. I haven't checked if it is called when recyclerView layout changes, but if it does, then it will work. Something like this (in your Activity/Fragment onCreate()/onCreateView() method:

recyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() { 
                recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);

                YourParentLayoutType.LayoutParams params = (YourParentLayoutType.LayoutParams) recyclerView.getLayoutParams();
                params.height = YourParentLayoutType.LayoutParams.WRAP_CONTENT;
                recyclerView.setLayoutParams(params);

                return true;
            }
        });

Use your recyclerView's parent layout type instead of YourParentLayoutType in the code. I'm not sure this will work when layout refreshes, but maybe worth a try.

like image 23
e-shfiyut Avatar answered Oct 19 '22 01:10

e-shfiyut


Use this class:

Please use 23.2.1 as 23.2 was way buggy.

Also have you tried to call notifyDataSetChanged on the recyclerview adapter , as far as i think it should expand without problem if you have given wrap_content as height of recyclerview

else u can use this class:

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

public class MyLinearLayoutManager extends LinearLayoutManager {

    public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout)    {
        super(context, orientation, reverseLayout);

    }



    private int[] mMeasuredDimension = new int[2];


    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                          int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);
        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);



        int width = 0;
        int height = 0;
        for (int i = 0; i < getItemCount(); i++) {
            if (getOrientation() == HORIZONTAL) {
                measureScrapChild(recycler, i,
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                        heightSpec,
                        mMeasuredDimension);

                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                measureScrapChild(recycler, i,
                        widthSpec,
                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                        mMeasuredDimension);

                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }


        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        int widthDesired = Math.min(widthSize,width);
        setMeasuredDimension(widthDesired, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                   int heightSpec, int[] measuredDimension) {
        View view = recycler.getViewForPosition(position);
        // For adding Item Decor Insets to view
        super.measureChildWithMargins(view, 0, 0);
        RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
        int childWidthSpec = ViewGroup.getChildMeasureSpec(
                widthSpec,
                getPaddingLeft() + getPaddingRight() + getDecoratedLeft(view) + getDecoratedRight(view),
                p.width);
        int childHeightSpec = ViewGroup.getChildMeasureSpec(
                heightSpec,
                getPaddingTop() + getPaddingBottom() + getDecoratedTop(view) + getDecoratedBottom(view) ,
                p.height);
        view.measure(childWidthSpec, childHeightSpec);

        // Get decorated measurements
        measuredDimension[0] = getDecoratedMeasuredWidth(view) + p.leftMargin + p.rightMargin;
        measuredDimension[1] = getDecoratedMeasuredHeight(view) + p.bottomMargin + p.topMargin;
        recycler.recycleView(view);
        }
    }
like image 34
JAAD Avatar answered Oct 18 '22 23:10

JAAD