findViewById () gibt null für benutzerdefinierte Komponenten in Layout-XML zurück, nicht für andere Komponenten

91

Ich habe res/layout/main.xmldiese und andere Elemente eingeschlossen:

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

In onCreate meiner Aktivität mache ich Folgendes:

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

Die anderen Elemente wurden erfolgreich gefunden, kommen jedoch fooauf null zurück. MyCustomView hat einen Konstruktor MyCustomView(Context c, AttributeSet a)und ein Log.d(...)am Ende dieses Konstruktors wird erfolgreich in logcat angezeigt, kurz bevor der "epische Fehler" auftritt.

Warum ist foonull?

Chris Boyle
quelle

Antworten:

182

Denn im Konstruktor hatte ich super(context)statt super(context, attrs).

Sinnvoll, wenn Sie die Attribute wie die ID nicht übergeben, hat die Ansicht keine ID und kann daher mit dieser ID nicht gefunden werden. :-)

Chris Boyle
quelle
1
Immer schön, wenn Sie Ihre eigenen Fragen beantworten können :) Markieren Sie auch Ihre als akzeptierte Antwort.
MattC
Tatsächlich. Wird dies tun, wenn SO es mir erlaubt ("Sie können Ihre eigene Antwort in 2 Tagen akzeptieren.")
Chris Boyle
4
Auch sollte nicht Sie Zeilen wie (MyCustomView) foo = findViewById(R.id.foo);sein MyCustomView foo = (MyCustomView) findViewById(R.id.foo);?
Jeremy Logan
3
Hatte das gleiche Problem, in meinem Fall hatte ich das setContentView () vergessen .. XD
Tom Brito
Es gibt ein schönes Beispiel dafür auf vogella.com: vogella.com/articles/AndroidCustomViews/article.html
Wolkenjaeger
27

Ich habe das gleiche Problem, weil ich in meiner benutzerdefinierten Ansicht den Konstruktor überschrieben, aber den Superkonstruktor ohne attrs-Parameter aufgerufen habe. Das ist Kopieren Einfügen)

Meine vorherige Konstruktorversion:

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

Jetzt habe ich:

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

Und das funktioniert!

Alexander Korolchuk
quelle
Gleiches Problem hier. Übrigens war es auch für mich ein Copy Paste Fehler.
KurtCobain
Das gleiche ist mir passiert. Die richtigste Antwort auf Stackoverflow. Wenn Sie AttributeSet-Attribute zurück hinzufügen, ist alles in Ordnung.
Spikeyang
Ich denke, wir haben alle das gleiche Tutorial mit benutzerdefinierter Ansicht nachgeschlagen;) Dieser Fehler hat mich 0,5 Stunden sinnloses Debuggen
gekostet
Das hat auch bei mir funktioniert! Ich habe lange damit gekämpft. Vielen Dank!
us_david
18

Ich hatte das gleiche Problem. Mein Fehler war: Ich schrieb

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

und da ich einen Inflater verwendet habe, um die Ansicht aus einer XML-Datei zu "laden", war die letzte Zeile falsch. Um es zu lösen, musste ich schreiben:

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

Ich habe meine Lösung geschrieben, falls jemand das gleiche Problem hat.

Vincent
quelle
Alter, du bist ein Genie. Das war die einzige Lösung, die für mich von all den zahlreichen, die ich auf SO gelesen habe,
funktioniert hat
Das war auch für mich das Problem. Wow, was für ein Stück ... Ich hätte Tage gebraucht, um das selbst herauszufinden. Danke dir!
poshaughnessy
18

Es scheint eine Vielzahl von Gründen zu geben. Ich habe gerade "Clean ..." in Eclipse verwendet, um ein ähnliches Problem zu lösen. (FindViewByID hatte zuvor funktioniert und aus irgendeinem Grund null zurückgegeben.)

Qualle
quelle
1
Anscheinend besteht das zugrunde liegende Problem darin, dass die R.java-IDs irgendwie kaputt gehen oder möglicherweise nicht aktualisiert werden. Ich habe dies nicht nur bei den IDs bemerkt, sondern auch in anderen Fällen, z. B. bei einer falschen Zeichenfolge, die in einer Textansicht angezeigt wird usw. Ich weiß jedoch nicht genau, warum dies geschieht.
Quallen
Das bereitete mir viel zu lange Kummer - eine Reinigung hat es tatsächlich für mich behoben.
Nicholas MT Elliott
1
Aufräumen in der Tat. Wow, das ist scheiße!
Tim Büthe
11

Gleiches Problem, aber andere Lösung: Ich habe nicht angerufen

setContentView(R.layout.main)

BEVOR ich versucht habe, die hier angegebene Ansicht zu finden

Daniel
quelle
Ich denke, dies ist die Lösung, wenn Sie ein Element in einer anderen Ansicht anstelle der aktuellen verwandten Ansicht erhalten.
StarCub
4

Wenn Sie mehrere Layoutversionen haben (abhängig von der Bildschirmdichte, SDK-Versionen), stellen Sie sicher, dass alle das gesuchte Element enthalten.

MQ
quelle
2

In meinem Fall gab findViewById null zurück, da meine benutzerdefinierte Ansicht im Haupt-XML ungefähr so ​​aussah:

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

und ich fand heraus, dass es beim Hinzufügen des XML-Materials so funktionierte:

        <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" 
            />
gerfmarquez
quelle
2

Stellen Sie sicher, dass die setContentView(R.layout.main)Anweisung vor der Anweisung aufgerufen wird findViewById(...).

Mason
quelle
1

Für mich wurde das Problem gelöst, als ich den Ordner res in den Projekteinstellungen zur Quelle im Java-Erstellungspfad hinzufügte.

Jiwon Park
quelle
1

Ich bin vor einiger Zeit auf dasselbe Problem gestoßen, als ich eine benutzerdefinierte Ansicht über das Layout-XML hinzugefügt und dann versucht habe, einen Rückruf an eine andere Stelle in der Anwendung anzuhängen ...

Ich habe eine benutzerdefinierte Ansicht erstellt und sie meiner "layout_main.xml" hinzugefügt.

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

Und in der Hauptaktivität wollte ich einige Rückrufe anhängen und Verweise auf die UI-Elemente aus dem XML erhalten.

public class MainActivity extends Activity {

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

        // ...

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

Der Initilizer hat nichts Besonderes getan, aber alle Änderungen, die er an der benutzerdefinierten Ansicht (MUIComponent) oder anderen nicht benutzerdefinierten UI-Elementen vornehmen wollte, wurden in der Anwendung einfach nicht angezeigt .

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

}

Der Unterschied zwischen "badInst" und "goodInst" ist:

  • badInst verwendet die findViewByID der Aktivität
  • goodInst bläst das Layout auf und verwendet das aufgeblasene Layout, um die Suche durchzuführen
DevByStarlight
quelle
Bemerkt Vincent hat die gleiche Lösung ... und seine Antwort ist kürzer ... +1 seine stattdessen :)
DevByStarlight
1

Dies ist mir mit einer benutzerdefinierten Komponente für Wear passiert, ist aber ein allgemeiner Ratschlag. Wenn Sie einen Stub verwenden (wie ich ihn verwendet habe WatchViewStub), können Sie den Anruf nicht einfach an eine findViewById()beliebige Stelle richten. Alles im Stummel muss zuerst aufgeblasen werden, was nicht erst danach passiert setContentView(). Sie sollten also so etwas schreiben, um darauf zu warten:

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);
            ...
Stephen Wylie
quelle
0

Mein Problem war ein Tippfehler. Ich hatte statt android.id(Punkt) geschrieben android:id. : P.

Anscheinend gibt es keine Syntaxprüfung in meiner benutzerdefinierten Komponente xml. :(

JOGGEN
quelle
0

Hatte das gleiche Problem.

Ich hatte Layout mit wenigen Kindern. Vom Konstruktor eines von ihnen habe ich versucht, einen Verweis (unter Verwendung von context.findViewById) auf ein anderes Kind zu erhalten. Es hat nicht funktioniert, weil das zweite Kind im Layout weiter definiert wurde.

Ich habe es so gelöst:

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

Es würde auch funktionieren, wenn die Reihenfolge der Kinder entgegengesetzt wäre, aber ich denke, es sollte im Allgemeinen wie oben gemacht werden.

Kangur
quelle
1
Im Allgemeinen sollten Sie nicht findViewByIdim Konstruktor von a verwenden View, sondern den Initialisierungscode eingeben OnFinishInflate?
Sanjay Manohar
0

Die findViewById()Methode wird manchmal zurückgegeben, nullwenn der Stamm des Layouts kein android:idAttribut hat. Der Eclipse-Assistent zum Generieren einer Layout-XML-Datei generiert nicht automatisch ein android:idAttribut für das Stammelement.

Santosh
quelle
0

In meinem Fall befand sich die Ansicht im übergeordneten Element NICHT in der Ansicht, in der ich sie aufrufen wollte. In der untergeordneten Ansicht musste ich also Folgendes aufrufen:

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

Die Option "sauber" hat bei mir funktioniert.

In meinem Fall liegt die Hauptursache darin, dass sich der Quellcode auf einer Netzwerkfreigabe befindet und meine Workstation und mein Dateiserver nicht korrekt synchronisiert wurden und um 5 Sekunden verschoben wurden. Die Zeitstempel für die von Eclipse erstellten Dateien liegen in der Vergangenheit (da sie vom Dateiserver zugewiesen werden) für die Uhr der Workstation, sodass Eclipse die Abhängigkeiten zwischen generierten und Quelldateien falsch auflöst. In diesem Fall scheint eine 'Bereinigung' zu funktionieren, da sie eine vollständige Neuerstellung anstelle einer inkrementellen Erstellung erzwingt, die von falschen Zeitstempeln abhängt.

Nachdem ich die NTP-Einstellungen auf meiner Workstation korrigiert hatte, trat das Problem nie wieder auf. Ohne die richtigen NTP-Einstellungen würde dies alle paar Stunden passieren, da die Uhren schnell driften.

Diter
quelle
Sollte dies zum Kommentar der obigen Antwort hinzufügen
Trung Nguyen
0

So fügen Sie den Antworten einen weiteren trivialen Fehler hinzu:

Überprüfen Sie, ob Sie tatsächlich die richtige Layout-XML-Datei bearbeiten ...

blub
quelle
0

Ich hatte das gleiche Problem, weil ich vergessen habe, die Ansichts-ID in allen meinen Layout-Ordnern zu aktualisieren.

Shanij PS
quelle