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

169

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

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

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

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

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

aryaxt
источник

Ответы:

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");
    }
IHeartAndroid
источник
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);
}
Jonson
источник
У меня нет доступа к идентификаторам, пользовательский интерфейс генерируется с помощью кода, я пишу автоматизацию с использованием robotium, поэтому возможности, которые я могу сделать, ограничены
aryaxt
@jonson, бесполезно, если вам нужно, чтобы все дети.
Pacerier
Также quora.com/unanspted/…
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);
vanangelov
источник
3

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

Чак Стейн
источник
0

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

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

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

jkincali
источник
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;
}
NJY404
источник