android scrollview listview 加载更多,Android Scrollview 嵌套 ListView 显示不全的原因和解决方法...

Scrollview 嵌套 ListView 显示不全的原因

public class ScrollView extends FrameLayout {

...

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

...

}

...

}

ScrollView 继承 FrameLayout,FrameLayout -> onMeasure()

public class FrameLayout extends ViewGroup {

...

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

...

for (int i = 0; i < count; i++) {

final View child = getChildAt(i);

if (mMeasureAllChildren || child.getVisibility() != GONE) {

...

measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

...

}

}

...

}

}

ViewGroup -> measureChildWithMargins()

public abstract class ViewGroup extends View implements ViewParent, ViewManager {

...

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 childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin+ heightUsed, lp.height);

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

...

}

ScrollView 重写了 measureChildWithMargins() 方法

public class ScrollView extends FrameLayout {

...

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

final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(

Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec)

- usedTotal) ,MeasureSpec.UNSPECIFIED);

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

...

}

ScrollView 在测量时会将子布局的 Mode 设置为 MeasureSpec.UNSPECIFIED

ListView -> onMeasure()

public class ListView extends AbsListView {

...

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

...

if (heightMode == MeasureSpec.UNSPECIFIED) {

heightSize = mListPadding.top + mListPadding.bottom + childHeight +

getVerticalFadingEdgeLength() * 2;

}

...

setMeasuredDimension(widthSize, heightSize);

...

}

...

}

当 heightMode 为 MeasureSpec.UNSPECIFIED 时,ListView 的真实高度为一个 Item 的高度,这就是 ScrollView 嵌套 ListView 显示不全的原因

Scrollview 嵌套 ListView 显示不全的解决方法

public class MyListView extends ListView {

...

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,

MeasureSpec.AT_MOST);

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

}

重写 onMeasure(),重新设置 heightMeasureSpec

为什么这样写能解决问题 ?

为什么将 Mode 设置成 MeasureSpec.AT_MOST ?

public class ListView extends AbsListView {

...

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

...

if (heightMode == MeasureSpec.UNSPECIFIED) {

heightSize = mListPadding.top + mListPadding.bottom + childHeight +

getVerticalFadingEdgeLength() * 2;

}

if (heightMode == MeasureSpec.AT_MOST) {

heightSize = measureHeightOfChildren(widthMeasureSpec, 0,

NO_POSITION, heightSize, -1);

}

...

setMeasuredDimension(widthSize, heightSize);

...

}

...

}

上面我们将 Mode 设置成 MeasureSpec.AT_MOST,它不会执行 MeasureSpec.UNSPECIFIED 的条件判断,会执行后面的条件判断

Integer.MAX_VALUE >> 2 为什么会向右平移 2 位 ?

// MeasureSpec 是 View 中的一个静态内部类

public static class MeasureSpec {

...

public static final int UNSPECIFIED = 0 << MODE_SHIFT;

public static final int EXACTLY = 1 << MODE_SHIFT;

public static final int AT_MOST = 2 << MODE_SHIFT;

...

public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,

@MeasureSpecMode int mode) {

if (sUseBrokenMakeMeasureSpec) {

return size + mode;

} else {

return (size & ~MODE_MASK) | (mode & MODE_MASK);

}

}

...

}

makeMeasureSpec 方法会根据 API 大小来执行判断,如果 API <= 17

sUseBrokenMakeMeasureSpec 就为 true,否则为 false,默认为 false

MeasureSpec 中模式只有 3 种 :「UNSPECIFIED」「EXACTLY」「AT_MOST」

它们对应的值分别为 0、1、2,对应的二进制为 00、01、10

MeasureSpec 是 32 位的 int 值,刚才 makeMeasureSpec 设置了「AT_MOST」模式占 2 位,所以设置值时会向右平移 2 位。

ScrollView 嵌套 ListView 显示不全的原因和解决方法以及为什么这样设置介绍到这里。如果有什么写得不对的,可以在下方评论留言,我会第一时间改正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值