findViewById () возвращает null для пользовательского компонента в XML макета, а не для других компонентов


93

У меня есть в res/layout/main.xmlтом числе эти элементы и другие:

<some.package.MyCustomView android:id="@+id/foo" (some other params) />
<TextView android:id="@+id/boring" (some other params) />

В моем Activity onCreate я делаю следующее:

setContentView(R.layout.main);
TextView boring = (TextView) findViewById(R.id.boring);
// ...find other elements...
MyCustomView foo = (MyCustomView) findViewById(R.id.foo);
if (foo == null) { Log.d(TAG, "epic fail"); }

Остальные элементы найдены успешно, но fooвозвращаются нулевыми. MyCustomView имеет конструктор, MyCustomView(Context c, AttributeSet a)и Log.d(...)в конце этого конструктора успешно отображается в logcat непосредственно перед «эпической ошибкой».

Почему fooноль?

Ответы:


184

Потому что в конструкторе super(context)вместо super(context, attrs).

Имеет смысл, если вы не передадите атрибуты, такие как идентификатор, тогда представление не будет иметь идентификатора и, следовательно, его нельзя будет найти с помощью этого идентификатора. :-)


1
Всегда приятно иметь возможность ответить на свои вопросы :) Не забудьте отметить свой ответ как принятый.
MattC

На самом деле. Сделаю это, когда SO позволит мне («Вы можете принять свой ответ через 2 дня.»)
Крис Бойл,

4
Кроме того , не стоит вам линий , как (MyCustomView) foo = findViewById(R.id.foo);быть MyCustomView foo = (MyCustomView) findViewById(R.id.foo);?
Джереми Логан,

3
Была та же проблема, в моем случае я забыл setContentView () .. XD
Том Брито

На vogella.com есть хороший пример таких действий: vogella.com/articles/AndroidCustomViews/article.html
Wolkenjaeger

27

У меня та же проблема, потому что в моем пользовательском представлении я переопределил конструктор, но вызвал суперконструктор без параметра attrs. Это копипаст)

Моя предыдущая версия конструктора:

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

Теперь у меня есть:

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

И это работает!


Здесь та же проблема. Между прочим, для меня это тоже была ошибка копирования и вставки.
KurtCobain

Со мной случилось то же самое. Самый правильный ответ на Stackoverflow. При добавлении атрибутов AttributeSet обратно все в порядке.
spikeyang

Думаю, мы все просмотрели один и тот же учебник с настраиваемым представлением;) эта ошибка потребовала у меня 0,5 часа бессмысленной отладки ...
KrwawyKefir

У меня это тоже сработало! Я боролся с этим довольно долгое время. Благодарность!
us_david

18

У меня такая же проблема. Моя ошибка заключалась в том, что я написал

        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View layout=inflater.inflate(R.layout.dlg_show_info, null);
        alertDlgShowInfo.setView(layout);
        TableRow trowDesc=(TableRow)findViewById(R.id.trowDesc);

и поскольку я использовал надувной элемент для «загрузки» представления из файла XML, последняя строка была неправильной. Чтобы решить эту проблему, мне пришлось написать:

TableRow trowDesc=(TableRow)layout.findViewById(R.id.trowDesc);

Я написал свое решение на случай, если у кого-то возникнет такая же проблема.


Чувак, ты гений. Это было единственное решение, которое сработало для меня из всех многочисленных решений, которые я читал на SO
Игорь Ганапольский

Для меня это тоже было проблемой. Вау, что за кусок ... Мне понадобилось бы несколько дней, чтобы разобраться в этом самостоятельно. Спасибо!
poshaughnessy

18

Кажется, есть множество причин. Я просто использовал «Очистить ...» в Eclipse, чтобы решить аналогичную проблему. (FindViewByID работал раньше и по какой-то причине начал возвращать null.)


1
очевидно, основная проблема заключается в том, что идентификаторы R.java каким-то образом не работают или, возможно, не обновляются. Я заметил это не только с идентификаторами, но и в других случаях, например, неправильная строка, отображаемая в TextView и т. Д. Я действительно не знаю, почему это происходит.
jellyfish

Это слишком долго мучило меня - чистка действительно исправила это для меня.
Николас МТ Эллиотт

1
Действительно, уборка. Вау, это отстой!
Тим Бют,

11

Та же проблема, но другое решение: я не звонил

setContentView(R.layout.main)

ДО того, как я пытался найти представление, как указано здесь


Я думаю, что это решение, если вы получаете элемент в другом представлении вместо текущего связанного представления.
StarCub

4

Если у вас есть несколько версий макета (в зависимости от плотности экрана, версий SDK), убедитесь, что все они включают искомый элемент.


2

В моем случае findViewById возвращал null, потому что мое пользовательское представление выглядело примерно так в основном XML:

        <com.gerfmarquez.seekbar.VerticalSeekBar  
            android:id="@+id/verticalSeekBar"
            android:layout_width="wrap_content" 
            android:layout_height="fill_parent" 
            />

и я обнаружил, что когда я добавил материал xmlns, он работал так:

        <com.gerfmarquez.seekbar.VerticalSeekBar  
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/verticalSeekBar"
            android:layout_width="wrap_content" 
            android:layout_height="fill_parent" 
            />

2

Убедитесь, что setContentView(R.layout.main)оператор вызывает перед findViewById(...)оператором;


1

Для меня проблема была решена, когда я добавил папку res в источник в пути сборки Java в настройках проекта.


1

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

Я создал собственное представление и добавил его в свой "layout_main.xml"

public class MUIComponent extends SurfaceView implements SurfaceHolder.Callback {
    public MUIComponent (Context context, AttributeSet attrs ) {
        super ( context, attrs );
    }
    // ..
}

И в основном Activity я хотел прикрепить несколько обратных вызовов и получить ссылки на элементы пользовательского интерфейса из XML.

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        MUIInitializer muiInit = new MUIInitializer();
        muiInit.setupCallbacks(this);
        muiInit.intializeFields(this);
    }       
}

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

public class MUIInitializer {

    // ...

    public void setupCallbacks ( Activity mainAct ) {


        // This does NOT work properly
        // - The object instance returned is technically an instance of my "MUICompnent" view
        //   but it is a *different* instance than the instance created and shown in the UI screen
        // - Callbacks never get triggered, changes don't appear on UI, etc.
        MUIComponent badInst = (MUIComponent) mainAct.findViewById(R.id.MUIComponent_TESTSURF);


        // ...
        // This works properly

        LayoutInflater inflater = (LayoutInflater) mainAct.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View inflatedLayout = inflater.inflate ( R.layout.activity_main, null );

        MUIComponent goodInst = (MUIComponent) inflatedLayout.findViewById(R.id.MUIComponent_TESTSURF);


        // Add callbacks
        // ...
    }

}

Разница между badInst и goodInst:

  • badInst использует findViewByID Activity
  • goodInst раздувает макет и использует расширенный макет для поиска

Заметил, что у Винсента такое же решение ... и его ответ короче ... +1 вместо него :)
DevByStarlight

1

Это случилось со мной с пользовательским компонентом для Wear, но это общий совет. Если вы используете заглушку (такую ​​как я WatchViewStub), вы не можете просто переадресовать вызов findViewById()куда угодно. Все, что находится внутри заглушки, должно быть сначала надуто, а не только потом setContentView(). Таким образом, вы должны написать что-то вроде этого, чтобы дождаться, когда это произойдет:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_wear);
    final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
    stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
        @Override
        public void onLayoutInflated(WatchViewStub stub) {
            myCustomViewInst = (MyCustomView) findViewById(R.id.my_custom_view);
            ...

0

Моя проблема была в опечатке. Я написал android.id(точка) вместо android:id. :П

По-видимому, в моем пользовательском компоненте xml нет проверки синтаксиса. :(


0

Была такая же проблема.

У меня была верстка с несколькими детьми. Из конструктора одного из них я пытался получить ссылку (с помощью context.findViewById) на другого ребенка. Это не сработало, потому что второй дочерний элемент был определен в макете.

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

setContentView(R.layout.main);
MyView first = findViewById(R.layout.first_child);
first.setSecondView(findViewById(R.layout.second_child));

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


1
В общем, вы не должны использовать findViewByIdв конструкторе a View, а лучше вставлять код инициализации OnFinishInflate?
Санджай Манохар

0

findViewById()Метод иногда возвращается , nullкогда корень макета не имеет android:idатрибута. Мастер Eclipse для создания XML-файла макета не создает автоматически android:idатрибут для корневого элемента.


0

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

RelativeLayout relLayout = (RelativeLayout) this.getParent();
View view1 = relLayout.findViewById(R.id.id_relative_layout_search);

0

«Чистый» вариант у меня сработал.

В моем случае основная причина заключается в том, что исходный код находится в общем сетевом ресурсе, а моя рабочая станция и файловый сервер не были правильно синхронизированы и смещались на 5 секунд. Метки времени в файлах, созданных Eclipse, остались в прошлом (потому что они назначаются файловым сервером) относительно часов рабочей станции, что приводит к тому, что Eclipse неправильно разрешает зависимости между сгенерированными и исходными файлами. В этом случае кажется, что «чистый» работает, потому что он вызывает полную перестройку вместо инкрементальной сборки, которая зависит от неправильных временных меток.

После того, как я исправил настройки NTP на своей рабочей станции, проблема больше никогда не возникала. Без правильных настроек NTP это будет происходить каждые несколько часов, так как часы быстро уходят.


Следует добавить это к комментарию к ответу выше
Trung Nguyen

0

Чтобы добавить к ответам еще одну банальную ошибку, на которую следует обратить внимание:

Убедитесь, что вы действительно редактируете правильный XML-файл макета ...


0

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.