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

Odpowiedzi:


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

Czy to wystarczy?


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

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

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


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


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


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


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;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.