LayoutInfalter#inflateの引数について
RecyclerView.Adapter
の onCreateViewHolder
メソッドの中で、
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_in_list, parent, false)); }
のように書くつもりのところを
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_in_list, parent)); }
と、引数を一つ忘れてしまったところ、下記のようなエラーが発生しました。
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. at android.view.ViewGroup.addViewInner(ViewGroup.java:3378) at android.view.ViewGroup.addView(ViewGroup.java:3249) at android.view.ViewGroup.addView(ViewGroup.java:3194) // ...以下続く
ちょっと面白かったのでメモします。
忘れてしまっていたパラメータは、 attachToRoot
でした。
http://developer.android.com/intl/ja/reference/android/view/LayoutInflater.html#inflate(int, android.view.ViewGroup)
上記によると、引数を一つなくした #inflate (int resource, ViewGroup root)
メソッドの返り値は、
- resourceで指定されたXMLから生成されたViewのrootにあたるView
- 生成されたViewのRootにあたるViewは、rootがnullでなければ、rootに、rootがnullであればxmlに記述されているレイアウトのrootにあたるView
になります。なので、
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_in_list, parent)); }
の場合、R.layout.item_in_list
のrootであるLinearLayout
ではなく、
parent(RecyclerView)
が返ってきます。
ViewHolder
で扱うView = RecyclerView
の子要素のViewとして RecyclerView
を返していたので、The specified child already has a parent. You must call removeView() on the child's parent first
と怒られていたみたいです。
また、
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_in_list, parent, false)); }
のように書いた場合も確認しておきます。
http://developer.android.com/intl/ja/reference/android/view/LayoutInflater.html#inflate(int, android.view.ViewGroup, boolean)
attachToRoot
にfalseを指定した場合は、rootを返すのではなく、Xmlから生成されるViewのrootにあたるViewを返します。
ただし、rootに指定したViewのLayoutParamsは下記にある通り、xmlから生成されるViewのLayoutParamsに継承されるようです。
attachToRoot | boolean: Whether the inflated hierarchy should be attached to the root parameter? If false, root is only used to create the correct subclass of LayoutParams for the root view in the XML.
はっきりしなかったのですが、rootの指定の有無が子要素のレイアウトに影響するんだな、ということだけはわかるコード例を下記に置いておきます。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/itemText" android:text="hello, world" android:background="#98fb98" android:layout_width="wrap_content" android:gravity="center" android:layout_height="32dp" /> </LinearLayout>
<android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:padding="30dp" android:layout_width="match_parent" android:layout_height="match_parent" />
のレイアウトを用意して、
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // 下記のparentをnullにしてみる return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_in_list, parent, false)); }
Adapterのコードをコメントのように変更した場合としなかった場合のレイアウトを比べてみます。
(残りのコードの部分については
https://gist.github.com/woshidan/ea63ac52a9141eaa39de8c1cd26d9e45
をご参照ください.)
rootにparentを与えた場合
rootにnullを与えた場合
こちらも参考にさせていただきました。