William
William

Reputation: 171

CursorAdapter calling bindView on every item

I was using some of the code from DeskClock and found that when I tried to modify the CursorAdapter that newView was getting called for each item. (I actually wanted to add a divider and it seems like adding the dividers separately is better than adding them into a single listView)

If I have 3 items I get the following behavior:

 newView - called with item: 0
  bindView - called with item: 0
  bindView - called with item: 1
  bindView - called with item: 2
  bindView - called with item: 0
 newView - called with item: 1
  bindView - called with item: 1
 newView - called with item: 2
  bindView - called with item: 2
 newView - called with item: 0
  bindView - called with item: 0
  bindView - called with item: 1
  bindView - called with item: 2

I would've expected newView/bindView to be called once per cursor item. But, that doesn't seem to be the case. This was kind of a problem for me because I wanted to select the appropriate view depending on the cursor data, but since bindView could be called before newView that doesn't work.

Is this some odd recycler behavior (or somehow normal/expected)? Or is there something broken with the code (I removed everything except the ListView and it's still doing this)? I'm not quite sure why you would try to bind views that haven't been created, and why newView gets called on the first item twice.

Thanks!

Btw, if someone has an easy way of adding dividers to ListViews I'd love to know. I was going to try to dig through the contacts example to see how they did it there if not.


Btw, in case anyone's wondering MergeAdapter was neat to mess around with and try (I'll use it in some other situations). But, I did just end up writing a single adapter that handles multiple views. It computes the type of each item (which is small), stores it in a map, initializes it at adapter creation, and updates it in notifyDataSetChanged.

Then you just need to have getViewTypeCount() return the number of possible views. And getItemViewType to return the type from the map (which is 0 based, so 0-getViewTypeCount()). If you can compute your type from the position you don't need the map, but doing this on the fly wasn't possible so I just pre/recompute when needed.

Upvotes: 1

Views: 5522

Answers (3)

gokhansari
gokhansari

Reputation: 2439

Use getPosition method of the Cursor to see your item position.

cursor.getPosition();

Upvotes: 0

Matt Wolfe
Matt Wolfe

Reputation: 9284

I had the same problem where bindView was getting called multiple times (3 to be exact) for each entry in my cursor.. The fix I found was to switch my layout to a Relative Layout like so:

  <?xml version="1.0" encoding="utf-8"?>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    >
     <ListView
       android:id="@android:id/list"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:background="#666666"
       android:layout_weight="1"
       android:drawSelectorOnTop="false"
       android:layout_alignParentTop="true"
       />
    <TextView android:id="@android:id/empty"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
               android:background="@drawable/shape_gradient_2"
               android:text="No Evidence Available"               
               android:padding="10dp"
               android:layout_below="@android:id/list"

               />
  </RelativeLayout>

Previously I had been using a LinearLayout and could not get it to work. The only way that I would get bindView called once per row with LinearLayout was to set a fixed height on the ListView. Since pretty much any LinearLayout could be recreated with a RelativeLayout you should be able to just switch the two and then just tell the ui where to place each element. I still don't understand why we have to do this and I spent the better part of a day figuring out the problem, but at least now it's been solved. I'm really surprised more people haven't seen the issue.

Upvotes: 3

CommonsWare
CommonsWare

Reputation: 1006819

This was kind of a problem for me because I wanted to select the appropriate view depending on the cursor data, but since bindView could be called before newView that doesn't work.

Did you override getViewTypeCount() and getItemViewType()?

Is this some odd recycler behavior (or somehow normal/expected)? Or is there something broken with the code (I removed everything except the ListView and it's still doing this)?

Something definitely seems strange.

Btw, if someone has an easy way of adding dividers to ListViews I'd love to know. I was going to try to dig through the contacts example to see how they did it there if not.

In the end, how one adds dividers to ListViews depends almost entirely on what the criteria are for when dividers should appear. It is impossible to supply a one-size-fits-all solution.

For example, my MergeAdapter can be used for dividers, where each chunk of stuff between the dividers can be contained in its own ListAdapter. That works great for some scenarios. However, let's say that the divider is really determined by a column (e.g., category) out of a database query. Running lots of sub-queries per category to wrap each Cursor in its own SimpleCursorAdapter for use by MergeAdapter would be painful. Better would be to create a different sort of wrapping Adapter that can insert headings on the fly based on detected changes in the category value. I don't have a sample of such an Adapter on hand, though it is on my 18,000-item to-do list...

Upvotes: 2

Related Questions