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 непосредственно перед "epic fail".

почему foo null?

18 85

18 ответов:

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

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

у меня такая же проблема, потому что в моем пользовательском представлении я переопределил конструктор, но вызвал super contructor without attrs paramete. То есть копировать вставить)

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

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

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

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

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

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

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

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

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

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

setContentView(R.layout.main)

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

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

в моем случае 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" 
            />

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

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

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

Это случилось со мной с пользовательским компонентом для износа, но это общий совет. Если вы используете заглушку (например, я использую 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);
            ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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