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 ответов:
потому что в конструкторе, у меня было
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" />
для меня проблема была решена, когда я добавил папку 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 это будет происходить каждые несколько часов, так как часы быстро дрейфуют.