Android - получить детей внутри View?


169

Учитывая вид, как я могу получить дочерние виды внутри него?

Так что у меня есть пользовательский вид и отладчик показывает, что под ним mChildrenесть еще 7 видов. Мне нужен способ доступа к этим представлениям, но, похоже, для этого нет общедоступного API.

какие-либо предложения?

РЕДАКТИРОВАТЬ:

Мой пользовательский вид наследуется от AdapterView

Ответы:


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

Будет ли это делать?


2
Каков наилучший способ сделать это рекурсивно и, таким образом, сгенерировать иерархический объект JSON?
— jimbob

Предполагая, что родительский вид названviewGroup
— Prime624

57

Если вы хотите не только получить всех прямых детей, но и всех детей и т. Д., Вы должны сделать это рекурсивно:

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

Чтобы использовать результат, вы можете сделать что-то вроде этого:

    // 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
Этот вызов не сохраняется, так как, например, child может иметь тип View, а не ViewGroup, поэтому рекурсивный вызов getAllChildren может вызвать исключение, так как приведение не выполнено. Поэтому вы должны добавить if (! (V instanceof ViewGroup)) return; перед актерским составом
— Фил

2
Небольшое предложение: замените цикл for for (int i = 0, n = vg.getChildCount(); i < n; i++) на Android, доступ к локальным переменным вместо вызова метода, как правило, происходит намного быстрее. Таким образом, метод вызывается только один раз для каждой группы представлений, а затем при последующих запусках используется локальная переменная.
— ArtOfWarfare

Я изменил код из-за предложения Фила Дигманна. Надеюсь это поможет. Работает для меня.
— IHeartAndroid

1
Прохладно. Я бы предложил только добавить логическое значение includeViewGroups и только если оно истинно, iewArrayList.add (v); Это полезно для итерации в адаптере (и мне нужны только листья). Спасибо!
— JRun

20

Вы всегда можете получить доступ к дочерним представлениям через View.findViewById () http://developer.android.com/reference/android/view/View.html#findViewById(int ).

Например, в деятельности / представлении:

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

или если у вас есть ссылка на представление:

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

У меня нет доступа к идентификаторам, пользовательский интерфейс генерируется с помощью кода, я пишу автоматизацию с использованием robotium, поэтому возможности, которые я могу сделать, ограничены
— aryaxt

@jonson, бесполезно, если вам нужно, чтобы все дети.
— Pacerier


17

Я просто собираюсь предоставить этот ответ в качестве альтернативного рекурсивного алгоритма @ IHeartAndroid для обнаружения всех дочерних элементов Viewв иерархии представлений. Обратите внимание, что на момент написания этой статьи рекурсивное решение имеет недостатки, поскольку оно содержит дубликаты в своем результате.

Для тех, у кого проблемы с рекурсией, есть нерекурсивная альтернатива. Вы получаете бонусные баллы за то, что поняли, что это также альтернатива поиска в ширину по сравнению с рекурсивным решением, основанным на глубине .

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

Пара быстрых тестов (ничего формального) предполагает, что эта альтернатива также быстрее, хотя это, скорее всего, связано с количеством новых ArrayListэкземпляров, создаваемых другим ответом. Кроме того, результаты могут отличаться в зависимости от вертикальной / горизонтальной иерархии представления.

Кросс-пост от: Android | Получить все дочерние элементы ViewGroup


7

Вот предложение: вы можете получить ID(указанный, например, android:id="@+id/..My Str..), который был сгенерирован R, используя его имя (например My Str). Фрагмент кода с использованием getIdentifier()метода будет:

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

Изнутри Activity, пример использования в сочетании с findViewById:

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

3

В качестве обновления для тех, кто сталкивается с этим вопросом после 2018 года, если вы используете Kotlin, вы можете просто использовать свойство расширения Android KTX ViewGroup.children, чтобы получить последовательность непосредственных потомков представления.


0

Чтобы обновить макет таблицы (TableLayout), мне пришлось использовать рекурсивный подход, упомянутый выше, чтобы получить всех детей детей и так далее.

Моя ситуация несколько упростилась, потому что мне нужно было работать только с LinearLayout и теми классами, расширенными из него, как TableLayout. И мне было интересно только найти детей TextView. Но я думаю, что это все еще применимо к этому вопросу.

Последний класс запускается как отдельный поток, что означает, что он может выполнять другие действия в фоновом режиме перед синтаксическим анализом для детей. Код небольшой и простой, его можно найти на github: https://github.com/jkincali/Android-LinearLayout-Parser


0

Этот метод берет все виды внутри макета, это похоже на ответ Александра Куляхтина. Разница в том, что он принимает любой тип родительских макетов и возвращает список массивов представлений.

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;
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.