Android - umieścić dzieci w widoku?

169

Biorąc pod uwagę widok, jak mogę uzyskać w nim widoki dziecka?

Mam więc niestandardowy widok, a debugger pokazuje, że pod mChildrenjest 7 innych widoków. Potrzebuję sposobu, aby uzyskać dostęp do tych widoków, ale wygląda na to, że nie istnieje publiczny interfejs API, który to umożliwi.

jakieś sugestie?

EDYTOWAĆ:

Mój widok niestandardowy dziedziczy z AdapterView

aryaxt
źródło

Odpowiedzi:

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

Czy to wystarczy?

Alexander Kulyakhtin
źródło
2
Jaki jest najlepszy sposób, aby to zrobić rekurencyjnie, a tym samym wygenerować obiekt JSON hierarchii widoku?
jimbob
Zakładając, że widok nadrzędny nosi nazwęviewGroup
Prime624
57

Jeśli chcesz nie tylko uzyskać wszystkie bezpośrednie dzieci, ale wszystkie dzieci dzieci i tak dalej, musisz to zrobić rekurencyjnie:

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

Aby użyć wyniku, możesz zrobić coś takiego:

    // 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
źródło
8
To wywołanie nie jest zapisywane, ponieważ np. Child może być typu View, a nie ViewGroup, więc rekurencyjne wywołanie getAllChildren może spowodować wyjątek, ponieważ rzutowanie nie powiedzie się. Więc powinieneś dodać if (! (V instancja ViewGroup)) return; przed obsadą
Phil
2
Mała sugestia: zamień pętlę for na for (int i = 0, n = vg.getChildCount(); i < n; i++) W systemie Android dostęp do zmiennych lokalnych zamiast wywoływania metody jest znacznie szybszy. W ten sposób metoda jest wywoływana tylko raz na grupę widoków, a następnie przy kolejnych uruchomieniach używana jest zmienna lokalna.
ArtOfWarfare
Zmieniłem kod z powodu sugestii Phila Diegmanna. Mam nadzieję że to pomoże. Pracuje dla mnie.
IHeartAndroid
1
Chłodny. Sugerowałbym tylko dodanie wartości logicznej „includeViewGroups” i tylko wtedy, gdy jest to prawda, iewArrayList.add (v); Jest to przydatne do iteracji w adapterze (potrzebuję tylko listków). Dzięki!
JRun
20

Zawsze możesz uzyskać dostęp do widoków podrzędnych za pośrednictwem View.findViewById () http://developer.android.com/reference/android/view/View.html#findViewById(int ).

Na przykład w ramach działania / widoku:

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

lub jeśli masz odniesienie do widoku:

...
private void init(View root) {
  View child2 = root.findViewById(R.id.child2);
}
jonson
źródło
Nie mam dostępu do identyfikatorów, interfejs użytkownika jest generowany za pomocą kodu, piszę automatyzację za pomocą robotium, więc rzeczy, które mogę zrobić, są ograniczone
aryaxt
@jonson, w ogóle nieprzydatne, jeśli chcesz zdobyć wszystkie dzieci.
Pacerier
Również quora.com/unansovered/…
Pacerier
17

Podam tę odpowiedź jako alternatywę rekurencyjnego algorytmu @ IHeartAndroid do znajdowania wszystkich dzieci Vieww hierarchii widoków. Zauważ, że w chwili pisania tego rozwiązania rekurencyjne rozwiązanie jest błędne , ponieważ zawiera w swoim wyniku duplikaty.

Dla tych, którzy mają problemy z owinięciem głowy wokół rekurencji, oto nierekurencyjna alternatywa. Otrzymujesz dodatkowe punkty za uświadomienie sobie, że jest to również alternatywa przeszukiwania wszerz w stosunku do podejścia opartego na głębokości w rozwiązaniu rekurencyjnym.

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

Kilka szybkich testów (nic formalnego) sugeruje, że ta alternatywa jest również szybsza, chociaż najprawdopodobniej ma to związek z liczbą nowych ArrayListinstancji, które tworzy druga odpowiedź. Ponadto wyniki mogą się różnić w zależności od tego, jak pionowa / pozioma jest hierarchia widoków.

Przeksięgowano z: Android | Pobierz wszystkie elementy podrzędne ViewGroup

MH.
źródło
7

Oto sugestia: możesz pobrać ID(określone np. Przez android:id="@+id/..My Str..), które zostało wygenerowane Rprzez używając jego podanej nazwy (np My Str.). Fragment kodu korzystający z getIdentifier()metody wyglądałby wówczas następująco:

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

Z wewnątrz Activity, przykładowe użycie w połączeniu z findViewByIdto:

// 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
źródło
3

Jako aktualizacja dla tych, którzy napotkali to pytanie po 2018 roku, jeśli używasz Kotlina, możesz po prostu użyć właściwości rozszerzenia Android KTX ViewGroup.children, aby uzyskać sekwencję bezpośrednich elementów potomnych widoku.

Chuck Stein
źródło
0

Aby odświeżyć układ tabeli (TableLayout), musiałem użyć wspomnianego powyżej podejścia rekurencyjnego, aby uzyskać wszystkie dzieci i tak dalej.

Moja sytuacja została nieco uproszczona, ponieważ potrzebowałem pracować tylko z LinearLayout i tymi klasami z niego rozszerzonymi, takimi jak TableLayout. Interesowało mnie tylko znalezienie dzieci TextView. Ale myślę, że nadal ma to zastosowanie do tego pytania.

Ostatnia klasa działa jako osobny wątek, co oznacza, że ​​może wykonywać inne czynności w tle przed analizowaniem dla dzieci. Kod jest mały i prosty i można go znaleźć na github: https://github.com/jkincali/Android-LinearLayout-Parser

jkincali
źródło
0

Ta metoda obejmuje wszystkie widoki wewnątrz układu, jest to podobne do odpowiedzi Aleksandra Kulachtina. Różnica polega na tym, że akceptuje dowolny typ układów nadrzędnych i zwraca listę widoków w postaci tablicy.

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
źródło