您的位置:首页 > 移动开发 > Android开发

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

2016-04-14 16:56 666 查看

前言

最近,在使用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());


实际效果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 高度测量