it-swarm.com.ru

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

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

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

В моей Активности 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 возвращается null. MyCustomView имеет конструктор MyCustomView(Context c, AttributeSet a) и Log.d(...) в конце этого конструктора успешно отображается в logcat как раз перед "эпическим сбоем".

Почему foo пусто?

88
Chris Boyle

Потому что в конструкторе у меня была super(context) вместо super(context, attrs).

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

172
Chris Boyle

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

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

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

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

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

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

22
Alexander Korolchuk

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

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

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

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

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

18
Vincent

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

18
jellyfish

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

setContentView(R.layout.main)

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

11
Daniel

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

4
M.Q.

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

2
Mason

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

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

Я создал собственный вид и добавил его в свой файл layout_main.xml.

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

И в основной Деятельности я хотел прикрепить несколько обратных вызовов и получить ссылки на элементы пользовательского интерфейса из 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 Деятельности
  • goodInst раздувает макет и использует накачанный макет для поиска
1
DevByStarlight

Это случилось со мной с пользовательским компонентом для 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);
            ...
1
Stephen Wylie

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

1
Jiwon Park

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

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

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

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

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

0
Kangur

"Чистый" вариант работал для меня.

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

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

0
diter

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

0
Shanij P.S

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

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

0
usagidon

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

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

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

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

0
JOG

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

0
Santosh