嘿,今天的你过的还好吗,今天分享解决ScrollView嵌套ListView无法全部显示问题的经历
首先我们看一下ScrollView源码,发现他继承了FrameLayout,而FrameLayout下的onMeasure方法有如下代码
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
//拿到它的子View然后循环去测量
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//调用父类的方法进行测量,重要方法因为Scrollview重写了这个方法所以出现的问题
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
}
很明显,就是循环查找自己的子View不是GONE的话就调用ViewGroup的measureChildWithMargins方法
发现ScrollView重写了measureChildWithMargins方法
@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +
heightUsed;
<!-//childHeightMeasureSpec的模式设置成了MeasureSpec.UNSPECIFIED
final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
MeasureSpec.UNSPECIFIED);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
代码里ScrollView重写了measureChildWithMargins方法对他的childHeightMeasureSpec进行了设置.设置他的高度的测量模式heightMode为MeasureSpec.UNSPECIFIED.然后调用child.measure(childWidthMeasureSpec,childHeightMeasureSpec);方法
接下来我们在看看ListView的源码
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Sets up mListPadding
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获得高度的模式
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int childHeight = 0
//如果item数量大于0并且有个模式为--UNSPECIFIED执行此方法
if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED
|| heightMode == MeasureSpec.UNSPECIFIED)) {
final View child = obtainView(0, mIsScrap);
childHeight = child.getMeasuredHeight();
//如果高度的模式为UNSPECIFIED执行,我们通过刚刚查看ScrollView的源码发现是这种模式所以此方法为true
if (heightMode == MeasureSpec.UNSPECIFIED) {
heightSize = mListPadding.top + mListPadding.bottom + childHeight +
getVerticalFadingEdgeLength() * 2;
}
if (heightMode == MeasureSpec.AT_MOST) {
// TODO: after first layout we should maybe start at the first visible position, not 0
heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
}
setMeasuredDimension(widthSize, heightSize);
}
这样可以清楚看出第一个判断是如果Item的总数大于0并且它的高或者宽有一个模式为MeasureSpec.UNSPECIFIED就会执行这个方法,正常情况下这个判断应该 为true.就是拿到它的一个item的高度childHeight = child.getMeasuredHeight();紧接着它判断它的高度的模式是否为MeasureSpec.UNSPECIFIED显然也是true所以他就是让它的整个的高度等于childHeight 最后完成setMeasuredDimension(widthSize,heightSize)
那就可以知道出现这个原因是在scrollView中ListView在OnMeasure阶段无法测出实际的高度,我们需要给他设置AT_MOST模式以支持很大的高度。这时候可以自定义一个MyListView 继承自Listview,然后重写onMeasure方法即可:
接下来是我的解决办法
1.创建一个MyListView让他继承ListView
2.重写onMeasure方法
3.调用MyListView
代码实现方法首先是MyListView
public class MyListView extends ListView {
public MyListView(android.content.Context context, android.util.AttributeSet attrs) {
super(context, attrs);
}
/**
* 设置不滚动
* 重写onMeasure方法
*/
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
最后调用MyListView
<cn.ryanliu.mall.util.MyListView
android:id="@+id/list_lv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
测试后结果ok,解决问题
网友评论