Android - Kinder in eine Ansicht bringen?

169

Wie kann ich bei einer gegebenen Ansicht die untergeordneten Ansichten darin erhalten?

Ich habe also eine benutzerdefinierte Ansicht und der Debugger zeigt, dass es unter mChildren7 anderen Ansichten gibt. Ich brauche eine Möglichkeit, auf diese Ansichten zuzugreifen, aber es scheint nicht, dass es eine öffentliche API gibt, um dies zu tun.

irgendwelche Vorschläge?

BEARBEITEN:

Meine benutzerdefinierte Ansicht erbt von AdapterView

aryaxt
quelle

Antworten:

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

Wird das reichen?

Alexander Kulyakhtin
quelle
2
Was ist der beste Weg, dies rekursiv zu tun und dadurch ein JSON-Objekt für die Ansichtserbe zu generieren?
Jimbob
Angenommen, die übergeordnete Ansicht heißtviewGroup
Prime624
57

Wenn Sie nicht nur alle direkten Kinder, sondern auch alle Kinder usw. haben möchten, müssen Sie dies rekursiv tun:

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

Um das Ergebnis zu verwenden, können Sie Folgendes tun:

    // 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
quelle
8
Dieser Aufruf wird nicht gespeichert, da beispielsweise child vom Typ View und nicht von ViewGroup sein kann. Daher kann der rekursive Aufruf von getAllChildren eine Ausnahme verursachen, da die Umwandlung fehlschlägt. Sie sollten also eine if (! (V-Instanz von ViewGroup)) hinzufügen. vor der Besetzung
Phil
2
Kleiner Vorschlag: Ersetzen Sie Ihre for-Schleife durch for (int i = 0, n = vg.getChildCount(); i < n; i++) Auf Android. Der Zugriff auf lokale Variablen anstelle des Aufrufs einer Methode ist in der Regel viel schneller. Auf diese Weise wird eine Methode nur einmal pro Ansichtsgruppe aufgerufen und bei nachfolgenden Läufen wird eine lokale Variable verwendet.
ArtOfWarfare
Ich habe den Code aufgrund des Vorschlags von Phil Diegmann geändert. Hoffe das hilft. Funktioniert bei mir.
IHeartAndroid
1
Cool. Ich würde nur vorschlagen, ein boolesches 'includeViewGroups' hinzuzufügen, und nur wenn es wahr ist, iewArrayList.add (v); Dies ist nützlich für die Iteration in einem Adapter (und ich brauche nur die Blätter). Vielen Dank!
JRun
20

Sie können jederzeit über View.findViewById () auf untergeordnete Ansichten zugreifen ( http://developer.android.com/reference/android/view/View.html#findViewById(int ).

Zum Beispiel innerhalb einer Aktivität / Ansicht:

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

oder wenn Sie einen Verweis auf eine Ansicht haben:

...
private void init(View root) {
  View child2 = root.findViewById(R.id.child2);
}
jonson
quelle
Ich habe keinen Zugriff auf IDs, die Benutzeroberfläche wird über Code generiert, ich schreibe Automatisierung mit Robotium, daher sind die Möglichkeiten, die ich ausführen kann, begrenzt
aryaxt
@jonson, überhaupt nicht nützlich, wenn Sie alle Kinder bekommen müssen.
Pacerier
Auch quora.com/unanswered/…
Pacerier
17

Ich werde diese Antwort nur als Alternative zum rekursiven Algorithmus von @ IHeartAndroid bereitstellen, um alle untergeordneten Elemente Viewin einer Ansichtshierarchie zu ermitteln. Beachten Sie, dass zum Zeitpunkt dieses Schreibens die rekursive Lösung insofern fehlerhaft ist , als sie Duplikate im Ergebnis enthält.

Für diejenigen, die Probleme haben, sich mit Rekursion zu beschäftigen, ist hier eine nicht rekursive Alternative. Sie erhalten Bonuspunkte für diese Realisierung ist auch eine Breiten erste Suche Alternative zum depth-first Ansatz der rekursiven Lösung.

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

Ein paar schnelle Tests (nichts Formales) legen nahe, dass diese Alternative auch schneller ist, obwohl dies höchstwahrscheinlich mit der Anzahl neuer ArrayListInstanzen zu tun hat, die durch die andere Antwort erstellt werden. Die Ergebnisse können auch variieren, je nachdem, wie vertikal / horizontal die Ansichtshierarchie ist.

Cross-posted von: Android | Holen Sie sich alle untergeordneten Elemente einer ViewGroup

MH.
quelle
7

Hier ist ein Vorschlag: Sie können das ID(z. B. von android:id="@+id/..My Str..) erhalten, das unter RVerwendung des angegebenen Namens (z My Str. B. ) generiert wurde . Ein Code-Snippet mit der getIdentifier()Methode wäre dann:

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

Von innen her ein Activity, gekoppelt ein Beispiel für die Verwendung mit findViewByIdwäre:

// 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
quelle
0

Um ein Tabellenlayout (TableLayout) zu aktualisieren, musste ich den oben erwähnten rekursiven Ansatz verwenden, um alle Kinder der Kinder usw. zu erhalten.

Meine Situation war etwas vereinfacht, da ich nur mit LinearLayout arbeiten musste und die daraus erweiterten Klassen wie TableLayout. Und ich war nur daran interessiert, TextView-Kinder zu finden. Aber ich denke, es ist immer noch auf diese Frage anwendbar.

Die letzte Klasse wird als separater Thread ausgeführt, was bedeutet, dass sie im Hintergrund andere Dinge tun kann, bevor sie für die Kinder analysiert. Der Code ist klein und einfach und kann unter github gefunden werden: https://github.com/jkincali/Android-LinearLayout-Parser

jkincali
quelle
0

Diese Methode berücksichtigt alle Ansichten innerhalb eines Layouts. Dies ähnelt der Antwort von Alexander Kulyakhtin. Der Unterschied besteht darin, dass alle Arten von übergeordneten Layouts akzeptiert werden und eine Array-Liste mit Ansichten zurückgegeben wird.

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
quelle