Get height for RecyclerView child views at runtime

13.4k views Asked by At

I'm trying to make an expandable TextView that expands/collapses when a user presses a button. The TextView and ImageButton are in a CardView, which is added to a RecyclerView.

The expand/collapse work nicely, but now I want to add the ImageButton only when the TextView height exceeds a certain maximum height. But when I evaluate the height in the LayoutManager's addView method, it returns 0, probably because the TextView does not have any text yet.

Is there any callback method that I can override that is fired after the CardView gets it's new content, so that I can evaluate the height of the TextView?

Edit: I've tried onLayoutChildren as yigit suggested

@Override
public void onLayoutChildren (RecyclerView.Recycler recycler, RecyclerView.State state) {
    super.onLayoutChildren(recycler, state);
    Log.d("LayoutManager","State Count: " + state.getItemCount());

    for (int i = 1; i < state.getItemCount(); i++) {
        View v = recycler.getViewForPosition(i);
        TextView ct = (TextView) v.findViewById(R.id.comment_text);
        Log.d("LayoutManager", "Comment height: " + ct.getMeasuredHeight());
    }
}

The resulting output:

D/LayoutManager﹕ State Count: 4
D/LayoutManager﹕ Comment height: 0
D/LayoutManager﹕ Comment height: 0
D/LayoutManager﹕ Comment height: 0
2

There are 2 answers

2
yigit On

You can override onLayoutChildren in the LayoutManager and measure it if state.isPreLayout return false. I'm hoping that you are not changing the total size of the View because LayoutManager will not see this change. Also because layout requests are disabled at that stage, your view will not get a new measure and layout.

If it resizes your view, do it in onBind and measure the textview yourself to decide whether you want to enable the button or not.

1
Stepan Maksymov On

LinearLayout.LayoutParams - this could be RelativeLayout.LayoutParams according to yours main layout

mParentContainer - this is RecyclerView - passed to yours adapter

Utils.dpToPx(8+15); - here addition of margins cause they are not counted on measure

totalHeight += 1; - is to cause adapter to bind next view holder

in ViewHolder:

int totalHeight = 0;
for (int i = 0; i < getItemCount(); i++){

    View chView = viewHolder . itemView;
    chView.measure(
        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    );

    totalHeight += chView.getMeasuredHeight();
    totalHeight += Utils.dpToPx(8 + 15);
}

totalHeight += 1;
LinearLayout.LayoutParams listParams = (LinearLayout.LayoutParams) mParentContainer . getLayoutParams ();
listParams.height = totalHeight;
mParentContainer.setLayoutParams(listParams);