Android - faire entrer les enfants dans une vue?

169

Étant donné une vue, comment puis-je obtenir les vues enfants à l'intérieur?

J'ai donc une vue personnalisée et le débogueur montre que sous mChildrenil y a 7 autres vues. J'ai besoin d'un moyen d'accéder à ces vues, mais il ne semble pas qu'il existe une API publique pour le faire.

Aucune suggestion?

ÉDITER:

Ma vue personnalisée hérite de AdapterView

aryaxt
la source

Réponses:

313
for(int index = 0; index < ((ViewGroup) viewGroup).getChildCount(); index++) {
    View nextChild = ((ViewGroup) viewGroup).getChildAt(index);
}

Cela fera-t-il?

Alexandre Kulyakhtin
la source
2
Quelle est la meilleure façon de le faire de manière récursive et de générer ainsi un objet JSON de hiérarchie de vue?
jimbob
En supposant que la vue parent soit nomméeviewGroup
Prime624
57

Si vous voulez non seulement obtenir tous les enfants directs, mais tous les enfants des enfants et ainsi de suite, vous devez le faire de manière récursive:

private ArrayList<View> getAllChildren(View v) {

    if (!(v instanceof ViewGroup)) {
        ArrayList<View> viewArrayList = new ArrayList<View>();
        viewArrayList.add(v);
        return viewArrayList;
    }

    ArrayList<View> result = new ArrayList<View>();

    ViewGroup vg = (ViewGroup) v;
    for (int i = 0; i < vg.getChildCount(); i++) {

        View child = vg.getChildAt(i);

        ArrayList<View> viewArrayList = new ArrayList<View>();
        viewArrayList.add(v);
        viewArrayList.addAll(getAllChildren(child));

        result.addAll(viewArrayList);
    }
    return result;
}

Pour utiliser le résultat, vous pouvez faire quelque chose comme ceci:

    // check if a child is set to a specific String
    View myTopView;
    String toSearchFor = "Search me";
    boolean found = false;
    ArrayList<View> allViewsWithinMyTopView = getAllChildren(myTopView);
    for (View child : allViewsWithinMyTopView) {
        if (child instanceof TextView) {
            TextView childTextView = (TextView) child;
            if (TextUtils.equals(childTextView.getText().toString(), toSearchFor)) {
                found = true;
            }
        }
    }
    if (!found) {
        fail("Text '" + toSearchFor + "' not found within TopView");
    }
IHeartAndroid
la source
8
Cet appel n'est pas sauvegardé, car par exemple child peut être de type View et non de ViewGroup, donc l'appel récursif de getAllChildren pourrait provoquer une exception, car le cast échoue. Vous devez donc ajouter un if (! (V instanceof ViewGroup)) return; avant le casting
Phil
2
Petite suggestion: remplacez votre boucle for par for (int i = 0, n = vg.getChildCount(); i < n; i++) Sur Android, accéder aux variables locales au lieu d'appeler une méthode a tendance à être beaucoup plus rapide. De cette façon, une méthode n'est appelée qu'une seule fois par groupe de vues, puis lors des exécutions suivantes, une variable locale est utilisée.
ArtOfWarfare
J'ai changé le code à cause de la suggestion de Phil Diegmann. J'espère que cela t'aides. Travaille pour moi.
IHeartAndroid
1
Cool. Je suggérerais seulement d'ajouter un booléen 'includeViewGroups' et seulement si c'est vrai, iewArrayList.add (v); Ceci est utile pour itérer dans un adaptateur (et je n'ai besoin que des feuilles). Merci!
JRun
20

Vous pouvez toujours accéder aux vues enfants via View.findViewById () http://developer.android.com/reference/android/view/View.html#findViewById(int ).

Par exemple, dans une activité / vue:

...
private void init() {
  View child1 = findViewById(R.id.child1);
}
...

ou si vous avez une référence à une vue:

...
private void init(View root) {
  View child2 = root.findViewById(R.id.child2);
}
Jonon
la source
Je n'ai pas accès aux identifiants, l'interface utilisateur est générée via le code, j'écris l'automatisation à l'aide de robotium, donc les choses que je peux faire sont limitées
aryaxt
@jonson, pas du tout utile si vous avez besoin de tous les enfants.
Pacerier
Aussi quora.com/unanswered/…
Pacerier
17

Je vais juste fournir cette réponse comme une alternative à l'algorithme récursif de @ IHeartAndroid pour découvrir tous les enfants Viewdans une hiérarchie de vues. Notez qu'au moment de la rédaction de cet article, la solution récursive est imparfaite en ce qu'elle contiendra des doublons dans son résultat.

Pour ceux qui ont du mal à comprendre la récursivité, voici une alternative non récursive. Vous obtenez des points bonus pour la réalisation de c'est aussi une largeur d'abord la recherche alternative à la profondeur première approche de la solution récursive.

private List<View> getAllChildrenBFS(View v) {
    List<View> visited = new ArrayList<View>();
    List<View> unvisited = new ArrayList<View>();
    unvisited.add(v);

    while (!unvisited.isEmpty()) {
        View child = unvisited.remove(0);
        visited.add(child);
        if (!(child instanceof ViewGroup)) continue;
        ViewGroup group = (ViewGroup) child;
        final int childCount = group.getChildCount();
        for (int i=0; i<childCount; i++) unvisited.add(group.getChildAt(i));
    }

    return visited;
}

Quelques tests rapides (rien de formel) suggèrent que cette alternative est également plus rapide, bien que cela ait probablement à voir avec le nombre de nouvelles ArrayListinstances créées par l'autre réponse. En outre, les résultats peuvent varier en fonction du niveau vertical / horizontal de la hiérarchie des vues.

Posté par: Android | Obtenir tous les éléments enfants d'un ViewGroup

MH.
la source
7

Voici une suggestion: vous pouvez obtenir le ID(spécifié par exemple par android:id="@+id/..My Str..) qui a été généré Ren utilisant son nom donné (par exemple My Str). Un extrait de code utilisant la getIdentifier()méthode serait alors:

public int getIdAssignedByR(Context pContext, String pIdString)
{
    // Get the Context's Resources and Package Name
    Resources resources = pContext.getResources();
    String packageName  = pContext.getPackageName();

    // Determine the result and return it
    int result = resources.getIdentifier(pIdString, "id", packageName);
    return result;
}

À partir d'un Activity, un exemple d'utilisation associé à findViewByIdserait:

// Get the View (e.g. a TextView) which has the Layout ID of "UserInput"
int rID = getIdAssignedByR(this, "UserInput")
TextView userTextView = (TextView) findViewById(rID);
vanangelov
la source
3

En guise de mise à jour pour ceux qui rencontrent cette question après 2018, si vous utilisez Kotlin, vous pouvez simplement utiliser la propriété d'extension Android KTX ViewGroup.children pour obtenir une séquence des enfants immédiats de View.

Chuck Stein
la source
0

Afin de rafraîchir une disposition de table (TableLayout), j'ai fini par devoir utiliser l'approche récursive mentionnée ci-dessus pour obtenir tous les enfants des enfants et ainsi de suite.

Ma situation a été quelque peu simplifiée car je n'avais besoin que de travailler avec LinearLayout et les classes qui en étaient étendues telles que TableLayout. Et je n'étais intéressé que par la recherche d'enfants TextView. Mais je pense que cela s'applique toujours à cette question.

La classe finale s'exécute en tant que thread séparé, ce qui signifie qu'elle peut faire d'autres choses en arrière-plan avant d'analyser les enfants. Le code est petit et simple et peut être trouvé sur github: https://github.com/jkincali/Android-LinearLayout-Parser

jkincali
la source
0

Cette méthode prend toutes les vues à l'intérieur d'une mise en page, ceci est similaire à la réponse d'Alexander Kulyakhtin. La différence est qu'il accepte tout type de mise en page parente et renvoie une liste de tableaux de vues.

public List<View> getAllViews(ViewGroup layout){
        List<View> views = new ArrayList<>();
        for(int i =0; i< layout.getChildCount(); i++){
            views.add(layout.getChildAt(i));
        }
        return views;
}
NJY404
la source