findViewByID повертає null


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 дякую що вирішило мою проблему. Я починаю ненавидіти програмування андроїд у Xamarin)
Артіом

@Pacerier "Чистий проект" вирішив і мою проблему. Схоже, Dropbox зіпсував проект.
Кокі Маметані

Відповіді:


271

що повертає null

Можливо, тому, що ви називаєте це занадто рано. Зачекайте, поки onFinishInflate(). Ось зразок проекту, що демонструє користувацький Viewдоступ до його вмісту.


67
О БОЖЕ МІЙ! Не можу вірити, що я витрачаю дні на щось таке банальне. Я перемістив setContentView () над викликом findViewById (), і це не обдурило. Дякую!
агентстворіть

2
@CommonsВважаєте, що це правильний проект? Я ніде не бачу onFinishInflate ні в github.com/commonsguy/cw-advandroid/tree/master/Animation/…
likejudo

1
@likejiujitsu: Можливо, це було ще в 2010 році. Я оновив посилання на новий проект, який є onFinishInflate().
CommonsWare

3
У мене setContentView зателефонував перед findViewById, і він досі є нульовим. Я маю на увазі EditText
неонову війну

1
Завжди посилайтеся на фіксовані зобов’язання GitHub (наприклад, github.com/commonsguy/cw-omnibus/tree/… ) і ніколи не видаляйте
репости або перезавантаження

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

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

Я помітив це після приїзду сюди, на майданчик.


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

@naturalborncamper, якщо ви опублікуєте окреме запитання з прикладом коду, я роздивлюсь його.
Ніл Таунсенд

Завдяки Neil, я вже писав , і хто - то вказав кращий спосіб зробити це, хоча це не працює точно , як це повинно бути , тому мені довелося використовувати іншу стратегію: stackoverflow.com/questions/47955376 / ...
NaturalBornCamper


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. Мені потрібні кілька годин, щоб зрозуміти це ...


5

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

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

Коли я додав це до Activity_main.xml, більше не NullPointer.


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

3

У мене була ця сама проблема. Я використовував сторонню бібліотеку, яка дозволяє переосмислити їх адаптер для GridView та вказати власний макет для кожної комірки GridView.

Я нарешті зрозумів, що відбувається. Eclipse все ще використовував файл розміщення xml-файлу бібліотеки для кожної комірки GridView, хоча це не вказувало на це. У моєму користувальницькому адаптері було вказано, що він використовує ресурс xml з мого власного проекту, хоча під час виконання цього не було.

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

Якщо коротко, будьте обережні, якщо ви переосмислюєте адаптер сторонньої бібліотеки та вказуєте свій власний макет xml для використання адаптера. Якщо ваш макет у вашому проекті має те саме ім’я файлу, що і в бібліотеці, ви можете зіткнутися з дійсно важко знайти помилку!


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

2

У моєму випадку я використовував ExpandableListView і встановив android:transcriptMode="normal". Це призвело до зникнення небагатьох дітей у групі, що розширюється, і я коли-небудь отримував виняток NULL коли я використовував прокрутку списку.


2

Для мене було два макети xml для однієї і тієї ж діяльності - одна в портретному режимі та одна в альбомній. Звичайно, я змінив ідентифікатор об'єкта в пейзажному xml, але забув змінити таку ж зміну у портретній версії. Переконайтесь, що якщо ви зміните один, ви зробите те саме на інший xml, або ви не отримаєте помилку, поки не запустите / налагодите його, і він не зможе знайти ідентифікатор, який ви не змінили. О, дурні помилки, чому ти повинен так мене карати?



2

На додаток до вищезазначених рішень, ви переконайтесь, що
tools:context=".TakeMultipleImages" в макеті однакове значення у файлі mainfest.xml:
android:name=".TakeMultipleImages"для того ж елемента активності. це відбувається при використанні копіювання та вставки для створення нової діяльності


2

У мене така ж проблема, але я думаю, що варто поділитися з вами, хлопці. Якщо вам потрібно знайтиViewById у власному макеті, наприклад:

public class MiniPlayerControllBar extends LinearLayout {
    //code
}

ви не можете отримати представлення в конструкторі. Вам слід зателефонувати findViewById після того, як перегляд завищений. Їх метод, який можна перекритиonFinishInflate


2

Просто хотів сюди занести мою конкретну справу. Може допомогти комусь внизу.

Я використовував директиву у своєму XML-інтерфейсі 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" />

Дитячий вигляд (повторна кнопка):

<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. Але якщо я перемістив ідентифікатор з дочірнього виду в тег включення, він почав працювати.

Виправлений батьків:

<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

Добре ієрархії глибокого планування є анти-візерунком, майже завжди є більш чисте рішення. Ви говорите про меню переповнення на панелі інструментів або навігаційному ящику?
Deividas Strioga

Навігація ящик, я вже відправив ще одне питання на самому справі, за винятком того, що додавання пункту меню всередині групи не працює, просто додаю його в кінці всіх елементів: 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 ...

0

На мій досвід, здається, що це може статися і тоді, коли ваш код буде викликаний після OnDestroyView (коли фрагмент знаходиться на задній стеці.) Якщо ви оновлюєте інтерфейс користувача на вхід з BroadCastReceiver, вам слід перевірити, чи це так .


0

ВПЛИВАЙТЕ ЛАЙОН !! (який містить ідентифікатор)

У моєму випадку findViewById () повернув нуль, тому що макет, в якому записаний елемент, не був завищений ...

Напр. fragment_layout.xml

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

findViewById (R.id.listview) повернув нуль, тому що я не робив inflater.inflate (R.layout.fragment_layout, ..., ...); перед цим.

Сподіваюсь, що ця відповідь допомагає комусь із вас.



0

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

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


0

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


вибачте, я думаю, що статичний вигляд є поганою ідеєю
vuhung3990,

0

У моєму випадку findViewById повернув нуль, коли я перемістив виклик з батьківського об'єкта в об'єкт адаптера, ініційований батьком. Спробувавши перелічені тут трюки без успіху, я перемістив findViewById назад в батьківський об'єкт і передав результат як параметр під час інстанцізації об'єкта адаптера. Наприклад, я зробив це в батьківському об'єкті:

 Spinner hdSpinner = (Spinner)view.findViewById(R.id.accountsSpinner);

Потім я передав hdSpinner як параметр під час створення об'єкта адаптера:

  mTransactionAdapter = new TransactionAdapter(getActivity(),
        R.layout.transactions_list_item, null, from, to, 0, hdSpinner);

0

Це сталося для мене, тому що одне з полів мого ідентифікатора активності збігалося з ідентифікатором у іншій діяльності. Я виправив це, давши унікальний ідентифікатор.

У моєму loginActivity.xml ідентифікатор поля поля був "пароль". У своїй реєстраційній діяльності я його просто виправив, вказавши id 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.

Тому переконайтеся, що у вашому проекті немає жодних повторюваних назв макета .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.