この attachToRoot に true を指定した場合と false にした場合で返ってくるルートビューが異なります。
例えば
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
</LinearLayout>
LayoutInflater inflater = getLayoutInflater();
FrameLayout root = new FrameLayout(this);
に対して
View v = inflater.inflate(R.layout.main, root, true);
とした場合は v は FrameLayout (つまり root)になります。
一方、
View v = inflater.inflate(R.layout.main, root, false);
または
View v = inflater.inflate(R.layout.main, null, true);
or
View v = inflater.inflate(R.layout.main, null, false);
とした場合は v は LinearLayout (つまり R.layout.main のルートビュー)になります。
inflater.inflate(R.layout.main, root, false)
と
inflater.inflate(R.layout.main, null, false)
の違いは、root が null でない場合はそれに合うような LayoutParams が返ってくるルートビューにセットされるという点です。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/view/LayoutInflater.java#471
471 if (root != null) {
472 if (DEBUG) {
473 System.out.println("Creating params from root: " +
474 root);
475 }
476 // Create layout params that match root, if supplied
477 params = root.generateLayoutParams(attrs);
478 if (!attachToRoot) {
479 // Set the layout params for temp if we are not
480 // attaching. (If we are, we use addView, below)
481 temp.setLayoutParams(params);
482 }
483 }
さて、この inflate() メソッドをみると、<merge> タグのチェックを行っている箇所があります。
424 public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
...
453 if (TAG_MERGE.equals(name)) {
454 if (root == null || !attachToRoot) {
455 throw new InflateException("<merge /> can be used only with a valid "
456 + "ViewGroup root and attachToRoot=true");
457 }
458
459 rInflate(parser, root, attrs, false);
460 } else {
XML の最初のスタートタグが <merge> だった場合、root が null だったり attachToRoot が false だと InflateException が投げられます。
考えてみれば当たり前ですね。<merge> タグは addView されたときに親の View と合体するということなので、合体対象がいない場合戻り値の View として返すものがなくなってしまいます。
どういう場面で <merge> タグのレイアウトを inflate() することがあるかというとオリジナルの ViewGroup を作る場合です。
例えば、ボタンが縦に3つ並んだ LinearLayout をオリジナルの ViewGroup にしたいとします。
つまり、
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
</LinearLayout>
を
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<yanzm.example.viewgroupmerge.MyViewGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
にしたいということです。
そのためには、この MyViewGroup 自身に縦に並ぶボタン3つを持たせる必要があります。
初心者がやりがちなのがこういうコードです。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
public class MyViewGroup extends FrameLayout {
public MyViewGroup(Context context) {
super(context);
init(context);
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.custom_layout, null, false);
addView(v);
}
}
これでも動きますが、View 階層が一つ多くなってしまうのでよくありません。
そこで、<merge> タグを使って次のようにすると、元と同じ View 階層にとどめておけます。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</merge>
public class MyViewGroup extends LinearLayout {
public MyViewGroup(Context context) {
super(context);
init(context);
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
setOrientation(LinearLayout.VERTICAL);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.custom_layout, this, true);
}
}
こうすれば <merge> 部分が MyViewGroup と合体してくれます。
さらに、View には inflate(Context context, int resource, ViewGroup root) という static メソッドがあり、 このメソッドを使ってさらに簡単に書けます。
private void init(Context context) {
setOrientation(LinearLayout.VERTICAL);
View.inflate(context, R.layout.custom_layout, this);
}
LayoutInflater のインスタンスを取得するとしては以下の方法をよく使います。
- LayoutInflater inflater = activity.getLayoutInflater();
- LayoutInflater inflater = LayoutInflater.from(Context context);
- LayoutInflater inflater = (LayoutInflater) context.SystemService(Context.LAYOUT_INFLATER_SERVICE);
0 件のコメント:
コメントを投稿