美文网首页安卓面试宝典
Android Scrollview 嵌套 ListView 显

Android Scrollview 嵌套 ListView 显

作者: 是刘航啊 | 来源:发表于2020-12-09 22:24 被阅读0次

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

    相关文章

      网友评论

        本文标题:Android Scrollview 嵌套 ListView 显

        本文链接:https://www.haomeiwen.com/subject/hmzfgktx.html