Viewの階層を入れ替える
以下の二つのView構造が以下のような構造だったとしよう。
- content.xml
- scrollpane.xml
content.xmlはActivity生成時にsetContentViewメソッドでインフレートしており、その後何らかのタイミングで、適宜、View構造のルートより下全て(LinearLayout2以下全て)を、scrollpane.xmlから同様にインフレートした階層中のScrollView下にぶら下げ直したい。
結果としてViewの階層は以下のような構造になることを期待している。
目的としてはJFC/SwingのScrollPaneのように、LinearLayout2以下をScrollViewでスクロール可能な領域に設定したい訳だ。
これをコードで行う場合、以下のようになる。
- ScrollableActivity.java
//content.xml側のルートビューを取得 ViewGroup linearLayout1 = (ViewGroup)this.findViewById(R.id.LinearLayout1); LayoutInflater factory = LayoutInflater.from(this); //scrollpane.xmlのルートを取得 LinearLayout linearLayout3 = (LinearLayout)factory.inflate(R.layout.scrollpane, null); //その下のScrollViewを取得 ScrollView scrollView = (ScrollView)linearLayout3.findViewById(R.id.ScrollView); //scrollView 下に元々のView階層を追加 scrollView.addView(linearLayout1.getChildAt(0)); //コンテンツViewを差し替え this.setContentView(scrollView);
これで良いと思ったのだが、実行すると色を付けた行で以下の例外が発生する。
Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
例外のトレースから推するに、AndroidのViewGroupとその継承クラスは一度設定されたparent(そのViewの親に当たるView。addViewメソッドが実行された際に内部で設定される)を勝手に変更できないらしい。
removeViewを先に呼びだせとも言われているが、任意のView(ViewGroup)を他のView下に挿げ替えるには、
1. 自らの親のViewを取得する
2. 親のViewのremoveViewメソッドを呼びだして、自身との関連を除去する
3. 改めて他のViewに追加する(親Viewが設定し直される)
という手順を踏む必要があるようだ。
- 修正後のコード
//content.xml側のルートビューを取得 ViewGroup linearLayout1 = (ViewGroup)this.findViewById(R.id.LinearLayout1); LayoutInflater factory = LayoutInflater.from(this); //scrollpane.xmlのルートを取得 LinearLayout linearLayout3 = (LinearLayout)factory.inflate(R.layout.scrollpane, null); //その下のScrollViewを取得 ScrollView scrollView = (ScrollView)linearLayout3.findViewById(R.id.ScrollView); //親Viewを取得してremoveView実行 ViewGroup parent = (ViewGroup)linearLayout1.getParent(); if ( parent != null ) { parent.removeView(linearLayout1); } //ScrollView下に元々のView階層を追加 scrollView.addView(linearLayout1.getChildAt(0)); //コンテンツViewを差し替え this.setContentView(scrollView);
これで取り敢えず期待通りになったのだが、getParent()メソッドで戻るViewParentインタフェースがよく分からない。Viewの親たる振る舞いを規定しているインタフェースなのだが、ならばなぜViewParentに配下のViewを除去するためのremoveViewメソッドが無いのだろう。