Android開発をしていて、画面上で少し複雑な構造を動的に追加していくと、要素の階層構造を取得したくなることがあります。
今回は画面上に表示されている要素の階層構造を取得するプログラムを作成してみます。
やりたいことは、次のような画面で、「追加」ボタンを押す度に赤枠部分が動的に追加され、その都度、要素の階層構造をログに表示することです。

追加される要素群には、テキストフィールド、ラジオボタン、チェックボックスを設けました。
まず、string.xmlで画面文言を定義。
<resources>
<string name="app_name">NestLayoutSample</string>
<string name="header_message">ボタンを押すと要素グループを追加します</string>
<string name="etName">お名前</string>
<string name="cb_train">電車</string>
<string name="cb_car">バス</string>
<string name="cb_plane">飛行機</string>
<string name="rb_male">男</string>
<string name="rb_female">女</string>
<string name="bt_add">追加</string>
</resources>
次にメインのレイアウトを定義します。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/et_Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/header_message"
android:textSize="20dp"/>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray"/>
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="32dp">
<LinearLayout
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bt_add" />
</LinearLayout>
</ScrollView>
</LinearLayout>
そして「追加」ボタンを押す度に追加される要素群を、別のレイアウトxmlで定義します。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="15dp"
android:text="@string/etName"/>
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:text="@string/cb_train"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:text="@string/cb_car"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:text="@string/cb_plane"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:text="@string/rb_male"/>
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:text="@string/rb_female"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray"/>
</LinearLayout>
最後にActivityコード。Logcatに文字列が表示されるようにしています。
public class MainActivity extends AppCompatActivity {
private LinearLayout rootView;
private LayoutInflater inflater;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rootView = findViewById(R.id.root);
inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Button b = findViewById(R.id.button);
SampleListener sl = new SampleListener();
b.setOnClickListener(sl);
}
private class SampleListener implements View.OnClickListener {
@Override
public void onClick(View view) {
View subView = inflater.inflate(R.layout.sub, null);
rootView.addView(subView, rootView.getChildCount() - 1);
// Viewの階層構造を取得しLogに表示する
StringBuilder sb = new StringBuilder();
storeViewClassName(rootView, sb, "\t");
Log.i(this.getClass().getName().toString() + "-->\n", "\n" + sb.toString());
}
// Viewの階層構造をStringBuilderに格納する
private void storeViewClassName (View v, StringBuilder sb, String tab) {
sb.append(tab).append(v.getClass().getSimpleName()).append("\n");
if (v instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) v).getChildCount() ; i++) {
storeViewClassName(((ViewGroup) v).getChildAt(i), sb, tab + "\t");
}
}
}
}
}
初期画面はこんな感じ。ここからスタートします。

「追加」ボタンを一回押したとき。要素が追加されると共に、Logcatに要素名が表示されています。

LinearLayout
LinearLayout
LinearLayout
AppCompatTextView
AppCompatEditText
LinearLayout
AppCompatCheckBox
AppCompatCheckBox
AppCompatCheckBox
LinearLayout
RadioGroup
AppCompatRadioButton
AppCompatRadioButton
View
AppCompatButton
さらにもう一度「追加」ボタンを押すと、再度要素追加が行われ、Logcatに現れる階層構造も一つ増えています。

LinearLayout
LinearLayout
LinearLayout
AppCompatTextView
AppCompatEditText
LinearLayout
AppCompatCheckBox
AppCompatCheckBox
AppCompatCheckBox
LinearLayout
RadioGroup
AppCompatRadioButton
AppCompatRadioButton
View
LinearLayout
LinearLayout
AppCompatTextView
AppCompatEditText
LinearLayout
AppCompatCheckBox
AppCompatCheckBox
AppCompatCheckBox
LinearLayout
RadioGroup
AppCompatRadioButton
AppCompatRadioButton
View
AppCompatButton

