Android遍歷所有控制元件的遞迴和非遞迴實現

yangxi_001發表於2018-09-30

題目描述

給出佈局的根節點,要求不使用遞迴的方式將所有型別為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());

        }
    }


}

 

相關文章