失效表现:
在item的xml文件中,设置了“match_parent”,但是在显示的时候,展现形式确实“warp_content”。xml文件如下:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/rv_city_header_name"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="#cccccc"
android:gravity="left|center"
android:text="我是头"/>
</LinearLayout>
加载方式如下:
View viewHeader = LayoutInflater.from(mAct).inflate(R.layout.rv_city_header, null);
在网上查找一些资料之后,找到了原因。正确写法应该是:
View viewHeader = LayoutInflater.from(mAct).inflate(R.layout.rv_city_header, parent, false);
为什么呢??? 下面我们就来分析一下。
先来分析下LayoutInflater的inflate方法。
-
inflate方法有4个重载方法,但最终调用的是
参数介绍 1、parser 解析xml的解析器 2、root 如果attachToRoot为true时,将生成的View层级加到root上, 如果attachToRoot为false时,只是简单的提供一组layoutParams值的对象 3、attachToRoot 是否添加到root中,如果为false,root仅仅用来创建正确的LayoutParams对象 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { .... View resulte = root; .... if (TAG_MERGE.equals(name)) { if (root == null || !attachToRoot) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, inflaterContext, attrs, false); } else { // 生成view final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; // 如果不为空 if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // 根据布局文件,生成layoutParams params = root.generateLayoutParams(attrs); if (!attachToRoot) { // 如果attachToRoot 为 false,设置布局参数 temp.setLayoutParams(params); } } // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); ... // We are supposed to attach all the views we found (int temp) // to root. Do that now. 设置到root中 if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. 如果root为空或者attachToRoot为false if (root == null || !attachToRoot) { result = temp; } } return result; }
整体流程就是:根据xml绘制View - temp,
- 如果root不为null的话,会生成LayoutParams,并且attachToRoot为false,给temp设置布局参数。
- 如果root不为null,attachToRoot为true,把temp添加到root上
- 如果root为空 或者attachToRoot为false,返回temp, 否则,返回root
再来看看RecyclerView
可以追踪holder.itemView
在tryGetViewHolderForPositionByDeadline
方法里面
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
...
// 获取子View的布局参数
final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
final LayoutParams rvLayoutParams;
// 由上面分析可知,如果root不为null时,会给view设置布局参数
if (lp == null) {
// 如果root为null,会生成一个默认的params,设置给item。这个方法具体的返回在下面介绍
rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
holder.itemView.setLayoutParams(rvLayoutParams);
} else if (!checkLayoutParams(lp)) {
rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
holder.itemView.setLayoutParams(rvLayoutParams);
} else {
rvLayoutParams = (LayoutParams) lp;
}
rvLayoutParams.mViewHolder = holder;
rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
return holder;
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
if (mLayout == null) {
throw new IllegalStateException("RecyclerView has no LayoutManager" + exceptionLabel());
}
return mLayout.generateDefaultLayoutParams();
}
具体的实现方法,在LinearLayoutManager里面
@Override
public LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
返回的是warp_content
并设置给了item。所以,如果我们设置root为null,则 holder.itemView.getLayoutParams()
为空,设置wrap_content
的布局参数,自己本身设置的参数无效。
所以我们在Adapter中,生成View的时候,要使用下面这种形式,才能保证布局的正确性。
LayoutInflater.from(mAct).inflate(R.layout.rv_city_header, parent, false);