android – How to get the height of recyclerview item in "onBindViewHolder"-ThrowExceptions

Exception or error:

The height of RecyclerView item is not fixed,i need to set the background image for every item,so I want to get the height of recyclerview’s item to resize the Image,but the itemView.getHeight() always return 0 in onBindViewHolder.

I have try to search many questions or articles,but i still cant get a good soluation.

How to solve:

Short

Measure the View manually

view.measure(
             View.MeasureSpec.makeMeasureSpec(recyclerViewWidth, View.MeasureSpec.EXACTLY),
             View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

and get the height with view.getMeasuredHeight()

Background

A view will return a valid value for View.getHeight()only after it has been measured. The measuring itself will automatically happen by the system when the view is about to be displayed on screen.

When Android wants to display the layout, it will recursively call the view.layout() function for each view in the view tree. Each Parent tells its children the constraints they might have (width/height) and ask them to view.measure() themselves. As a result, the view will store the measured values BASED on the constraints in designated members (MeasuredHeight/Width). Note that at this point view.getMeasuredHeight() will hold the value while view.getHeight() will still be invalid. view.getHeight() will only return a valid value once the view has an actual height in the UI hierarchy.

Recap

So, to know the height of a view element, before it has been measured and laid out by the system, we will need to invoke the view.measure() function manually.

The measure function expects 2 parameters which derived from the view LayoutParams + the parent constraints.

In the above code sample, we are measuring the view forcing its width to be EXACTLY the width of the parent (the RecycleView), and the height is not limited.

###

I suggest that you define multiple layout files with the expected heights and inflate them according to some criteria in your data set.

ViewHolder onCreateViewHolder (ViewGroup parent, int viewType){
  if(some condition){
    //inflate layout 1
  }else{
    //inflate layout 2
 }

or as answered here: you can get the measurements while initializing the view holder

itemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
int width = itemView.getMeasuredWidth();
int height = itemView.getMeasuredHeight();

###

How about this:

view.post(() -> {
   int width = view.getMeasuredWidth();
   int height = view.getMeasuredHeight();
}

###

using this code to get recycler view’s item height:

view.getViewTreeObserver().addOnGlobalLayoutListener(new 
         ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        //don't forget remove this listener
        view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        //get item height here
        int itemHeight = v.getHeight();
    }
});

###

MyViewHolder.kt

With the addOnGlobalLayoutListener() method, the height value is obtained before the TextView is drawn. And then save it in a member variable.

The key is to modify the UI inside and outside the implementation of the listener so that there are no rendering problems (when the views are redrawn).

That is, you shoud use the getter inside the listener and; the setter inside and outside the listener.

companion object {

    var maxHeight: Int = 0

    fun create(mContext: Context): MyViewHolder {
        val view = LayoutInflater.from(mContext).inflate(R.layout.item_answer, null)

        updateLayout(view)

        view.viewTreeObserver.addOnGlobalLayoutListener {

            if (view.myTextView.height > maxHeight)
                maxHeight = view.myTextView.height

            updateLayout(view)

        }

        return MyViewHolder(mContext, view).apply {
            setIsRecyclable(false)
        }
    }

    fun updateLayout(view: View) {
        if (maxHeight != 0 && view.myTextView.height != maxHeight) 
            view.myTextView.height = maxHeight
    }


}

GL

Source

Leave a Reply

Your email address will not be published. Required fields are marked *