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

来源:互联网 发布:淘宝网站首页源代码 编辑:程序博客网 时间:2024/05/17 07:44

前言

最近,在使用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 0
原创粉丝点击