ListView嵌套ScrollView,MeasureSpec相关

来源:互联网 发布:kindle微信推送 知乎 编辑:程序博客网 时间:2024/06/05 03:12

初始情景

需要在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);    }
1 0
原创粉丝点击