美文网首页
RecyclerView item设置宽高失效问题

RecyclerView item设置宽高失效问题

作者: 小锡兵鸥 | 来源:发表于2018-11-26 20:30 被阅读39次

    失效表现:
    在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方法。

    1. 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.itemViewtryGetViewHolderForPositionByDeadline方法里面

    @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);

    相关文章

      网友评论

          本文标题:RecyclerView item设置宽高失效问题

          本文链接:https://www.haomeiwen.com/subject/qthiqqtx.html