findViewById () renvoie null pour le composant personnalisé dans le format XML, pas pour les autres composants


91

J'ai un res/layout/main.xmlincluant ces éléments et d'autres:

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

Dans mon activité onCreate, je fais ceci:

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

Les autres éléments sont trouvés avec succès, mais foorevient null. MyCustomView a un constructeur MyCustomView(Context c, AttributeSet a)et un Log.d(...)à la fin de ce constructeur apparaît avec succès dans logcat juste avant "l'échec épique".

Pourquoi est foonul?

Réponses:


182

Parce que dans le constructeur, j'avais super(context)au lieu de super(context, attrs).

Cela a du sens, si vous ne transmettez pas les attributs, tels que l'identifiant, alors la vue n'aura pas d'identifiant et ne pourra donc pas être trouvée en utilisant cet identifiant. :-)


1
Toujours agréable de pouvoir répondre à vos propres questions :) Assurez-vous également de marquer la vôtre comme la réponse acceptée.
MattC

En effet. Je le ferai quand SO me le permettra ("Vous pouvez accepter votre propre réponse dans 2 jours.")
Chris Boyle

4
Aussi, ne devriez-vous pas des lignes comme l' (MyCustomView) foo = findViewById(R.id.foo);être MyCustomView foo = (MyCustomView) findViewById(R.id.foo);?
Jeremy Logan

3
J'ai eu le même problème, dans mon cas j'avais oublié le setContentView () .. XD
Tom Brito

Il y a un bel exemple pour faire de telles choses sur vogella.com: vogella.com/articles/AndroidCustomViews/article.html
Wolkenjaeger

27

J'ai le même problème car dans ma vue personnalisée, j'ai remplacé le constructeur mais j'ai appelé le super constructeur sans paramètre attrs. C'est du copier-coller)

Ma précédente version constructeur:

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

Maintenant j'ai:

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

Et ça marche!


Même problème ici. Incidemment, c'était une erreur de copier-coller pour moi aussi.
KurtCobain

Il m'est arrivé la même chose. La réponse la plus correcte sur Stackoverflow. Lorsque vous ajoutez AttributeSet, tout va bien.
spikeyang

Je suppose que nous avons tous recherché le même tutoriel avec vue personnalisée;) cette erreur m'a pris 0,5h de débogage inutile ...
KrwawyKefir

Cela a fonctionné pour moi aussi! J'ai combattu cela pendant un certain temps. Merci!
us_david

18

J'ai eu le même problème. Mon erreur a été que: j'ai écrit

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

et comme j'ai utilisé un gonfleur pour "charger" la vue à partir d'un fichier XML, la dernière ligne était fausse. Pour le résoudre, j'ai dû écrire:

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

J'ai écrit ma solution, au cas où quelqu'un aurait le même problème.


Mec, tu es un génie. C'est la seule solution qui a fonctionné pour moi parmi toutes les nombreuses que j'ai lues sur SO
IgorGanapolsky

C'était aussi le problème pour moi. Wow, quel morceau de ... Cela m'aurait pris des jours pour comprendre ça par moi-même. Je vous remercie!
poshaughnessy

18

Il semble qu'il y ait une variété de raisons. Je viens d'utiliser "Clean ..." dans Eclipse pour résoudre un problème similaire. (FindViewByID avait déjà fonctionné et, pour une raison quelconque, a commencé à renvoyer null.)


1
apparemment, le problème sous-jacent est que les ID de R.java sont en quelque sorte cassés ou peuvent ne pas être mis à jour. J'ai remarqué cela non seulement avec les identifiants, mais aussi dans d'autres cas, par exemple une mauvaise chaîne affichée dans un TextView, etc. Je ne sais pas vraiment pourquoi cela se produit, cependant.
méduse

Cela m'a donné du chagrin pendant trop longtemps - un nettoyage m'a en effet résolu.
Nicholas MT Elliott

1
Nettoyage, en effet. Wow, ça craint!
Tim Büthe

11

Même problème, mais solution différente: je n'ai pas appelé

setContentView(R.layout.main)

AVANT j'ai essayé de trouver la vue comme indiqué ici


Je pense que c'est la solution si vous obtenez un élément sur une autre vue au lieu de la vue associée actuelle.
StarCub

4

Si vous avez plusieurs versions de mise en page (en fonction de la densité de l'écran, des versions du SDK), assurez-vous qu'elles incluent toutes l'élément que vous recherchez.


2

Dans mon cas, findViewById retournait null car ma vue personnalisée ressemblait à ceci dans le XML principal:

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

et j'ai découvert que lorsque j'ai ajouté les éléments xmlns, cela fonctionnait comme ceci:

        <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

Assurez-vous que l' setContentView(R.layout.main)instruction appelle avant l' findViewById(...)instruction;


1

Pour moi, le problème a été résolu lorsque j'ai ajouté le dossier res à la source dans le chemin de construction Java dans les paramètres du projet.


1

J'ai rencontré le même problème il y a quelque temps lorsque j'ai ajouté une vue personnalisée via le XML de mise en page, puis j'ai essayé de joindre un rappel ailleurs dans l'application ...

J'ai créé une vue personnalisée et l'ai ajoutée à mon "layout_main.xml"

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

Et dans l'activité principale, je voulais attacher des rappels et obtenir des références aux éléments de l'interface utilisateur à partir du 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'initiateur ne faisait rien d'extraordinaire, mais les modifications qu'il tentait d'apporter à la vue personnalisée (MUIComponent) ou à d'autres éléments d'interface utilisateur non personnalisés n'apparaissaient tout simplement pas dans l'application.

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 différence entre "badInst" et "goodInst" est:

  • badInst utilise le findViewByID de l'activité
  • goodInst gonfle la mise en page et utilise la mise en page gonflée pour faire la recherche

Remarqué que Vincent a la même solution ... et sa réponse est plus courte ... +1 sienne à la place :)
DevByStarlight

1

Cela m'est arrivé avec un composant personnalisé pour Wear, mais c'est un conseil générique. Si vous utilisez un stub (tel que celui que j'utilisais WatchViewStub), vous ne pouvez pas simplement mettre l'appel findViewById()n'importe où. Tout ce qui se trouve à l'intérieur du talon doit être gonflé en premier, ce qui ne se produit pas seulement après setContentView(). Ainsi, vous devriez écrire quelque chose comme ceci pour attendre que cela se produise:

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

Mon problème était une faute de frappe. J'avais écrit android.id(point) au lieu de android:id. : P

Apparemment, il n'y a pas de vérification de syntaxe dans mon xml de composant personnalisé. :(


0

Avait le même problème.

J'avais une mise en page avec peu d'enfants. Du constructeur de l'un d'eux, j'essayais d'obtenir une référence (en utilisant context.findViewById) à un autre enfant. Cela ne fonctionnait pas car le deuxième enfant était défini plus en détail dans la mise en page.

Je l'ai résolu comme ceci:

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

Cela fonctionnerait aussi si l'ordre des enfants était opposé, mais je suppose que cela devrait généralement être fait comme ci-dessus.


1
En général, vous ne devriez pas utiliser findViewByIddans le constructeur de a View, mais plutôt mettre le code d'initialisation OnFinishInflate?
Sanjay Manohar

0

La findViewById()méthode retourne parfois nulllorsque la racine du layout n'a pas d' android:idattribut. L'assistant Eclipse pour générer un fichier xml de mise en page ne génère pas automatiquement d' android:idattribut pour l'élément racine.


0

Dans mon cas, la vue était dans le parent PAS dans la vue dans laquelle j'essayais de l'appeler. Donc, dans la vue enfant, j'ai dû appeler:

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

0

L'option «propre» a fonctionné pour moi.

Dans mon cas, la cause première est que le code source réside sur un partage réseau et que mon poste de travail et mon serveur de fichiers n'étaient pas synchronisés correctement et avaient dérivé de 5 secondes. Les horodatages des fichiers créés par Eclipse sont dans le passé (car ils sont attribués par le serveur de fichiers) par rapport à l'horloge du poste de travail, ce qui oblige Eclipse à résoudre de manière incorrecte les dépendances entre les fichiers générés et les fichiers source. Dans ce cas, un «nettoyage» semble fonctionner, car il force une reconstruction complète au lieu d'une construction incrémentielle qui dépend de mauvais horodatages.

Une fois que j'ai corrigé les paramètres NTP sur mon poste de travail, le problème ne s'est plus jamais produit. Sans les paramètres NTP appropriés, cela se produirait toutes les quelques heures, car les horloges dérivent rapidement.


Devrait ajouter ceci au commentaire de la réponse ci
Trung Nguyen

0

Pour ajouter une autre erreur triviale aux réponses à rechercher:

Vérifiez que vous modifiez réellement le bon fichier XML de mise en page ...


0

J'ai eu le même problème parce que j'ai oublié de mettre à jour l'identifiant de vue dans tous mes dossiers de mise en page.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.