【发布时间】:2013-09-11 05:03:03
【问题描述】:
getChildAt(i) on 只获取ViewGroup 的直接子代,是否可以在不进行嵌套循环的情况下访问所有子代?
【问题讨论】:
-
stackoverflow.com/a/18980154/1093872 比答案更好的解决方案。 (不创建对象垃圾,运行更快,更简单)
标签: android
getChildAt(i) on 只获取ViewGroup 的直接子代,是否可以在不进行嵌套循环的情况下访问所有子代?
【问题讨论】:
标签: android
在撰写此答案时,已接受的答案存在缺陷,因为它的结果中会包含重复项。
对于那些难以理解递归的人,这里有一个非递归的替代方案。如果您意识到这也是另一个答案的深度优先方法的广度优先搜索替代方案,您将获得奖励积分。
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 实例的数量有关。此外,结果可能会因视图层次结构的垂直/水平程度而异。
【讨论】:
(source)
如果您想获取所有子视图以及子 ViewGroups 中的视图,则必须递归执行,因为 API 中没有提供开箱即用的功能。
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 viewGroup = (ViewGroup) v;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View child = viewGroup.getChildAt(i);
ArrayList<View> viewArrayList = new ArrayList<View>();
viewArrayList.add(v);
viewArrayList.addAll(getAllChildren(child));
result.addAll(viewArrayList);
}
return result;
}
这将为您提供一个包含层次结构中所有视图的 ArrayList,然后您可以对其进行迭代。
本质上,如果在层次结构中找到另一个 ViewGroup,则此代码会调用自身,然后返回一个 ArrayList 以添加到更大的 ArrayList。
【讨论】:
ViewGroup 封装了另一个具有多个子级的ViewGroup,就保证您的代码在结果集中有重复项。我会留给你找出原因。你很接近,尽管结果中存在所有视图,但它也包含欺骗。也许这就是copy-pasting 所得到的? ;)
forEach{} 遍历ViewGroup
如果要遍历所有子视图,可以在任何 ViewGroup 中使用预定义的 kotlin 扩展 forEach。示例:
yourViewGroup.forEach{ childView ->
// do something with this childView
}
ViewGroup 中使用 .children.toList 返回子视图列表
要返回Views 的列表,您可以使用函数children 返回Sequence,然后使用toList() 将其转换为List。示例:
val yourChildViewsList: List<View> = yourViewGroup.children.toList()
【讨论】:
我以递归方式重新实现了这个方法。不确定它是否比 MH 快,但至少它没有重复。
private List<View> getAllViews(View v) {
if (!(v instanceof ViewGroup) || ((ViewGroup) v).getChildCount() == 0) // It's a leaf
{ List<View> r = new ArrayList<View>(); r.add(v); return r; }
else {
List<View> list = new ArrayList<View>(); list.add(v); // If it's an internal node add itself
int children = ((ViewGroup) v).getChildCount();
for (int i=0;i<children;++i) {
list.addAll(getAllViews(((ViewGroup) v).getChildAt(i)));
}
return list;
}
}
MH 的回答包括给方法的父视图,所以我创建了这个辅助方法(这是要调用的方法)。
我的意思是,如果您在 TextView(没有子视图)上调用此方法,它会返回 0 而不是 1。
private List<View> getAllChildren(View v) {
List list = getAllViews(v);
list.remove(v);
return list;
}
TLDR:要计算所有孩子的复制和粘贴这两种方法,请调用 getAllChildren()
【讨论】:
我不知道它是否好用。只需覆盖 onViewAdded(View child) 方法并在List<View>中添加视图。给它想法。通过docs。这在您编写自定义时很有用查看组
在将新子项添加到此 ViewGroup 时调用。覆盖应始终调用 super.onViewAdded。
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
views.add(child);//add in list and then use it later
}
【讨论】:
这是我的(递归)解决方案,可以像这样打印层次结构:
android.widget.LinearLayout children:2 id:-1
android.view.View id:2131624246
android.widget.RelativeLayout children:2 id:2131624247
android.support.v7.widget.AppCompatTextView id:2131624248
android.support.v7.widget.SwitchCompat id:2131624249
public static String getViewHierarcy(ViewGroup v) {
StringBuffer buf = new StringBuffer();
printViews(v, buf, 0);
return buf.toString();
}
private static String printViews(ViewGroup v, StringBuffer buf, int level) {
final int childCount = v.getChildCount();
v.getId();
indent(buf, level);
buf.append(v.getClass().getName());
buf.append(" children:");
buf.append(childCount);
buf.append(" id:"+v.getId());
buf.append("\n");
for (int i = 0; i < childCount; i++) {
View child = v.getChildAt(i);
if ((child instanceof ViewGroup)) {
printViews((ViewGroup) child, buf, level+1);
} else {
indent(buf, level+1);
buf.append(child.getClass().getName());
buf.append(" id:"+child.getId());
buf.append("\n");
}
}
return buf.toString();
}
private static void indent(StringBuffer buf, int level) {
for (int i = 0; i < level; i++) {
buf.append(" ");
}
}
【讨论】:
我最近遇到了同样的问题,并使用 Kotlin 的 扩展函数解决了这个问题,如下所示:
fun ViewGroup.getAllChildren(): List<View> {
val children = ArrayList<View>()
for (i in 0 until this.childCount) {
children.add(this.getChildAt(i))
}
return children
}
【讨论】:
你可以使用 Kotlin 的扩展函数 Like ViewGroup.children
此方法返回此视图组中子视图的序列。
【讨论】: