android-viewflow
android-viewflow copied to clipboard
AdapterDataSetObserver.onChanged() performs a layout
When swapCursor()
is called on a SimpleCursorAdapter
and the cursor is transitioning from null
to populated, the app encounters a NPE. This is due to requestFocus()
being called from AdapterDataSetObserver.onChanged()
, where other adapters (in particular AdapterView.AdapterDataSetObserver
) call requestLayout()
instead.
Ideally, the layout should not happen until onLayout().
This is using the Android v4 support library.
Demo:
(make sure to add <uses-permission android:name="android.permission.READ_CONTACTS"/>
and at least one contact)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<org.taptwo.android.widget.ViewFlow
android:id="@+id/contacts"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
package com.example.viewflowtest;
import org.taptwo.android.widget.ViewFlow;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
public class MainActivity extends FragmentActivity implements LoaderCallbacks<Cursor> {
private ViewFlow mViewFlow;
private SimpleCursorAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewFlow = (ViewFlow) findViewById(R.id.contacts);
mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null,
new String[] { Contacts.DISPLAY_NAME_PRIMARY }, new int[] { android.R.id.text1 }, 0);
mViewFlow.setAdapter(mAdapter);
getSupportLoaderManager().restartLoader(0, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
final String[] projection = new String[] { Contacts._ID, Contacts.DISPLAY_NAME_PRIMARY };
return new CursorLoader(this, Contacts.CONTENT_URI, projection, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor c) {
mAdapter.swapCursor(c);
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
// XXX this is the start of the crash
mAdapter.swapCursor(null);
}
}