findViewByID возвращает ноль

252

Прежде всего: да, я прочитал все другие темы на эту тему. И не только с этого сайта ... (видишь, я немного расстроен)

Большинство из них приходят с советами, чтобы использовать 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работал так, как предполагалось. Сюрприз: это ничего не решило.

Так какого чёрта я делаю не так?

Я буду благодарен за любые комментарии.

Томас
источник
6
Это может произойти, если проект также поврежден. Я исправил это,
очистив
1
пропущено неправильное представление контекста спасибо
izzy
1
@Pacerier спасибо. это решило мою проблему. Я начинаю ненавидеть программирование Android в Xamarin)
Артём
@Pacerier "Чистый проект" тоже решил мою проблему. Кажется, Dropbox испортил проект.
Кохи Маметани

Ответы:

271

который возвращает ноль

Возможно, потому что вы звоните слишком рано. Подожди пока onFinishInflate(). Вот пример проекта, демонстрирующего пользовательский Viewдоступ к его содержимому.

CommonsWare
источник
67
О, МОЙ БОГ! Не могу поверить, что я провожу дни на чем-то таком тривиальном. Я переместил setContentView () выше вызова findViewById (), и это помогло. Спасибо!
agentcurry
2
@ CommonsWare это что правильный проект? Я не вижу на FinishInflate нигде в github.com/commonsguy/cw-advandroid/tree/master/Animation/…
likejudo
1
@likejiujitsu: Это могло произойти еще в 2010 году. Я обновил ссылку на новый проект, который имеет onFinishInflate().
CommonsWare
3
У меня есть setContentView, вызываемый до findViewById, и он все еще нулевой. Я
ссылаюсь
1
Всегда делайте ссылки на фиксированные коммиты GitHub (например, github.com/commonsguy/cw-omnibus/tree/… ) и никогда не удаляйте репо или ребаз ;-)
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
145

Возможно, вы звоните findViewByIdдо звонка setContentView? Если это так, попробуйте позвонить findViewById ПОСЛЕ вызоваsetContentView

Байрам Бойраз
источник
1
Такая легкая ошибка, что я делаю, но, к сожалению, это то, что я сделал. Отладчик бесполезен, так как он сказал, что ошибка была в моей новой строке намерений, а не в фактическом новом действии, которое я вызывал. Спасибо!
edude05
6
На самом деле у меня была эта ошибка, потому что я вызывал setContentView, указывающий на неправильное представление ... к сожалению, компилятор не улавливает этот тип ошибки.
HeatfanJohn
Да, я удалил setContentView по ошибке и не понял, почему моя программа начала давить.
Ронен Фестингер
100

Убедитесь, что у вас нет нескольких версий макета для разных плотностей экрана. Я столкнулся с этой проблемой один раз при добавлении нового идентификатора в существующий макет, но забыл обновить версию hdpi. Если вы забудете обновить все версии файла макета, он будет работать для некоторых плотностей экрана, но не для других.

Джо
источник
8
СПАСИБО! Вот и все. (В моем случае у меня есть несколько версий для разных версий Android). Я постоянно об этом забываю, хотя я помещаю гигантские комментарии сверху и снизу каждого файла макета. Я хотел бы, чтобы компилятор сделал ошибку или большое предупреждение, если это произойдет.
Tiktak
Динь ​​Динь Динь, спасибо большое!
Зак
21

FindViewById может быть нулевым, если вы вызываете неправильный супер-конструктор в пользовательском представлении. Идентификационный тег является частью attrs, поэтому, если вы игнорируете attrs, вы удаляете этот идентификатор.

Это было бы неправильно

public CameraSurfaceView(Context context, AttributeSet attrs) {
        super(context);
}

Это верно

public CameraSurfaceView(Context context, AttributeSet attrs) {
        super(context,attrs);
}
repkap11
источник
1
Большое спасибо, сэр! Я так небрежен, я даже сомневался, что что-то не так с моей IDE. Как повезло встретить ваш ответ.
EffectiveMatrix
Я сделал эту ошибку, когда создал свой собственный вид. Спасибо, repkap11.
Эндрю Ф.
15

В моем случае у меня было 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);

Я заметил это после того, как приехал сюда, на сайт.

infografnet
источник
14

Наряду с классическими причинами, упомянутыми в другом месте:

  • Убедитесь, что вы позвонили setContentView() раньшеfindViewById()
  • Убедитесь, что id вы хотите, чтобы в представлении или макете вы далиsetContentView()
  • Убедитесь, что idне случайно дублируется в разных макетах

Я нашел один для пользовательских представлений в стандартных макетах, что противоречит документации:

Теоретически вы можете создать собственный вид и добавить его в макет ( см. Здесь ). Однако я обнаружил, что в таких ситуациях иногда idатрибут работает для всех представлений в макете, кроме пользовательских. Решение, которое я использую:

  1. Замените каждое пользовательское представление на FrameLayoutте же свойства макета, которые вы хотели бы иметь в пользовательском представлении. Дайте ему соответствующее id, скажемframe_for_custom_view .
  2. В onCreate:

    setContentView(R.layout.my_layout);
    FrameView fv = findViewById(R.id.frame_for_custom_layout);
    MyCustomView cv = new MyCustomView(context);
    fv.addView(cv);

    который помещает пользовательский вид в рамку.

Нил Таунсенд
источник
Это помогло мне: «Убедитесь, что нужный вам идентификатор находится в представлении или макете, который вы дали setContentView ()». Что делать, когда это подвид однако? Попытался получить родительское представление, затем выполнить parentView.findById (), но он по-прежнему возвращает
ноль
@naturalborncamper, если вы отправите отдельный вопрос с примером кода, я посмотрю на него.
Нил Таунсенд
Спасибо, Нил, я уже писал, и кто-то указал лучший способ сделать это, хотя он не работает именно так, как должно быть, поэтому мне пришлось использовать другую стратегию: stackoverflow.com/questions/47955376/…
NaturalBornCamper
9
    @Override
protected void onStart() {
         // use findViewById() here instead of in onCreate()
    }
Джонатан
источник
6

Ответ для тех, кто использует 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);
        }

Я нашел это здесь .

Джош Меткалф
источник
5

Я довольно новичок в Android / Eclipse, по ошибке я добавил материал для интерфейса activity_main.xmlвместо fragment_main.xml. Мне понадобилось несколько часов, чтобы понять это ...

lama12345
источник
5

FWIW, я не вижу, чтобы кто-то решил это так же, как мне было нужно. Никаких жалоб во время компиляции, но я получал нулевое представление во время выполнения и вызывал вещи в правильном порядке. То есть findViewById () после setContentView (). Проблема оказалась в том, что мое представление определено в content_main.xml, но в моем activity_main.xml мне не хватало одного этого утверждения:

<include layout="@layout/content_main" />

Когда я добавил это в activity_main.xml, больше не было NullPointer.

чертово
источник
Проблема с копированием, вставляющим фрагмент, состоит в том, что вы иногда забываете изменить некоторые вещи ... будучи правильным макетом, чтобы надуть один из них. Спасибо!
Пабло Квеме
3

У меня была такая же проблема. Я использовал стороннюю библиотеку, которая позволяет переопределить их адаптер для GridView и указать свой собственный макет для каждой ячейки GridView.

Я наконец понял, что происходит. Eclipse все еще использовал XML-файл макета библиотеки для каждой ячейки в GridView, хотя и не дал никаких указаний на это. В моем собственном адаптере он указывал, что он использовал ресурс xml из моего собственного проекта, хотя во время выполнения этого не было.

Поэтому я убедился, что мои пользовательские макеты и идентификаторы XML отличаются от тех, которые все еще находятся в библиотеке, очистили проект, а затем он начал читать правильные пользовательские макеты, которые были в моем проекте.

Короче говоря, будьте осторожны, если вы переопределяете адаптер сторонней библиотеки и указываете свой собственный XML-формат для использования адаптером. Если ваш макет внутри вашего проекта имеет то же имя файла, что и в библиотеке, вы можете столкнуться с действительно трудной для поиска ошибкой!

JDJ
источник
3

В моем конкретном случае я пытался добавить нижний колонтитул в 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);
Matt
источник
2

В моем случае я использовал ExpandableListView, и я установил android:transcriptMode="normal". Это приводило к исчезновению нескольких детей в расширяемой группе, и я получал исключение NULL, когда использовал прокрутку списка.

Дживан
источник
2

Для меня у меня было два xml-макета для одной и той же деятельности - одна в портретном режиме и одна в альбомной. Конечно, я изменил идентификатор объекта в xml ландшафта, но забыл сделать то же самое изменение в портретной версии. Удостоверьтесь, что если вы замените один, вы сделаете то же самое на другой xml, иначе вы не получите сообщение об ошибке, пока не запустите / отладите его и не сможете найти идентификатор, который вы не изменили. О глупые ошибки, почему вы меня так наказываете?

ColossalChris
источник
2

Установите содержание деятельности из ресурса макета. то есть. setContentView(R.layout.basicXml);

Аджай Такур
источник
2

В дополнение к вышеупомянутым решениям вы убедитесь, что
tools:context=".TakeMultipleImages" в макете то же значение в файле mainfest.xml:
android:name=".TakeMultipleImages"для того же элемента действия. это происходит при использовании копирования и вставки для создания новой деятельности

user2982286
источник
2

У меня та же проблема, но я думаю, что стоит поделиться с вами, ребята. Если вам нужно найти ViewById в пользовательском макете, например:

public class MiniPlayerControllBar extends LinearLayout {
    //code
}

Вы не можете получить представление в конструкторе. Вы должны вызвать findViewById после того, как представление завышено. Это метод, который вы можете переопределитьonFinishInflate

Шоу
источник
2

Просто хотел бросить мой конкретный случай здесь. Может помочь кому-нибудь по линии.

Я использовал директиву в своем 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">
Джон Д.
источник
2

Мой случай не похож на выше, решения не сработали. Я предполагаю, что мое мнение было слишком глубоко в иерархии макетов. Я переместил его на один уровень вверх, и он больше не был нулевым.

Дейвидас Стриога
источник
У меня та же проблема, также можно найти на один уровень выше ... это довольно раздражает, и я хотел бы знать, почему это происходит, потому что я не могу сделать свое меню, как я хочу
NaturalBornCamper
Хорошо глубокие иерархии макетов - это анти-паттерн, почти всегда есть более чистое решение. Вы говорите о переполнении меню на панели инструментов или в панели навигации?
Дейвидас Стриога
На панели навигации я уже разместил еще один вопрос, за исключением того, что добавление элемента меню внутри группы не работает, просто добавляет его в конце всех элементов: stackoverflow.com/questions/47955376/…
NaturalBornCamper
1

В моем случае я раздул макет, но дочерние представления возвращали ноль. Изначально у меня было это:

@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 ...
Suragch
источник
0

По моему опыту, кажется, что это также может происходить, когда ваш код вызывается после OnDestroyView (когда фрагмент находится в заднем стеке). Если вы обновляете пользовательский интерфейс при вводе из BroadCastReceiver, вам следует проверить, так ли это ,

Тэд
источник
0

Надуйте план! (который содержит идентификатор)

В моем случае findViewById () вернул null, потому что макет, в котором был написан элемент, не был раздут ...

Например. fragment_layout.xml

<ListView
android:id="@+id/listview">

findViewById (R.id.listview) возвратил ноль, потому что я не сделал inflater.inflate (R.layout.fragment_layout, ..., ...); перед этим.

Надеюсь, что этот ответ поможет некоторым из вас.

КОВЧЕГ
источник
0

Мое решение было просто очистить проект.

Liangjun
источник
0

findViewById также может возвращать ноль, если вы находитесь внутри фрагмента. Как описано здесь: findViewById во фрагменте

Вы должны вызвать getView (), чтобы вернуть View верхнего уровня внутри фрагмента. Затем вы можете найти элементы макета (кнопки, текстовые представления и т. Д.)

технократ
источник
0

Я перепробовал все вышеперечисленное, ничего не получалось .. поэтому мне пришлось сделать свой ImageView статичным, public static ImageView texture; а затем texture = (ImageView) findViewById(R.id.texture_back);, я не думаю, что это хороший подход, но это действительно работает для моего случая

Tabish
источник
извините, я думаю, что статичное представление - плохая идея
vuhung3990
0

В моем случае 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);
Lexo
источник
0

Сбой произошел из-за того, что одно из полей в идентификаторе моей активности совпадало с идентификатором в другой активности. Я исправил это, дав уникальный идентификатор.

В моем поле логина loginActivity.xml идентификатор был "пароль". В своей регистрационной активности я просто исправил это, указав идентификатор r_password, после чего он вернул ненулевой объект:

password = (EditText)findViewById(R.id.r_password);
Ягелу Ядав
источник
0

Я столкнулся с аналогичной проблемой, когда я пытался сделать пользовательский вид для ListView .

Я решил это просто так:

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);
Рафаэль Коста
источник
0

Способы отладки и поиска проблемы:

  • Закомментируйте все findViewById в вашей деятельности.
  • Закомментируйте все, кроме onCreate и setContentView
  • Запустите проект и посмотрите, установлен ли какой-либо макет

В моем случае я использовал файл activity_main.xml как в модуле приложения, так и в модуле библиотеки. Поэтому, когда я выполнил вышеуказанные шаги, вместо макета, который я разработал в библиотеке, макет внутри модуля приложения был раздут.

Поэтому я изменил имя файла Activity_main.xml на Activity_main_lib.xml.

Поэтому убедитесь, что у вас нет повторяющихся имен макетов во всем вашем проекте .

Reejesh PK
источник