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, дерево выглядит следующим образом:
поэтому, если вы 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.