4

I have a ListFragment whose list should diplay the Albums of the device and the associated artist using MediaStore. Each row has thus two TextViews. I'm using LoaderManager and CursorLoader to fill the list up, with a custom CursorAdapter in order to bind the TextViews of the row to the data.

ListFragment Code :

public class AlbumsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

AlbumsAdapter mAdapter;

        @Override
           public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
           View myFragmentView = inflater.inflate(R.layout.albums_fragment_layout, container, false);
           return myFragmentView;
         }


        @Override
          public void onActivityCreated(Bundle savedInstanceState) {
           super.onActivityCreated(savedInstanceState);

                    setListAdapter(mAdapter);
                    getLoaderManager().initLoader(0, null, this);                               
        }


static final String[] ALBUM_SUMMARY_PROJECTION = { MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Albums.ARTIST}; 



        public Loader<Cursor> onCreateLoader(int id, Bundle args) {      
            String select = null;  
            return new CursorLoader(getActivity(), MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,ALBUM_SUMMARY_PROJECTION, select, null, null);  
        }  



        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {  
            mAdapter.swapCursor(data);  
        }  



        public void onLoaderReset(Loader<Cursor> loader) {  
            mAdapter.swapCursor(null);  
        }  
    }

AlbumsAdapter (Custom CursorAdapter) Code :

public class AlbumsAdapter extends CursorAdapter {

    private final LayoutInflater mInflater;

     public AlbumsAdapter(Context context, Cursor c) {
        super(context, c);
        mInflater=LayoutInflater.from(context);
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        TextView albumTitle =(TextView)view.findViewById(R.id.albumTextView);
        albumTitle.setText(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM)));

        TextView artistName=(TextView)view.findViewById(R.id.artistTextView);
        artistName.setText(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Albums.ARTIST)));

    }


    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        final View view=mInflater.inflate(R.layout.albums_row,parent,false); 
        return view;
    }

For the moment, when I display this ListFragment, I have a java.lang.NullPointerException, here is the logCat :

 06-14 09:39:23.109: W/dalvikvm(8632): threadid=1: thread exiting with uncaught exception (group=0x40c2d1f8)
06-14 09:39:23.109: E/AndroidRuntime(8632): FATAL EXCEPTION: main
06-14 09:39:23.109: E/AndroidRuntime(8632): java.lang.NullPointerException
06-14 09:39:23.109: E/AndroidRuntime(8632):     at com.music.pod.AlbumsFragment.onLoadFinished(AlbumsFragment.java:61)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at com.music.pod.AlbumsFragment.onLoadFinished(AlbumsFragment.java:1)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:438)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.app.LoaderManagerImpl$LoaderInfo.onLoadComplete(LoaderManager.java:406)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.content.Loader.deliverResult(Loader.java:125)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.content.CursorLoader.deliverResult(CursorLoader.java:88)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.content.CursorLoader.deliverResult(CursorLoader.java:42)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.content.AsyncTaskLoader.dispatchOnLoadComplete(AsyncTaskLoader.java:236)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.content.AsyncTaskLoader$LoadTask.onPostExecute(AsyncTaskLoader.java:76)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.os.AsyncTask.finish(AsyncTask.java:602)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.os.AsyncTask.access$600(AsyncTask.java:156)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:615)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.os.Handler.dispatchMessage(Handler.java:99)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.os.Looper.loop(Looper.java:137)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at android.app.ActivityThread.main(ActivityThread.java:4507)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at java.lang.reflect.Method.invokeNative(Native Method)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at java.lang.reflect.Method.invoke(Method.java:511)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
06-14 09:39:23.109: E/AndroidRuntime(8632):     at dalvik.system.NativeStart.main(Native Method)

Could you please help me to correct my code in order to have the album and the associated artist displayed into the listview ?

Problem Solved :

For anyone trying to fill up a custom listview with Album,Artist,etc... with the technique LoaderManager/CursorLoader you should :

1) Create your own Custom CursorAdapter which binds TextViews of your row to Cursor's data(see my Custom CursorAdapter above)

2) Then, in your fragment here's what to do :

public class AlbumsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

AlbumsAdapter mAdapter;

        @Override
           public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
           View myFragmentView = inflater.inflate(R.layout.albums_fragment_layout, container, false);
           return myFragmentView;
         }


        @Override
          public void onActivityCreated(Bundle savedInstanceState) {
           super.onActivityCreated(savedInstanceState);

                    mAdapter = new AlbumsAdapter(getActivity(), null);
                    setListAdapter(mAdapter);
                    getLoaderManager().initLoader(0, null, this);                               
        }


static final String[] ALBUM_SUMMARY_PROJECTION = { MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Albums.ARTIST};  



        public Loader<Cursor> onCreateLoader(int id, Bundle args) {      
            String select = null;  
            return new CursorLoader(getActivity(), MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,ALBUM_SUMMARY_PROJECTION, select, null, null);  
        }  



        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {  
            mAdapter.swapCursor(data);  
        }  



        public void onLoaderReset(Loader<Cursor> loader) {  
            mAdapter.swapCursor(null);  
        }  
    }

And that's it !!! I don't know why nobody has ever procured a very simple example like this about how to use LoaderManager,CursorLoader and Custom adapter in order to fill a custom listview. Anyway, I hope this will help beginners like me in the future.

Jecimi
  • 4,113
  • 7
  • 31
  • 40
  • When you have an exception(`NullPointerException` or whatever exception) you should always show the line where that exception is thrown. – user Jun 14 '12 at 03:55
  • @Luksprog : Thanks for the precision, I've updated my post. The exception seems to be at line 61 where there's `mAdapter.swapCursor(data);`. Maybe there's something wrong the way I query the `MediaStore` so the `data` are empty causing this exception ? – Jecimi Jun 14 '12 at 07:44
  • Hey @Jecimi I have a few questions if you don't mind. I too am trying to make a CursorAdapter with a LoaderManager and CursorLoader. Except I am getting a cursor from a database. I am also not using a ListFragment, but just a regular list. So that means my list is in an activity with another view, a GridView actually. How can I set this up to use LoaderManager and CursorLoader without having to change my current layout? – Andy Jun 17 '12 at 20:00
  • @Andy: If you have only one `ListView` in your layout, the other Views being of other kind, I think the simplest is to convert your `Activity` to a `ListActivity`. Even if it is a `ListActivity`, it doesn't mean that you have to have compulsory only one `ListView`, it just mean that your activity contain one `ListView`.Look at my `ListFragment` above, I do inflate it with my XML `albums_fragment_layout.xml` which doesn't contain only one `ListView` but other views. You just have to set `android:id="@id/android:list` to your `ListView`, then just use your `ListActivity` like a `ListFragment`. – Jecimi Jun 18 '12 at 13:24
  • I searched for hours and this helped me out tremendously... so clear and concise! Along with sections 1-3 in this tutorial it made implementation a breeze. http://mobile.tutsplus.com/series/android-fundamentals/ – logray Dec 07 '12 at 23:06

1 Answers1

2

I think your mAdapter is null(I don't see where you initialized it, if you initialized it) and when you try to swap the cursor it throws that exception.

if you didn't initialized that AlbumAdapter reference then you should do it before you set the adapter:

mAdapter = new AlbumsAdapter(getActivity(), null);
setListAdapter(mAdapter);
user
  • 86,916
  • 18
  • 197
  • 190
  • Thanks!!! Two things were missing : I forgot to initialize the custom Adapter as you said and I forgot to put `MediaStore.Audio.Albums._ID` in my projection. – Jecimi Jun 14 '12 at 08:13