I have a ListView
with a custom Adapter
that extends CursorAdapter
. The list is defined in such a layout:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<ProgressBar
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/ProgressBar.Itasa.Indeterminate.Large"/>
<net.italiansubs.droitasa.widget.HookedListView
android:id="@android:id/list"
style="@style/NewsList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
</RelativeLayout>
The HookedListView
looks like this:
package net.italiansubs.droitasa.widget;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.ListView;
import net.italiansubs.droitasa.util.OnLayoutChangedListener;
/**
* An extended ListView that has an hook for getting layout changes.
*
* @author Sebastiano Poggi, Francesco Pontillo
*/
public class HookedListView extends ListView {
private OnLayoutChangedListener mLayoutListener;
private final Handler mHandler;
private boolean mFirstLayout = true;
public HookedListView(Context context) {
super(context);
mHandler = new Handler(context.getMainLooper());
}
public HookedListView(Context context, AttributeSet attrs) {
super(context, attrs);
mHandler = new Handler(context.getMainLooper());
}
public HookedListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mHandler = new Handler(context.getMainLooper());
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mFirstLayout) {
// In this case we want to avoid posting, because this could
// have the two layout passes happen one after the other with
// a pause between each other, with the effects, for example,
// of having the number of columns change
mFirstLayout = false;
new LayoutChangedRunnable().run();
}
else if (changed) {
mHandler.postAtFrontOfQueue(new LayoutChangedRunnable());
}
}
/**
* Sets the OnLayoutChangedListener for this instance, that will
* receive callbacks after layout changes for the View.
*
* @param l The new OnLayoutChangedListener, or null to remove
* the current listener.
*/
public void setOnLayoutChangedListener(OnLayoutChangedListener l) {
mLayoutListener = l;
}
/**
* Gets the OnLayoutChangedListener set for this instance.
*
* @return Returns the instance's OnLayoutChangedListener, if any.
*/
public OnLayoutChangedListener getOnLayoutChangedListener() {
return mLayoutListener;
}
private class LayoutChangedRunnable implements Runnable {
@Override
public void run() {
// We don't need to synchronize on the listener because the handler
// is hooked up to the main thread's looper so we shouldn't be
// called from other threads.
if (mLayoutListener != null) {
mLayoutListener.onLayoutChanged(HookedListView.this);
}
}
}
}
The problem I'm having is that the ListView
contents don't appear on the device, even though the Cursor
has items, the Adapter
has items, and the ListView
itself has items. Attaching the HierarchyViewer to the window does make the ListView
items appear without any further intervention, and the same does requesting an update of the data from the webservice, which in turn updates the contents of the ContentProvider
. I've tried setting up content observers for both the Cursor
and the Adapter
, and logging to ensure the correct number of items is set to the Adapter
, and it's all working as intended. We already perform one extra layouting round on the List because of our custom MultiCoumnAdapter
, that calculates the number of columns after the first ListView
layout pass (thus the need for a HookedListView
).
I've tried using a standard CursorAdapter
, instead of my own MultiColumnAdapter
(which emulates a GridView
, without all its limitations and bugs), but this doesn't change anything.
The Fragment
is the only content of an Activity
; the Activity
itself basically does nothing but getting an item ID from the extras of the Intent
used to launch it, and passes it to the Fragment
, which is defined in the Activity
's layout XML as the only element.
What the Fragment
does is just handling getting the data from the app's ContentProvider
using a standard CursorLoader
. We use DataDroid to manage getting new data from the webservice.
Is there anyone with the slightest clue on what's happening here? Seems like something gets stuck in the ListView
own code until we update the underlying Cursor
...
The fact you're messing with onLayout on ListView rings alarm bells. Why don't you just use a standard ListView with a OnGlobalLayoutListener?
Or if you need the custom ListView, try changing your onLayout()
to:
final LayoutChangedRunnable mLayoutRunnable = new LayoutChangedRunnable()
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
post(mLayoutRunnable);
}
}