Android: ¿meter a los niños dentro de una vista?

169

Dada una vista, ¿cómo puedo obtener las vistas secundarias dentro de ella?

Así que tengo una vista personalizada y el depurador muestra que debajo mChildrenhay otras 7 vistas. Necesito una forma de acceder a estas vistas, pero no parece que haya una API pública para hacerlo.

¿alguna sugerencia?

EDITAR:

Mi vista personalizada hereda de AdapterView

aryaxt
fuente

Respuestas:

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

¿Eso hará?

Alexander Kulyakhtin
fuente
2
¿Cuál es la mejor manera de hacer esto de forma recursiva y generar así un objeto JSON de jerarquía de vistas?
jimbob
Asumiendo que la Vista principal se llamaviewGroup
Prime624
57

Si no solo desea obtener todos los hijos directos, sino también los hijos de todos los niños, etc., debe hacerlo de forma recursiva:

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

Para usar el resultado, podría hacer algo como esto:

    // 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
fuente
8
Esta llamada no se guarda, ya que, por ejemplo, child puede ser de tipo View y no de ViewGroup, por lo que la llamada recursiva de getAllChildren podría causar una excepción, ya que la conversión falla. Por lo tanto, debe agregar un if (! (V instanceof ViewGroup)) return; antes del reparto
Phil
2
Pequeña sugerencia: reemplace su bucle for con for (int i = 0, n = vg.getChildCount(); i < n; i++) Android, acceder a variables locales en lugar de llamar a un método tiende a ser mucho más rápido. De esta manera, se llama a un método solo una vez por grupo de vista y luego, en ejecuciones posteriores, se utiliza una variable local.
ArtOfWarfare
Cambié el código por sugerencia de Phil Diegmann. Espero que esto ayude. Funciona para mi.
IHeartAndroid
1
Frio. Solo sugeriría agregar un booleano 'includeViewGroups' y solo si es cierto, iewArrayList.add (v); Esto es útil para iterar en un adaptador (y solo necesito las hojas). ¡Gracias!
JRun
20

Siempre puede acceder a las vistas secundarias a través de View.findViewById () http://developer.android.com/reference/android/view/View.html#findViewById(int ).

Por ejemplo, dentro de una actividad / vista:

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

o si tiene una referencia a una vista:

...
private void init(View root) {
  View child2 = root.findViewById(R.id.child2);
}
jonson
fuente
No tengo acceso a los identificadores, la interfaz de usuario se genera a través de código, estoy escribiendo la automatización usando robotium, así que las cosas que puedo hacer están limitados
aryaxt
@jonson, no es útil en absoluto si necesita tener a todos los niños.
Pacerier
También quora.com/unanswered/…
Pacerier
17

Solo voy a proporcionar esta respuesta como un algoritmo recursivo alternativo de @ IHeartAndroid para descubrir a todos los niños Viewen una jerarquía de vistas. Tenga en cuenta que en el momento de escribir este artículo, la solución recursiva es defectuosa ya que contendrá duplicados en su resultado.

Para aquellos que tienen problemas para entender la recursividad, aquí hay una alternativa no recursiva. Obtiene puntos de bonificación por darse cuenta de que esto también es una alternativa de búsqueda de primer orden al enfoque de profundidad de la solución recursiva.

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

Un par de pruebas rápidas (nada formal) sugieren que esta alternativa también es más rápida, aunque es probable que tenga que ver con la cantidad de nuevas ArrayList instancias que crea la otra respuesta. Además, los resultados pueden variar según la verticalidad / horizontalidad de la jerarquía de vistas.

Publicación cruzada desde: Android | Obtenga todos los elementos secundarios de un ViewGroup

MH
fuente
7

Aquí hay una sugerencia: puede obtener el ID(especificado, por ejemplo, por android:id="@+id/..My Str..) que se generó Rutilizando su nombre de pila (por ejemplo My Str). Un fragmento de código que usa el getIdentifier()método sería:

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

Desde dentro de Activity, un ejemplo de uso junto con findViewByIdsería:

// 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
fuente
3

Como una actualización para aquellos que se encuentran con esta pregunta después de 2018, si está usando Kotlin, simplemente puede usar la propiedad de extensión KTX de Android ViewGroup.children para obtener una secuencia de los hijos inmediatos de la Vista.

Chuck Stein
fuente
0

Para actualizar un diseño de tabla (TableLayout) terminé teniendo que usar el enfoque recursivo mencionado anteriormente para obtener todos los hijos de los niños, etc.

Mi situación se simplificó de alguna manera porque solo necesitaba trabajar con LinearLayout y esas clases se extendieron como TableLayout. Y solo estaba interesado en encontrar niños TextView. Pero creo que todavía es aplicable a esta pregunta.

La clase final se ejecuta como un hilo separado, lo que significa que puede hacer otras cosas en segundo plano antes de analizar para los niños. El código es pequeño y simple y se puede encontrar en github: https://github.com/jkincali/Android-LinearLayout-Parser

jkincali
fuente
0

Este método toma todas las vistas dentro de un diseño, esto es similar a la respuesta de Alexander Kulyakhtin. La diferencia es que acepta cualquier tipo de diseño principal y devuelve una Lista de vistas de matriz.

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
fuente