android-viewflow icon indicating copy to clipboard operation
android-viewflow copied to clipboard

AdapterDataSetObserver.onChanged() performs a layout

Open xxv opened this issue 12 years ago • 0 comments

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);
    }
}

xxv avatar Oct 31 '12 20:10 xxv