findViewById () restituisce null per il componente personalizzato nel layout XML, non per altri componenti


91

Ho res/layout/main.xmlincluso questi elementi e altri:

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

Nella mia attività onCreate, faccio questo:

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

Gli altri elementi vengono trovati correttamente, ma fooritorna nulli. MyCustomView ha un costruttore MyCustomView(Context c, AttributeSet a)e un Log.d(...)alla fine di quel costruttore appare correttamente in logcat appena prima del "fallimento epico".

Perché è foonullo?

Risposte:


182

Perché nel costruttore, avevo super(context)invece di super(context, attrs).

Ha senso, se non si passano gli attributi, come l'id, la vista non avrà ID e quindi non sarà trovabile utilizzando quell'ID. :-)


1
È sempre bello poter rispondere alle tue domande :) Assicurati di contrassegnare anche la tua come risposta accettata.
MattC

Infatti. Lo farò quando SO me lo permette ("Puoi accettare la tua risposta in 2 giorni.")
Chris Boyle

4
Inoltre, non dovresti linee come (MyCustomView) foo = findViewById(R.id.foo);essere MyCustomView foo = (MyCustomView) findViewById(R.id.foo);?
Jeremy Logan

3
Ho avuto lo stesso problema, nel mio caso avevo dimenticato il setContentView () .. XD
Tom Brito

C'è un bell'esempio per fare queste cose su vogella.com: vogella.com/articles/AndroidCustomViews/article.html
Wolkenjaeger

27

Ho lo stesso problema perché nella mia vista personalizzata ho sovrascritto il costruttore ma ho invocato il supercostruttore senza attrs paramete. Questo è copia incolla)

La mia versione precedente del costruttore:

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

Adesso ho:

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

E funziona!


Stesso problema qui. Per inciso, anche per me è stato un errore di copia incolla.
KurtCobain

La stessa cosa è successa a me. La risposta più corretta su Stackoverflow. Quando aggiungi AttributeSet attira indietro, va tutto bene.
spikeyang

Immagino che abbiamo cercato tutti lo stesso tutorial con la visualizzazione personalizzata;) questo errore mi ha richiesto 0,5 ore di debug inutile ...
KrwawyKefir

Questo ha funzionato anche per me! Ho combattuto questo per un bel po 'di tempo. Grazie!
us_david

18

Ho avuto lo stesso problema. Il mio errore è stato questo: ho scritto

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

e poiché ho usato un inflater per "caricare" la vista da un file XML, l'ultima riga era sbagliata. Per risolverlo, ho dovuto scrivere:

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

Ho scritto la mia soluzione, nel caso qualcuno avesse lo stesso problema.


Amico, sei un genio. Questa è stata l'unica soluzione che ha funzionato per me tra tutte le numerose che ho letto su SO
IgorGanapolsky

Questo è stato il problema anche per me. Wow, che pezzo di ... Mi ci sarebbero voluti giorni per capirlo da solo. Grazie!
poshaughnessy

18

Sembra che ci siano una serie di ragioni. Ho appena usato "Clean ..." in Eclipse per risolvere un problema simile. (FindViewByID aveva funzionato prima e per qualche motivo ha iniziato a restituire null.)


1
apparentemente, il problema di fondo è che gli ID R.java in qualche modo si rompono o forse non vengono aggiornati. L'ho notato non solo con gli ID, ma anche in altri casi, ad esempio una stringa errata visualizzata in un TextView ecc. Non so davvero perché questo accade, però.
medusa

Questo mi ha dato dolore per troppo tempo - una pulizia ha davvero risolto il problema per me.
Nicholas MT Elliott

1
Pulizia, davvero. Wow, che schifo!
Tim Büthe

11

Stesso problema, ma soluzione diversa: non ho chiamato

setContentView(R.layout.main)

PRIMA ho provato a trovare la vista come indicato qui


Penso che questa sia la soluzione se stai ottenendo un elemento su un'altra vista invece della vista correlata corrente.
StarCub

4

Se disponi di più versioni di layout (a seconda delle densità dello schermo, delle versioni SDK) assicurati che tutte includano l'elemento che stai cercando.


2

Nel mio caso findViewById restituiva null perché la mia visualizzazione personalizzata era simile a questa nell'XML principale:

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

e ho scoperto che quando ho aggiunto la roba xmlns ha funzionato in questo modo:

        <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

Assicurati che l' setContentView(R.layout.main)istruzione chiami prima findViewById(...)dell'istruzione;


1

Per me, il problema è stato risolto quando ho aggiunto la cartella res all'origine in Java Build Path nelle impostazioni del progetto.


1

Mi sono imbattuto nello stesso problema qualche tempo fa quando ho aggiunto una vista personalizzata tramite il layout XML e poi ho provato ad allegare una richiamata altrove nell'applicazione ...

Ho creato una visualizzazione personalizzata e l'ho aggiunta al mio "layout_main.xml"

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

E nell'attività principale volevo allegare alcuni callback e ottenere riferimenti agli elementi dell'interfaccia utente dall'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);
    }       
}

L'inizializzatore non stava facendo nulla di stravagante, ma le modifiche che ha cercato di apportare alla visualizzazione personalizzata (MUIComponent) o ad altri elementi dell'interfaccia utente non personalizzati semplicemente non venivano visualizzati nell'applicazione.

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
        // ...
    }

}

La differenza tra "badInst" e "goodInst" è:

  • badInst utilizza findViewByID dell'attività
  • goodInst gonfia il layout e utilizza il layout gonfiato per eseguire la ricerca

Ho notato che Vincent ha la stessa soluzione ... e la sua risposta è più breve ... +1 sua invece :)
DevByStarlight

1

Questo è successo a me con un componente personalizzato per Wear, ma è un consiglio generico. Se stai usando uno stub (come lo stavo usando io WatchViewStub), non puoi semplicemente mettere la chiamata findViewById()ovunque. Tutto ciò che si trova all'interno dello stub deve essere gonfiato per primo, cosa che non accade solo dopo setContentView(). Quindi, dovresti scrivere qualcosa del genere per aspettare che ciò accada:

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

Il mio problema era un errore di battitura. Avevo scritto android.id(punto) invece di android:id. : P

Apparentemente non esiste un controllo della sintassi all'interno del mio componente xml personalizzato. :(


0

Ho avuto lo stesso problema.

Ho avuto layout con pochi bambini. Dal costruttore di uno di essi stavo cercando di ottenere un riferimento (utilizzando context.findViewById) ad un altro bambino. Non funzionava perché il secondo figlio era definito ulteriormente nel layout.

L'ho risolto in questo modo:

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

Funzionerebbe anche se l'ordine dei bambini fosse opposto, ma immagino che generalmente dovrebbe essere fatto come sopra.


1
In generale non dovresti usare findViewByIdnel costruttore di a View, ma piuttosto inserire il codice di inizializzazione OnFinishInflate?
Sanjay Manohar

0

Il findViewById()metodo a volte ritorna nullquando la radice del layout non ha android:idattributi. La procedura guidata Eclipse per la generazione del file xml di layout non genera automaticamente l' android:idattributo per l'elemento root.


0

Nel mio caso la vista era nella vista genitore NON nella vista in cui stavo cercando di richiamarla. Quindi nella vista figlio ho dovuto chiamare:

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

0

L'opzione "pulita" ha funzionato per me.

Nel mio caso, la causa principale è che il codice sorgente risiede su una condivisione di rete e la mia workstation e il file server non erano sincronizzati correttamente e si erano spostati di 5 secondi. I timestamp sui file creati da Eclipse sono nel passato (perché sono assegnati dal file server) rispetto all'orologio della workstation, facendo sì che Eclipse risolva le dipendenze tra i file generati e quelli di origine in modo errato. In questo caso, un "clean" sembra funzionare, perché forza una ricostruzione completa invece di una build incrementale che dipende da timestamp errati.

Dopo aver corretto le impostazioni NTP sulla mia workstation, il problema non si è più verificato. Senza adeguate impostazioni NTP, accadrebbe ogni poche ore, poiché gli orologi vanno alla deriva velocemente.


Dovrebbe aggiungere questo al commento della risposta sopra
Trung Nguyen

0

Per aggiungere un altro banale errore alle risposte a cui prestare attenzione:

Verifica di aver effettivamente modificato il file XML di layout corretto ...


0

ho avuto lo stesso problema perché ho dimenticato di aggiornare l'ID di visualizzazione in tutte le mie cartelle di layout.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.