findViewByID возвращает ноль
Прежде всего: да, я прочитал все другие темы на эту тему. И не только с этого сайта... (видите, я немного расстроен)
Большинство из них приходят с советом, чтобы использовать android:id
вместо просто id
в файле XML. Я сделал.
От других я узнал, что View.findViewById
работает иначе, чем Activity.findViewById
, Я тоже с этим справился.
В моем location_layout.xml
, Я использую:
<FrameLayout .... >
<some.package.MyCustomView ... />
<LinearLayout ... >
<TextView ...
android:id="@+id/txtLat" />
...
</LinearLayout>
</FrameLayout>
В своей деятельности я делаю:
...
setContentView( R.layout.location_layout );
и в моем пользовательском классе просмотра:
...
TextView tv = (TextView) findViewById( R.id.txtLat );
который возвращается null
, Делая это, моя активность работает нормально. Так что, возможно, это из-за Activity.findViewById
а также View.findViewById
различия. Поэтому я сохранил контекст, переданный конструктору представления таможни локально, и попытался:
...
TextView tv = (TextView) ((Activity) context).findViewById( R.id.txtLat );
который также вернулся null
,
Затем я изменил свой пользовательский вид, чтобы расширить ViewGroup
вместо View
и изменил location_layout.xml
чтобы позволить TextView
быть прямым ребенком моего обычного взгляда, так что View.findViewById
должен работать как положено. Сюрприз: это ничего не решило.
Так какого чёрта я делаю не так?
Я буду благодарен за любые комментарии.
34 ответа
который возвращает ноль
Возможно, потому что вы звоните слишком рано. Подожди пока onFinishInflate()
, Вот пример проекта, демонстрирующего View
доступ к его содержимому.
Возможно, вы звоните findViewById
перед звонком setContentView
? Если это так, попробуйте позвонить findViewById
ПОСЛЕ вызова setContentView
Убедитесь, что у вас нет нескольких версий макета для разных плотностей экрана. Я столкнулся с этой проблемой один раз при добавлении нового идентификатора в существующий макет, но забыл обновить версию hdpi. Если вы забудете обновить все версии файла макета, он будет работать для некоторых плотностей экрана, но не для других.
FindViewById может быть нулевым, если вы вызываете неправильный супер-конструктор в пользовательском представлении. Идентификационный тег является частью attrs, поэтому, если вы игнорируете attrs, вы удаляете этот идентификатор.
Это было бы неправильно
public CameraSurfaceView(Context context, AttributeSet attrs) {
super(context);
}
Это правильно
public CameraSurfaceView(Context context, AttributeSet attrs) {
super(context,attrs);
}
Наряду с классическими причинами, упомянутыми в другом месте:
- Убедитесь, что вы позвонили
setContentView()
доfindViewById()
- Убедитесь, что
id
Вы хотите, чтобы в представлении или макете вы далиsetContentView()
- Убедитесь, что
id
не случайно дублируется в разных макетах
Я нашел один для пользовательских представлений в стандартных макетах, что противоречит документации:
Теоретически вы можете создать собственный вид и добавить его в макет ( см. Здесь). Тем не менее, я обнаружил, что в таких ситуациях иногда id
Атрибут работает для всех видов в макете, кроме пользовательских. Решение, которое я использую:
- Замените каждый пользовательский вид на
FrameLayout
с теми же свойствами макета, которые вы хотели бы иметь в пользовательском представлении. Дать ему соответствующийid
, сказатьframe_for_custom_view
, В
onCreate
:setContentView(R.layout.my_layout); FrameView fv = findViewById(R.id.frame_for_custom_layout); MyCustomView cv = new MyCustomView(context); fv.addView(cv);
который помещает пользовательский вид в рамку.
В моем случае у меня было 2 актива в моем проекте, main.xml
а также main2.xml
, С начала, main2
была копия main
и все работало хорошо, пока я не добавил новый TextView
в main2
, Итак R.id.textview1
стало доступно для остальной части приложения. Затем я попытался получить его стандартным вызовом:
TextView tv = (TextView) findViewById( R.id.textview1 );
и это всегда было нулевым. Оказалось, что в onCreate
конструктор я не был создан экземпляр main2
, но другой. Я имел:
setContentView(R.layout.main);
вместо
setContentView(R.layout.main2);
Я заметил это после того, как приехал сюда, на сайт.
@Override
protected void onStart() {
// use findViewById() here instead of in onCreate()
}
FWIW, я не вижу, чтобы кто-то решил это так же, как мне было нужно. Никаких жалоб во время компиляции, но я получал нулевое представление во время выполнения и вызывал вещи в правильном порядке. То есть findViewById() после setContentView(). Проблема оказалась в том, что мое представление определено в content_main.xml, но в моем activity_main.xml мне не хватало одного этого утверждения:
<include layout="@layout/content_main" />
Когда я добавил это в activity_main.xml, больше не было NullPointer.
Ответ для тех, кто использует ExpandableListView и сталкиваются с этим вопросом, основываясь на его названии.
Я имел эту ошибку, пытаясь работать с TextViews в моем дочернем и групповом представлениях как часть реализации ExpandableListView.
Вы можете использовать что-то вроде следующего в своих реализациях методов getChildView() и getGroupView().
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) myContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.child_layout, null);
}
Я нашел это здесь.
Я довольно новичок в Android/Eclipse, по ошибке я добавил материал для интерфейса activity_main.xml
вместо fragment_main.xml
, Мне понадобилось несколько часов, чтобы понять это...
В моем конкретном случае я пытался добавить нижний колонтитул в ListView. Следующий вызов в onCreate() возвращал ноль.
TextView footerView = (TextView) placesListView.findViewById(R.id.footer);
Изменение этого значения для надувания представления нижнего колонтитула вместо поиска по идентификатору решило эту проблему.
View footerView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.footer_view, null, false);
Мой случай не похож на выше, решения не сработали. Я предполагаю, что мое мнение было слишком глубоко в иерархии макетов. Я переместил его на один уровень вверх, и он больше не был нулевым.
У меня была такая же проблема. Я использовал стороннюю библиотеку, которая позволяет переопределить их адаптер для GridView и указать свой собственный макет для каждой ячейки GridView.
Я наконец понял, что происходит. Eclipse все еще использовал XML-файл макета библиотеки для каждой ячейки в GridView, хотя и не дал никаких указаний на это. В моем собственном адаптере он указывал, что он использовал ресурс xml из моего собственного проекта, хотя во время выполнения этого не было.
Так что я сделал, чтобы убедиться, что мои пользовательские макеты и идентификаторы XML отличаются от тех, которые все еще находятся в библиотеке, очистили проект, а затем он начал читать правильные пользовательские макеты, которые были в моем проекте.
Короче говоря, будьте осторожны, если вы переопределяете адаптер сторонней библиотеки и указываете свой собственный формат XML для этого адаптера. Если ваш макет внутри вашего проекта имеет то же имя файла, что и в библиотеке, вы можете столкнуться с действительно трудной для поиска ошибкой!
Просто хотел бросить мой конкретный случай здесь. Может помочь кому-нибудь по линии.
Я использовал директиву в своем XML-интерфейсе Android для Android следующим образом:
Родительский вид:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="home_phone"
android:background="@color/colorPrimary">
...
<include
layout="@layout/retry_button"
android:visibility="gone" />
Детский вид (retry_button):
<com.foo.RetryButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/retry"
android:layout_gravity="center"
android:orientation="vertical"
android:layout_width="100dp"
android:layout_height="140dp">
.findViewById (R.id.retry) всегда будет возвращать ноль. Но если я переместил идентификатор из дочернего представления в тег include, он начал работать.
Фиксированный родитель:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:tag="home_phone"
android:background="@color/colorPrimary">
...
<include
layout="@layout/retry_button"
android:id="@+id/retry"
android:visibility="gone" />
Фиксированный ребенок:
<com.foo.RetryButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_gravity="center"
android:orientation="vertical"
android:layout_width="100dp"
android:layout_height="140dp">
Установите содержание деятельности из ресурса макета. то есть.,setContentView(R.layout.basicXml)
;
В дополнение к вышеупомянутым решениям вы убедитесь, чтоtools:context=".TakeMultipleImages"
в макете то же значение в файле mainfest.xml:android:name=".TakeMultipleImages"
для того же элемента деятельности. это происходит при использовании копирования и вставки для создания новой деятельности
Для меня у меня было два xml-макета для одной и той же деятельности - одна в портретном режиме и одна в альбомной. Конечно, я изменил идентификатор объекта в xml ландшафта, но забыл сделать то же самое изменение в портретной версии. Удостоверьтесь, что если вы замените один, вы сделаете то же самое на другой xml, иначе вы не получите сообщение об ошибке, пока не запустите / отладите его и не сможете найти идентификатор, который вы не изменили. О глупые ошибки, почему вы меня так наказываете?
У меня та же проблема, но я думаю, что стоит поделиться с вами, ребята. Если вам нужно найти ViewById в пользовательском макете, например:
public class MiniPlayerControllBar extends LinearLayout {
//code
}
Вы не можете получить представление в конструкторе. Вы должны вызвать findViewById после того, как представление завышено. Это метод, который вы можете переопределить onFinishInflate
В моем случае я использовал ExpandableListView, и я установил android:transcriptMode="normal"
, Это приводило к исчезновению нескольких детей в расширяемой группе, и я получал исключение NULL, когда использовал прокрутку списка.
Я пробовал все вышеперечисленное, ничего не получалось.. поэтому мне пришлось сделать мой ImageView статическим public static ImageView texture;
а потом texture = (ImageView) findViewById(R.id.texture_back);
Я не думаю, что это хороший подход, но это действительно сработало для моего случая:)
Надуйте план! (который содержит идентификатор)
В моем случае findViewById() вернул null, потому что макет, в котором был написан элемент, не был раздут...
Например. fragment_layout.xml
<ListView
android:id="@+id/listview">
findViewById (R.id.listview) возвратил ноль, потому что я не сделал inflater.inflate(R.layout.fragment_layout, ..., ...); перед этим.
Надеюсь, что этот ответ поможет некоторым из вас.
В моем случае я раздул макет, но дочерние представления возвращали ноль. Изначально у меня было это:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_history);
footerView = ((LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.listview_footer, null, false);
pbSpinner = (ProgressBar) findViewById(R.id.pbListviewFooter);
tvText = (TextView) findViewById(R.id.tvListviewFooter);
...
}
Однако, когда я изменил его на следующее, это сработало:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_history);
footerView = ((LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.listview_footer, null, false);
pbSpinner = (ProgressBar) footerView.findViewById(R.id.pbListviewFooter);
tvText = (TextView) footerView.findViewById(R.id.tvListviewFooter);
...
}
Ключ должен был специально ссылаться на уже раздутый макет, чтобы получить дочерние представления. То есть добавить footerView
:
- footerView.findViewById...
Сбой для меня, потому что одно из полей в идентификаторе моей активности совпадает с идентификатором в другой активности. Я исправил это, дав уникальный идентификатор.
В моем поле логина loginActivity.xml идентификатор был "пароль". В своей регистрационной активности я просто исправил это, указав идентификатор r_password, затем он вернул не нулевой объект:
password = (EditText)findViewById(R.id.r_password);
findViewById также может возвращать ноль, если вы находитесь внутри фрагмента. Как описано здесь: findViewById во фрагменте
Вы должны вызвать getView(), чтобы вернуть View верхнего уровня внутри фрагмента. Затем вы можете найти элементы макета (кнопки, текстовые представления и т. Д.)
У меня был один и тот же файл .xml в двух отдельных модулях с одинаковым идентификатором представления.
- приложение/src/main/res/layout/header.xml
- supportModule/src/main/res/layout/header.xml
Оба макета имелиTextView
с тем же идентификатором @+id/tv_prompt .
МойTextView tvPrompt = findViewById(R.id.tv_prompt);
всегда возвращал ноль. Мне пришлось переименовать другой файл xml в header_main.xml , чтобы решить эту проблему.
Я использую Flamingo 2022.2.1 с исправлением 2 (сборка № AI-222.4459.24.2221.10121639, построенная 12 мая 2023 г.).
В моем случае это произошло потому, что по какой-то причине у меня было ДВА объекта Activity_main.xml в папке макета. Один из них был «Activity_main.xml», а другой — «(v31)activity_main.xml».
Конечно, (v31)activity_main.xml не содержал кнопку, которую я пытался использовать с findViewById, и в результате она вернула значение null.
Я понятия не имею, откуда взялся «(v31)activity_main.xml», но после того, как я выполнил безопасное удаление и удалил его, проблема исчезла.
Надеюсь, это поможет кому-то в будущем!
I was facing a similar problem when I was trying to do a custom view for a ListView
.
I solved it simply by doing this:
public View getView(int i, View view, ViewGroup viewGroup) {
// Gets the inflater
LayoutInflater inflater = LayoutInflater.from(this.contexto);
// Inflates the layout
ConstraintLayout cl2 = (ConstraintLayout)
inflater.inflate(R.layout.custom_list_view, viewGroup, false);
//Insted of calling just findViewById, I call de cl2.findViewById method. cl2 is the layout I have just inflated.
TextView tv1 = (TextView)cl2.findViewById(cl2);
Для меня он был нулевым только при использовании Evaluate Expression или представления Debug Watch в среде IDE.
В моем случае findViewById вернул null, когда я переместил вызов из родительского объекта в объект адаптера, созданный родителем. После безуспешных попыток, перечисленных здесь, я переместил findViewById обратно в родительский объект и передал результат в качестве параметра во время создания объекта адаптера. Например, я сделал это в родительском объекте:
Spinner hdSpinner = (Spinner)view.findViewById(R.id.accountsSpinner);
Затем я передал hdSpinner в качестве параметра при создании объекта адаптера:
mTransactionAdapter = new TransactionAdapter(getActivity(),
R.layout.transactions_list_item, null, from, to, 0, hdSpinner);
Способы отладки и поиска проблемы:
- Закомментируйте все findViewById в своей деятельности.
- Закомментируйте все, кроме onCreate и setContentView
- Запустите проект и посмотрите, установлен ли какой-либо макет
В моем случае я использовал activity_main.xml как в моем модуле приложения, так и в модуле библиотеки. Поэтому, когда я выполнил вышеуказанные шаги, вместо макета, который я разработал в библиотеке, макет внутри модуля приложения был раздут.
Поэтому я изменил имя файла activity_main.xml на activity_main_lib.xml.
Поэтому убедитесь, что у вас нет повторяющихся имен макетов во всем проекте.