Calculating the height of each row of a ListView

I am trying to make ListView scroll background using ListView. I base my approach on this class on Shelves , but while in Shelves everything is the same height, I cannot make the same guarantee.

I have this activity:

public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ListView listView = (ListView) findViewById(R.id.listView); List<String> items = new ArrayList<String>(); for(int i=0; i < 100; ++i) { items.add("Hello " + i); } CustomArrayAdapter adapter = new CustomArrayAdapter(this, items); listView.setAdapter(adapter); } } 

Where is the CustomArrayAdapter:

 public class CustomArrayAdapter extends ArrayAdapter<String> { private List<Integer> mHeights; public View getView(int position, View convertView, ViewGroup parent) { //snip } } 

What I want to do is fill mHeights in the adapter with the line height of the view.

My main attempt was to do this in getView ():

 if(row.getMeasuredHeight() == 0) { row.measure( MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY)); } mHeights.set(position, row.getMeasuredHeight()); 

I tried several ways to do this, but I can't get anything to work.

In getView , if I call getHeight() , I get a return value of 0, and if I call getMeasuredHeight() , I get something non-zero, but not real height. If I scroll down and then again (taking one of the lines out of view) getHeight() == getMeasuredHeight() , and both have the correct value. If I just update mHeights in getView along the way (saying mHeights.set(position, row.getHeight() ); it works, but only after scrolling to the bottom of the list. I tried calling row.measure () in the getView method, but it all the same, getHeight() will be 0 on first run.

My question is this: how to calculate and populate mHeight with the correct ListView row height? I saw some similar questions, but they don't seem to be what I'm looking for. The ViewTreeObserver method seems promising, but I can't figure out how to force getView () calls to be invoked (alternately to iterate through ListView strings). The adapter.notifyDataSetChanged() call causes an infinite (albeit non-blocking) loop.

This is related to my previous question on this issue, which seems to have missed the point: ListView distance from the top of the list

+6
source share
2 answers

I figured out how to do it. It was a bit complicated, but it works.

Refinement got the layout options when they were known. As I found out, getView() returns the view at the initial time, so of course it cannot yet have a height - the parent does not yet know about the line (since the getView() task must inflate it), so we need a callback.

What could be this callback? It turns out as far as I can tell it is OnLayoutChangeListener() to represent the string. So, in getView() in my custom ArrayAdapter , I added a callback for onLayoutChanged as follows:

 row.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { ref.removeOnLayoutChangeListener(this); Log.d("CustomArrayAdapter", "onLayoutChange(" + position + "): height " + (bottom - top)); mHeights.set(position, (bottom - top)); if(position > 0) { mDistances.set(position, bottom - top + mDistances.get(position-1) + ((ListView)parent).getDividerHeight()); } holderRef.distanceFromTop = mDistances.get(position); Log.d("CustomArrayAdapter", "New height for " + position + " is " + mHeights.get(position) + " Distance: " + mDistances.get(position)); } }); 

Here row is the view that getView() will return in the adapter, mHeights is the list of heights of each ListView element, and mDistances is the list of distances from the top of the list in pixels - the real thing I wanted to calculate (i.e. if you put the whole list from end to end, how far from the top element I will be on top). All of them are stored in a custom ArrayAdapter . These lists are initialized to zeros.

Adding this callback allowed me to calculate the height of the view.

My goal was to have a ListView that has a background that scrolls with the list. If anyone is interested, I put the code on GitHub, if someone wants to view it: https://github.com/mdkess/TexturedListView

+12
source

This SO answer should work for developers who also want to support API level <11.

Basically, instead of adding the View.onLayoutChnageListener to your view, you can set OnGlobalLayoutListener in your ViewTreeObserver .

+2
source

All Articles