ListView嵌套ScrollView,MeasureSpec相关

本文介绍了解决ScrollView中嵌套ListView时出现的滑动冲突问题的方法,包括通过自定义ListView重写onMeasure方法来确保ListView可以展示多个条目,以及调整ListView的触摸事件监听来解决滑动冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初始情景

需要在ScrollView中嵌套ListView中,并且ListView高度有最大值,超出部分滑动展示,不足部分自适应大小。需要解决:
  1. ListView在ScrollView中使用只展示一项的问题;
  2. ListView和ScrollView的滑动冲突问题。

问题解决

只展示一项

重写一个继承自ListView的控件,重写onMeasure()测量方法,重新获取测量大小,并设置代码如下:
public class AgentListView extends ListView {
    public AgentListView(Context context) {
        super(context);
    }

    public AgentListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public AgentListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 重写测量方法,自适应大小的高度,且最大高度约等Integer.MAX_VALUE/4
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }
}

滑动冲突

        mListView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    mScrollView.requestDisallowInterceptTouchEvent(false);
                }else {
                    // 父类不拦截action
                    mScrollView.requestDisallowInterceptTouchEvent(true);
                }
                return false;
            }
        });

onMeasure(widthMeasureSpec, heightMeasureSpec)方法

View中使用此方法进行测量控件大小,传入两个参数,长度和宽度。
每一个int型参数都包含了两个信息:测量模式 & 测量大小。

一个int型整数,如何表示两个信息?
一个int型,4byte,32bit,即32位。
这里使用前两位作为测量模式:UNSPECIFIED, EXACTLY, AT_MOST
后30位表示控件的测量大小。

MeasureSpec源码如下:

public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        // 测量模式注解
        @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
        @Retention(RetentionPolicy.SOURCE)
        public @interface MeasureSpecMode {}

        // UNSPECIFIED  未指定模式  前两位为: 00
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        // EXACTLY  精确测量模式  前两位为: 01
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        // AT_MOST  最大测量模式  前两位为: 10
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        /**
              * 传入测量大小和测量模式,获取 规范测量值
              * @params size 大小范围 0 ~ 2^30-1
              * @params mode MeasureSpecMode模式三选一
              */
        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);
            }
        }

        // 类似上面方法,UNSPECIFIED时返回0,用于兼容
        public static int makeSafeMeasureSpec(int size, int mode) {
            if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
                return 0;
            }
            return makeMeasureSpec(size, mode);
        }

        // 获取测量模式
        @MeasureSpecMode
        public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }
        // 获取测量大小
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
        static int adjust(int measureSpec, int delta) {
            //省略
        }
        public static String toString(int measureSpec) {
           // 省略
        }
    }

如上问题解决,即使用AT_MOST测量模式,且最大值为2^(31-2)-1,也几乎等于有多大就是多大。
下面测量方式,即最大高度为 2400/4=600,实现不足600自适应大小,最大高度600滑动展示剩余项。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(2400 >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值