FindViewByID возвращает null

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

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

Так что, черт возьми, я делаю неправильно?

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

Ответ 1

который возвращает null

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

  • Убедитесь, что вы вызывали 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);
    

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

Ответ 6

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

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

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

Это правильно.

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

Ответ 7

    @Override
protected void onStart() {
         // use findViewById() here instead of in onCreate()
    }

Ответ 8

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

Я нашел здесь здесь.

Ответ 9

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

Ответ 10

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

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

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

Ответ 11

В моем конкретном случае я пытался добавить нижний колонтитул в ListView. Следующий вызов в onCreate() возвращал значение null.

TextView footerView = (TextView) placesListView.findViewById(R.id.footer);

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

View footerView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.footer_view, null, false);

Ответ 12

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

Ответ 13

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

Ответ 14

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

Ответ 15

Задайте содержимое активности из ресурса макета. т.е. setContentView(R.layout.basicXml);.

Ответ 16

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

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

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

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

Ответ 17

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

Ответ 18

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

public class MiniPlayerControllBar extends LinearLayout {
    //code
}

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

Ответ 19

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

Я использовал эту директиву в своем 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) всегда возвращает null. Но, если я переместил идентификатор из дочернего представления в тег 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">

Ответ 20

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

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

Ответ 21

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

Ответ 22

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

Ответ 23

ОГРАНИЧИВАЙТЕ СРОК!! (который содержит идентификатор)

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

Eg. fragment_layout.xml

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

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

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

Ответ 24

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

Ответ 25

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

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

Ответ 26

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

Ответ 27

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

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

password = (EditText)findViewById(R.id.r_password);