RecyclerView控件列表项布局match_parent属性失效的根本原理

之前用RecyclerView为了达到自己想要的结果,把item的根布局(最外层Layout)的大小设为match_parent,一开始却发现一个很大的问题!咦?为什么我的item一加载就成了wrap_content的效果?我的match_parent为什么效果显示不出来…在尝试了很多很多方法觉得应该不是我写错了之后,我才意识到我根本不知道LayoutInflater的inflate这个函数的参数的意义,查了api还是不解,这个第三个参数attachToRoot到底是啥意思?为了弄懂这个问题,看了很多博客,觉得这个是个好问题!弄懂了它,你再也不会错误的用inflate了!

我们最常用的便是LayoutInflater的inflate方法,这个方法重载了四种调用方式,分别为:

1. public View inflate(int resource, ViewGroup root)

2. public View inflate(int resource, ViewGroup root, boolean attachToRoot)

3.public View inflate(XmlPullParser parser, ViewGroup root)

4.public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)

而我们最常用的用法就是这样(RecyclerView的Adapter中):

[java]  view plain  copy
  1. @Override  
  2.     public AgendaDetailHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  3.         View view = mInflater.inflate(R.layout.item_agenda_detail, null);  
  4.         AgendaDetailHolder holder = new AgendaDetailHolder(view);  
  5.         return holder;  
  6.     }  

看似很简单的一个调用,原来有四个重载,而我们最简单的用法就是上面这段用法,也是我一开始的用法(复制粘贴就是这样,你也不会去看细节)

可当你运行测试的时候,你惊讶的发现,说好的效果呢?

下面是我item的布局文件,清清楚楚的写着match_parent

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent">  
  6.   
  7.     <View  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="50dp">  
  10.     </View>  
  11.     <LinearLayout  
  12.         android:orientation="vertical"  
  13.         android:id="@+id/id_agenda_detail_layout"  
  14.         android:background="#afbfff"  
  15.         android:layout_width="match_parent"  
  16.         android:layout_height="0dp"  
  17.         android:layout_weight="1">  
  18.         <LinearLayout  
  19.             android:background="#FFFFFF"  
  20.             android:orientation="vertical"  
  21.             android:layout_marginLeft="2dp"  
  22.             android:layout_marginRight="2dp"  
  23.             android:layout_marginTop="2dp"  
  24.             android:layout_marginBottom="2dp"  
  25.             android:layout_width="match_parent"  
  26.             android:layout_height="match_parent">  
  27.   
  28.             <TextView  
  29.                 android:id="@+id/id_text_view_agenda_title"  
  30.                 android:gravity="center"  
  31.                 android:textSize="30sp"  
  32.                 android:text="议程标题"  
  33.                 android:layout_width="match_parent"  
  34.                 android:layout_height="0dp"  
  35.                 android:layout_weight="1"/>  
  36.             <TextView  
  37.                 android:id="@+id/id_text_view_agenda_speaker"  
  38.                 android:gravity="center"  
  39.                 android:textSize="20sp"  
  40.                 android:text="主讲人:"  
  41.                 android:layout_width="match_parent"  
  42.                 android:layout_height="0dp"  
  43.                 android:layout_weight="1"/>  
  44.             <TextView  
  45.                 android:id="@+id/id_text_view_agenda_time"  
  46.                 android:gravity="center"  
  47.                 android:textSize="20sp"  
  48.                 android:text="议程时长:5:00"  
  49.                 android:layout_width="match_parent"  
  50.                 android:layout_height="0dp"  
  51.                 android:layout_weight="1"/>  
  52.             <RelativeLayout  
  53.                 android:layout_width="match_parent"  
  54.                 android:layout_height="0dp"  
  55.                 android:layout_weight="1">  
  56.   
  57.                 <Button  
  58.                     android:id="@+id/id_btn_show_agenda_file"  
  59.                     android:text="查看文件"  
  60.                     android:layout_centerInParent="true"  
  61.                     android:layout_width="100dp"  
  62.                     android:layout_height="50dp" />  
  63.   
  64.             </RelativeLayout>  
  65.   
  66.         </LinearLayout>  
  67.     </LinearLayout>  
  68.     <View  
  69.         android:layout_width="match_parent"  
  70.         android:layout_height="50dp">  
  71.     </View>  
  72.   
  73. </LinearLayout>  
运行的效果我就不说了…挤在一起的item,丑陋不堪,完全和我预想的效果不一样!然后我试了一下wrap_content,我惊讶的发现,真的一模一样……所以说我的match_parent竟然被改成了wrap_content!

网上查了一圈,我发现我的adapter代码和别人的不一样!于是,我机智地改成了这样:

[java]  view plain  copy
  1. @Override  
  2.     public AgendaDetailHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  3.         View view = mInflater.inflate(R.layout.item_agenda_detail, parent, false);  
  4.         AgendaDetailHolder holder = new AgendaDetailHolder(view);  
  5.         return holder;  
  6.     }  
运行了一遍之后,我发现竟然可以了!我的效果又设置成功了,match_parent又有了效果!

但我强烈地想知道,到底为什么有了效果…所以我开始了查找资料的过程,源码中是这么写的

[java]  view plain  copy
  1. public View inflate(int resource, ViewGroup root) {    
  2.     return inflate(resource, root, root != null);    
  3. }    
[java]  view plain  copy
  1. public View inflate(int resource, ViewGroup root, boolean attachToRoot) {    
  2.     if (DEBUG) System.out.println("INFLATING from resource: " + resource);    
  3.     XmlResourceParser parser = getContext().getResources().getLayout(resource);    
  4.     try {    
  5.         return inflate(parser, root, attachToRoot);    
  6.     } finally {    
  7.         parser.close();    
  8.     }    
  9. }    

这是前两种的调用,它们最终都会调用第四个重载,所以我们关注这个即可

[java]  view plain  copy
  1. public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {  
  2.         synchronized (mConstructorArgs) {  
  3.             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");  
  4.    
  5.             final AttributeSet attrs = Xml.asAttributeSet(parser);  
  6.             Context lastContext = (Context)mConstructorArgs[0];  
  7.             mConstructorArgs[0] = mContext;  
  8.             View result = root;  
  9.    
  10.             try {  
  11.                 // Look for the root node.  
  12.                 int type;  
  13.                 while ((type = parser.next()) != XmlPullParser.START_TAG &&  
  14.                         type != XmlPullParser.END_DOCUMENT) {  
  15.                     // Empty  
  16.                 }  
  17.    
  18.                 if (type != XmlPullParser.START_TAG) {  
  19.                     throw new InflateException(parser.getPositionDescription()  
  20.                             + ": No start tag found!");  
  21.                 }  
  22.    
  23.                 final String name = parser.getName();  
  24.                    
  25.                 if (DEBUG) {  
  26.                     System.out.println("**************************");  
  27.                     System.out.println("Creating root view: "  
  28.                             + name);  
  29.                     System.out.println("**************************");  
  30.                 }  
  31.    
  32.                 if (TAG_MERGE.equals(name)) {  
  33.                     if (root == null || !attachToRoot) {  
  34.                         throw new InflateException("<merge> can be used only with a valid "  
  35.                                 + "ViewGroup root and attachToRoot=true");  
  36.                     }  
  37.    
  38.                     rInflate(parser, root, attrs, false);  
  39.                 } else {  
  40.                     // Temp is the root view that was found in the xml  
  41.                     View temp;  
  42.                     if (TAG_1995.equals(name)) {  
  43.                         temp = new BlinkLayout(mContext, attrs);  
  44.                     } else {  
  45.                         temp = createViewFromTag(root, name, attrs);  
  46.                     }  
  47.    
  48.                     ViewGroup.LayoutParams params = null;  
  49.    
  50.                     if (root != null) {  
  51.                         if (DEBUG) {  
  52.                             System.out.println("Creating params from root: " +  
  53.                                     root);  
  54.                         }  
  55.                         // Create layout params that match root, if supplied  
  56.                         params = root.generateLayoutParams(attrs);  
  57.                         if (!attachToRoot) {  
  58.                             // Set the layout params for temp if we are not  
  59.                             // attaching. (If we are, we use addView, below)  
  60.                             temp.setLayoutParams(params);  
  61.                         }  
  62.                     }  
  63.    
  64.                     if (DEBUG) {  
  65.                         System.out.println("-----> start inflating children");  
  66.                     }  
  67.                     // Inflate all children under temp  
  68.                     rInflate(parser, temp, attrs, true);  
  69.                     if (DEBUG) {  
  70.                         System.out.println("-----> done inflating children");  
  71.                     }  
  72.    
  73.                     // We are supposed to attach all the views we found (int temp)  
  74.                     // to root. Do that now.  
  75.                     if (root != null && attachToRoot) {  
  76.                         root.addView(temp, params);  
  77.                     }  
  78.    
  79.                     // Decide whether to return the root that was passed in or the  
  80.                     // top view found in xml.  
  81.                     if (root == null || !attachToRoot) {  
  82.                         result = temp;  
  83.                     }  
  84.                 }  
  85.    
  86.             } catch (XmlPullParserException e) {  
  87.                 InflateException ex = new InflateException(e.getMessage());  
  88.                 ex.initCause(e);  
  89.                 throw ex;  
  90.             } catch (IOException e) {  
  91.                 InflateException ex = new InflateException(  
  92.                         parser.getPositionDescription()  
  93.                         + ": " + e.getMessage());  
  94.                 ex.initCause(e);  
  95.                 throw ex;  
  96.             } finally {  
  97.                 // Don't retain static reference on context.  
  98.                 mConstructorArgs[0] = lastContext;  
  99.                 mConstructorArgs[1] = null;  
  100.             }  
  101.    
  102.             Trace.traceEnd(Trace.TRACE_TAG_VIEW);  
  103.    
  104.             return result;  
  105.         }  
  106.     }  

代码很长,关键是这一段
[java]  view plain  copy
  1. ViewGroup.LayoutParams params = null;    
  2.     
  3. if (root != null) {    
  4.     if (DEBUG) {    
  5.         System.out.println("Creating params from root: " +    
  6.                 root);    
  7.     }    
  8.     // Create layout params that match root, if supplied    
  9.     params = root.generateLayoutParams(attrs);    
  10.     if (!attachToRoot) {    
  11.         // Set the layout params for temp if we are not    
  12.         // attaching. (If we are, we use addView, below)    
  13.         temp.setLayoutParams(params);    
  14.     }    
  15. }    

我们的root终于出现了,意思就是当root不是空的时候,并且你的attachToRoot位false,就把root的参数设置给temp,那root的实参parent是啥呢?在这个

[java]  view plain  copy
  1. <pre name="code" class="java">public AgendaDetailHolder onCreateViewHolder(ViewGroup parent, int viewType)  

 
函数中,parent就是RecyclerView,即你item的父试图,你创建的布局的即我们设置的layout的参数的计算都要依赖于这个父视图,而没有这个父视图(null)等于告诉框架你不需要父视图去添加你的view 

那这个temp是干嘛的,我们接着看代码

[java]  view plain  copy
  1. // Decide whether to return the root that was passed in or the  
  2.                     // top view found in xml.  
  3.                     if (root == null || !attachToRoot) {  
  4.                         result = temp;  
  5.                     }  
而这个result最终就是我们的返回结果,那我们的参数就成功的得到了计算(因为有了父视图,match_parent被计算了大小,就是你父视图,即你的RecyclerView的大小),那我想要的效果就得到设置!
通过这一行行的解释,我们终于弄明白了!root的作用就是来计算我们item的设置参数,没有root自然也没办法计算参数,那我们设置什么都不会有效果!可能我的理解会有一点偏差,不过起码也算知道了我们的设置为什么没有用的原因,后几篇文章我想对LayoutInflater做一个根本的解析,真正理解这个大师的原理。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值