GetActivity(). findViewById возвращает null, вызванный из фрагмента onActivityCreated

Я следую урок провайдера контактов по извлечению контактов и показывая их с помощью фрагментов. Для справки, я установил уровень API до 16 (Android 4.1).

Я в основном следовал этому руководству к письму с несколькими заметными исключениями. Например, я импортирую из mypackage.R, а не android.R.

Моя проблема в моем обработчике onActivityCreated в моем 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 имеет значение null, а значение findViewById не работает.

Моя родительская активность является стандартной по умолчанию, созданной eclipse. Для этого я сделал две вещи:

  • Замените import android.app.Activity на android.support.v4.app.FragmentActivity, чтобы предотвратить ClasscastException, если это не так.
  • Импортировал фрагмент в XML.

My activity_list_contacts.xml выглядит так:

<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>

совместимая операция, на всякий случай:

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

и 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"/>

Итак, мой вопрос: что я делаю неправильно для findViewById, чтобы не найти свое мнение?

Вещи, которые я пробовал (большинство из них являются принятыми ответами на вопросы, которые почти похожи на дубликат):

  • Чтение документа, который я копировал и вставлял слово в слово.
  • Попытаемся getView().findViewById() как предложено в этом вопросе. Это также возвращает null.
  • используя findViewById(R.id.contacts_list_view); вместо этого, как предложено этим ответом. Это не возвращает null; вместо этого он вызывает ClasscastException в том, что android.support.v4.app.NoSaveStateFrameLayout не может быть добавлено к android.widget.ListView.
  • Я читал, что иногда обратный вызов фрагмента для создания происходит до присоединения к активности. Итак, я добавил обработчик к методу onAttach следующим образом:

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

    Вы догадались - все еще null.

Итак, в этот момент я немного потерялся. У меня есть два вопроса:

  • Что я делаю неправильно (просьба запросить дополнительную информацию в комментариях, если я не предоставил достаточно)?
  • Предпочтительно ли оставить настройку адаптера в onAttach или где указано учебное пособие.

Ответ 1

Я следую учебнику в developer.android.com и столкнулся с той же проблемой.

Наконец, я заметил, что в этой части:

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

кажется, пытается найти a View, но передавая аргумент Layout id.

Просмотр | android reference говорит:

Идентификаторы просмотра не обязательно должны быть уникальными по всему дереву, но это хорошая практика, чтобы убедиться, что они по крайней мере уникальны в той части дерева, которое вы ищете.

Итак, я изменил ListView id в contacts_list_view.xml на уникальный:

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

затем попробуйте найти его с помощью:

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

и он работает (в обратном вызове onActivityCreated).

  • Об исключении литых: Как показано hierarchyviewer, дерево выглядит следующим образом: hierarchyviewer sc поэтому, если вы findViewById передаете корневой элемент представления, например, в который опубликует, он вернет NoSaveStateFrameLayout, который не является дочерним ListView, который вы хотите.

Ответ 2

getActivity().findViewById() ищет идентификатор, который вы предоставляете в качестве параметра в иерархии представлений Activity. Вероятно, ListView возвращается onCreateView вашего фрагмента. Если вы хотите выполнить findViewById в иерархии представлений фрагментов, вы должны вызвать getView().findViewById(). В этом случае, поскольку вы расширяете ListFragment, чтобы получить ListView, а для ListActivity, вы должны использовать getListView();, другая аналогия с ListActivity является идентификатором ListView внутри xml файл (android:id="@android:id/list")

Ответ 3

Обратите внимание, что ListFragment имеет метод getListView() для возврата экземпляра виджета ListView.

Документация разработчика: ListFragment.getListView

Ответ 4

@theme предлагает решение, но для того, чтобы использовать внутренний список Android, это другой способ. Потому что в вашем файле "contacts_list_view.xml" вы используете внутренний список android inroid с id = @android: id/list. И в вашем коде вы можете ссылаться на представление списка с помощью findViewById (android.R.id.list), поэтому ваш onActivityCreated будет выглядеть следующим образом:

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

Надеюсь, что это поможет, хорошо, что руководство разработчика Android содержит такую ​​ "ошибку", чтобы мы могли быть знакомы с api.