11

I am following the contacts provider lesson on retrieving contacts and displaying them using fragments. For reference, I have set the API level to 16 (Android 4.1).

I have mostly followed this tutorial to the letter, with a few notable exceptions. For example, I import from mypackage.R rather than android.R.

My problem is in my onActivityCreated handler in my ListContactsFragment:

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    // Initializes the loader
    getLoaderManager().initLoader(0, null, this);
    // Gets the ListView from the View list of the parent activity

    View mContactsListView = 
                    getActivity().findViewById(R.layout.contacts_list_view);
    mContactsList = (ListView) mContactsListView;

    // Gets a CursorAdapter
    mCursorAdapter = new SimpleCursorAdapter(
            getActivity(),
            R.layout.contacts_list_item,
            null,
            FROM_COLUMNS, TO_IDS,
            0);
    // Sets the adapter for the ListView
    mContactsList.setAdapter(mCursorAdapter);

    // Set the item click listener to be the current fragment.
    mContactsList.setOnItemClickListener(this);
}

View mContactsListView is null, meaning findViewById didn't work.

My parent activity is a default one created by eclipse. To this, I have done two things:

  1. Replaced import android.app.Activity with android.support.v4.app.FragmentActivity to prevent the ClasscastException that happens if you don't.
  2. Imported my fragment into the XML.

My activity_list_contacts.xml looks like this:

<RelativeLayout 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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".ListContactsActivity" >

    <fragment android:name="mypackage.ListContactsFragment"
              android:id="@+id/contacts_list_view"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />

</RelativeLayout>

the matching activity, just in case:

public class ListContactsActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_contacts);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.list_contacts, menu);
        return true;
    }
}

and contacts_list_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@android:id/list"
          android:layout_width="match_parent"
          android:layout_height="match_parent"/>

So my question is, what am I doing wrong for findViewById not to find my view?

Things I have tried (most of these are accepted answers on questions that almost look like a duplicate to this one):

  • Reading the document I copied and pasted from word for word.
  • Trying to getView().findViewById() as suggested in this question. This also returns null.
  • using findViewById(R.id.contacts_list_view); instead as suggested by this answer. This doesn't return null; instead it causes a ClassCastException in that android.support.v4.app.NoSaveStateFrameLayout could not be cast to android.widget.ListView.
  • I read that sometimes the fragment callback for creation occurs before being attached to the activity. So, I added the handler to the onAttach method like so:

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
    
        View mContactsListView = activity.findViewById(R.id.contacts_list_view);
        mContactsList = (ListView) mContactsListView;
    
        // Gets a CursorAdapter
        mCursorAdapter = new SimpleCursorAdapter(
                getActivity(),
                R.layout.contacts_list_item,
                null,
                FROM_COLUMNS, TO_IDS,
                0);
        // Sets the adapter for the ListView
        mContactsList.setAdapter(mCursorAdapter);
    
        // Set the item click listener to be the current fragment.
        mContactsList.setOnItemClickListener(this);
    }
    

    You guessed it - still null.

So at this point I am a little lost. I have two questions:

  1. What am I doing wrong (please request additional info in comments if I have not provided enough)?
  2. Is it preferable to leave the adapter setup in onAttach, or where the tutorial says.
Community
  • 1
  • 1
  • If you are using an id from the Android framework, it should be used as `android.R.id.*`. Is `TO_IDS` defined as `private final static int[] TO_IDS = { android.R.id.text1 }`? – Vikram Aug 01 '13 at 21:05
  • @vikram yep, it is, as a member of the fragment class. –  Aug 01 '13 at 21:07

4 Answers4

11

I follow the tutorial at developer.android.com and encountered the very same problem.

Finally i noticed that in this part:

// Gets the ListView from the View list of the parent activity
    mContactsList = (ListView) getActivity().findViewById(R.layout.contact_list_view);

it seems trying to find a View, but passing a Layout id as argument.

The View | android reference says :

View IDs need not be unique throughout the tree, but it is good practice to ensure that they are at least unique within the part of the tree you are searching.

So, i changed the ListView id in the contacts_list_view.xml to a unique one:

<?xml version="1.0" encoding="utf-8"?>
<ListView ...
android:id="@+id/contacts_listview_a_unique_id_here"
... />

then try to find it with:

        mContactsList = (ListView) getActivity().findViewById(R.id.contacts_listview_a_unique_id_here);

and it works ( in the onActivityCreated callback).

  • About the cast exception: As hierarchyviewer shows, the tree is like this: hierarchyviewer sc so if you findViewById passing root element of the view, like in that post, it will return NoSaveStateFrameLayout, which is not the child ListView you want.
Community
  • 1
  • 1
theme
  • 238
  • 5
  • 13
2

getActivity().findViewById() looks for the id you provide as parameter in the hierarchy of views of the Activity. Probably the ListView is returned by onCreateView of your fragment. If you want to perform a findViewById on the fragment views hierarchy, you have to call getView().findViewById(). In this case, since you are extending ListFragment, to retrieve the ListView, as for the ListActivity, you have to use getListView(); another analogy with the ListActivity is the id of the ListView inside the xml file ( android:id="@android:id/list")

Blackbelt
  • 156,034
  • 29
  • 297
  • 305
  • I know this is a old question, but hopefully you can clear something up for me. I asked a question about a hour ago, I think I've resolved the issue by calling `getActivity.findViewById`, but the view that I'm referring to is a fragment, which doesn't make sense, here is a link to my question - https://stackoverflow.com/questions/50736715/view-returning-null-when-trying-to-apply-margin-using-layoutparams – ClassA Jun 07 '18 at 09:57
1

Note that ListFragment has a getListView() method to return the ListView widget instance.

Developer docs: ListFragment.getListView

Alex MDC
  • 2,416
  • 1
  • 19
  • 17
0

@theme propose a solution, but in order to use android's inner listview here is another way. Because in your "contacts_list_view.xml" file your're using android's inner listview with id=@android:id/list. And in your code you can refer to the list view with findViewById(android.R.id.list), so your onActivityCreated will be like this:

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mContactsList = (ListView) getActivity().findViewById(android.R.id.list);//(R.layout.fragment_contact_list);
    mCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.contact_item_list,null, FROM_COLUMNS, TO_IDS, 0);
    mContactsList.setAdapter(mCursorAdapter);
    mContactsList.setOnItemClickListener(this);
    getLoaderManager().initLoader(0,null,this);
}

Hope it helps, it's good the android developer guide contains such "bug" so that we can be familiar with the api.

telmo
  • 153
  • 8