web-dev-qa-db-fra.com

findViewById () retourne null pour le composant personnalisé dans la présentation XML, pas pour les autres composants

J'ai un res/layout/main.xml Comprenant 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 onCreate de mon activité, 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 foo revient nul. 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 foo null?

90
Chris Boyle

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'id, la vue n'aura pas d'id et ne pourra donc pas être trouvée en utilisant cet id. :-)

174
Chris Boyle

J'ai le même problème parce que dans ma vue personnalisée, j'ai remplacé le constructeur mais j'ai invoqué le super contructor withot attrs paramete. C'est du copier-coller)

Ma version précédente du constructeur:

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

Maintenant j'ai:

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

Et ça marche!

25

J'ai eu le même problème. Mon erreur était la suivante: 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.

18
Vincent

Semble qu'il existe une variété de raisons. Je viens d'utiliser "Clean ..." dans Eclipse pour résoudre un problème similaire. (FindViewByID avait fonctionné auparavant et, pour une raison quelconque, a commencé à retourner null.)

18
jellyfish

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

setContentView(R.layout.main)

AVANT d'avoir essayé de trouver la vue comme indiqué ici

11
Daniel

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

4
M.Q.

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

2
Mason

Dans mon cas, findViewById retournait null parce que ma vue personnalisée ressemblait à quelque chose comme 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 trucs 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
gerfmarquez

Je suis tombé sur le même problème il y a quelque temps lorsque j'ai ajouté une vue personnalisée via la mise en page XML, 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 quelques rappels et obtenir des références aux éléments d'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'étaient tout simplement pas apparaissant dans la demande.

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 findViewByID de l'activité
  • goodInst gonfle la disposition et utilise la disposition gonflée pour faire la recherche
1
DevByStarlight

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 j'utilisais WatchViewStub), vous ne pouvez pas placer l'appel à findViewById() n'importe où. Tout à l'intérieur du stub doit être gonflé en premier, ce qui ne se produit pas juste après setContentView(). Ainsi, vous devriez écrire quelque chose comme ceci afin d'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);
            ...
1
Stephen Wylie

Pour moi, le problème a été résolu lorsque j'ai ajouté le dossier res à la source dans Java Build Path in project Settings.

1
Jiwon Park

Eu 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 disposition.

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.

0
Kangur

L'option "propre" a fonctionné pour moi.

Dans mon cas, la cause principale est que le code source réside sur un partage réseau et que ma station de travail et mon serveur de fichiers n'ont pas été synchronisés correctement et ont dérivé de 5 secondes. Les horodatages des fichiers créés par Eclipse sont du passé (car ils sont attribués par le serveur de fichiers) w.r.t. l'horloge de la station de travail, ce qui empêche Eclipse de résoudre incorrectement 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 génération 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 reproduit. Sans les paramètres appropriés NTP, cela se produirait toutes les quelques heures, car les horloges dérivaient) vite.

0
diter

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

0
Shanij P.S

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

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

0
usagidon

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
Mike6679

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
JOG

La méthode findViewById() renvoie parfois null lorsque la racine de la mise en page n'a pas Android:id attribut. L'assistant Eclipse pour générer le fichier xml de mise en page ne génère pas automatiquement Android:id attribut pour l'élément racine.

0
Santosh