题目描述
给出布局的根节点,要求不使用递归的方式将所有类型为Button的控件背景设置为红色。
分析
对于Android中的布局来说,有两种类型的节点,一种是ViewGroup布局,另外一种是View控件,按照类似树形结构来组织(注意,不是二叉树)。
对于控件的遍历,可以转化为对树的遍历。对树的遍历有递归方式和非递归的方式,非递归方式又可以分为深度优先遍历和广度优先遍历。
实现
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:background="#abcdef"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#156ec7"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="文本1"
android:textColor="#ffffff" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="文本2"
android:textColor="#ffffff" />
</LinearLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮1" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#5d9726"
android:padding="10dp">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮2" />
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btn"
android:padding="5dp"
android:text="文本3"
android:textColor="#ffffff"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btn"
android:layout_toRightOf="@id/tv"
android:padding="5dp"
android:text="文本4"
android:textColor="#ffffff" />
</RelativeLayout>
</LinearLayout>
界面效果和对应的树结构如下:
其中有颜色的节点类型为viewGroup
![]() | ![]() |
布局 | 抽象树结构 |
具体算法实现
以下方式实现了三种方式的遍历结果,读者可以参考。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewGroup root = (ViewGroup) findViewById(R.id.rootView);
travelTree3(root);
}
//递归遍历树
private void travelTree1(View root) {
if (root instanceof ViewGroup) {
int childCount = ((ViewGroup) root).getChildCount();
for (int i = 0; i < childCount; i++) {
travelTree1(((ViewGroup) root).getChildAt(i));
}
} else if (root instanceof View) {
Log.i("visitView", root.toString());
if (root instanceof Button)
root.setBackgroundColor(Color.parseColor("#ff0000"));
}
}
//非递归广度遍历,利用队列数据结构
private void travelTree2(View root) {
ArrayDeque queue = new ArrayDeque();
queue.addLast(root);
while (!queue.isEmpty()) {
//取得队头
View front = (View) queue.getFirst();
//如果为viewGroup则使子节点入队列
if (front instanceof ViewGroup) {
int childCount = ((ViewGroup) front).getChildCount();
for (int i = 0; i < childCount; i++) {
queue.addLast(((ViewGroup) front).getChildAt(i));
}
}
//如果队头为View类型,输出
else if (front instanceof View)
Log.i("visitView", front.toString());
//队头出队
queue.pollFirst();
}
}
//非递归深度遍历,利用栈数据结构
private void travelTree3(View root) {
ArrayDeque stack = new ArrayDeque();
stack.addLast(root);
while (!stack.isEmpty()) {
//取得栈顶
View top = (View) stack.getLast();
//出栈
stack.pollLast();
//如果为viewGroup则使子节点入栈
if (top instanceof ViewGroup) {
int childCount = ((ViewGroup) top).getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
stack.addLast(((ViewGroup) top).getChildAt(i));
}
}
//如果栈顶为View类型,输出
else if (top instanceof View)
Log.i("visitView", top.toString());
}
}
}