将RecyclerView的宽高属性设置为“wrap_content”

前言

最近,在使用RecyclerView时遇到一个问题,就是将RecyclerView的高度设置为“wrap_content”时,控件实际测量高度为”match_parent”,为什么会出现这种问题呢?RecyclerView设置如下:

<android.support.v7.widget.RecyclerView
    android:id="@+id/sv_rv"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/darker_gray">
</android.support.v7.widget.RecyclerView>
recyclerView = (RecyclerView)findViewById(R.id.sv_rv);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerView.setAdapter(new RvAdapter());

而实际显示效果如下:
这里写图片描述

正文

一般来说,出现这种问题是因为高度测量问题,所以先查看RecyclerView的onMeasure方法,如下:

@Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        if (mAdapterUpdateDuringMeasure) {
            eatRequestLayout();
            processAdapterUpdatesAndSetAnimationFlags();

            if (mState.mRunPredictiveAnimations) {
                // TODO: try to provide a better approach.
                // When RV decides to run predictive animations, we need to measure in pre-layout
                // state so that pre-layout pass results in correct layout.
                // On the other hand, this will prevent the layout manager from resizing properly.
                mState.mInPreLayout = true;
            } else {
                // consume remaining updates to provide a consistent state with the layout pass.
                mAdapterHelper.consumeUpdatesInOnePass();
                mState.mInPreLayout = false;
            }
            mAdapterUpdateDuringMeasure = false;
            resumeRequestLayout(false);
        }

        if (mAdapter != null) {
            mState.mItemCount = mAdapter.getItemCount();
        } else {
            mState.mItemCount = 0;
        }
        if (mLayout == null) {
            defaultOnMeasure(widthSpec, heightSpec);
        } else {
            mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
        }

        mState.mInPreLayout = false; // clear
    }

重要代码:

if (mLayout == null) {
    defaultOnMeasure(widthSpec, heightSpec);
    } else {
        mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
    }
}

可以发现当RecyclerView设置LayoutManager时,RecyclerView的测量高度由所选择的LayoutManager决定。由于上文所选择的LayoutManager为LinearLayoutManager,故查看其源码的onMeasure函数,然而并没找到,那就查看其父类的onMeasure函数。

public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
    mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
}

然后查看defaultOnMeasure函数,可以发现其并未对子item进行高度测量,所以导致其实测高度异常。故而要纠正此问题,需要重写LayoutManager的onMeasure函数。
如下,是我自己写的一个简单的LayoutManager子类,继承自LinearLayoutManager,仅作抛砖引玉之用:

package how.th.ridelib.RvLayoutManager;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;


public class RecyclerLayoutManager extends LinearLayoutManager {

    public RecyclerLayoutManager(Context context){
        super(context);
    }

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
        int itemCount = state.getItemCount();
        int measuredWidth = 0;
        int measuredHeight = 0;
        int heightSize = View.MeasureSpec.getSize(heightSpec);
        for(int i = 0; i < itemCount; i ++){
            View view = recycler.getViewForPosition(i);

            if(view != null){
                RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams)view.getLayoutParams();
                int margin = layoutParams.bottomMargin + layoutParams.topMargin;
                measuredWidth = View.MeasureSpec.getSize(widthSpec);
                view.measure(widthSpec, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
                switch (layoutParams.height){
                    case RecyclerView.LayoutParams.WRAP_CONTENT:
                        measuredHeight += (getDecoratedMeasuredHeight(view) + margin);
                        break;
                    case RecyclerView.LayoutParams.MATCH_PARENT:
                    default:
                        measuredHeight += (layoutParams.height + margin);
                        break;
                }
            }
        }
        if(measuredHeight > heightSize)
            super.onMeasure(recycler, state, widthSpec, heightSpec);
        else
            setMeasuredDimension(measuredWidth, measuredHeight);
    }
}

实际使用:

recyclerView = (RecyclerView)findViewById(R.id.sv_rv);
recyclerView.setLayoutManager(new RecyclerLayoutManager(getApplicationContext()));
recyclerView.setAdapter(new RvAdapter());

实际效果:
这里写图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值