事件分发机制-ScrollView嵌套ListView问题产生原理以及常见解决方式
来源:互联网 发布:批量注册淘宝小号软件 编辑:程序博客网 时间:2024/06/06 13:23
上一篇说明了事件分发的机制,接下来以一个实际会遇到的场景继续学习事件分发机制,
场景2:ScrollView嵌套ListView,listview只能显示一个item。
分析:
既然是高度问题的话那就先打开ScrollView查看一下onMeasure()方法:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //scrollview继承自FrameLayout,所以执行了framelayout的onMeasure()方法 super.onMeasure(widthMeasureSpec, heightMeasureSpec); //mFillViewport默认为false if (!mFillViewport) { return; } ...
一步一步往下看,
FrameLayout的onMeasure():
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { //最重要的是这一步,测量子view,且该方法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); } } } } ...
重要的就是measureChildWithMargins()这个方法,且srcollview已重写过,所以再看该方法:
@Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//listview的宽模式 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed;//listview的长模式,(留意,这里强行的吧listview的模式设置UNSPECIFIED),这里就是让listview只显示一个item的最主要原因。 final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec( Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal), MeasureSpec.UNSPECIFIED);//测量child。//childWidthMeasureSpec、childWidthMeasureSpec就是listview的测量模式 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
到这里我们知道,scrollview会强行设置他的childview的高模式为UNSPECIFIED,那接下来再看一下listview设置这种模式的话是如何计算其高度的:
listview-onMeasure():
if (heightMode == MeasureSpec.UNSPECIFIED) { heightSize = mListPadding.top + mListPadding.bottom + childHeight + getVerticalFadingEdgeLength() * 2; }
意思就是,在该测量模式下,listview的高度等于垂直方向上的padding+第一个子child的高度+??不影响判断。
至此scrollview嵌套listivew结果只显示一个item的原因已经分析清楚了。
小结:scrollview默认把childview设置为UNSPEFEIED模式,而该模式下的listview给自己的测量的高度就是第一个item的高度。
解决方案:
1.设置mFillViewport为true
看完整的ScrollView的onMeasure()代码:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);//如果mFillViewport为true的话,则后面的代码都能执行,那么会有怎样的效果呢? if (!mFillViewport) { return; } final int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode == MeasureSpec.UNSPECIFIED) { return; } if (getChildCount() > 0) { final View child = getChildAt(0); final int widthPadding; final int heightPadding; final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion; final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (targetSdkVersion >= VERSION_CODES.M) { widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin; heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin; } else { widthPadding = mPaddingLeft + mPaddingRight; heightPadding = mPaddingTop + mPaddingBottom; } final int desiredHeight = getMeasuredHeight() - heightPadding; if (child.getMeasuredHeight() < desiredHeight) { final int childWidthMeasureSpec = getChildMeasureSpec( widthMeasureSpec, widthPadding, lp.width);//测量模式为EXACTLY final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( desiredHeight, MeasureSpec.EXACTLY);//留意childHeightMeasureSpec的测量模式 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }
如果能执行到后面的代码,那么listview就能全部显示。也就是说,mFillViewport的值要设置为true就能解决问题。
可以直接在scrollview的布局中加属性fillViewport = true;也可以动态设置。
2.自定义ListView:
分析源码的目的就是要清楚产生问题的原因,从而能够针对性的提出解决办法,listview只显示一个也是因为自己在onMeasure()测量的问题造成的,毕竟是listview返回一个item的高度给scrollview,scrollview才显示一个item的高度的。所以我们也可以自定义Listview,并重写onMeasure()方法,返回正常的高度给ScrollView。
自定义的ListView只需重写onMeasure()方法即可:
@Override//>>2右移两位是因为MeasureSpec前2是表示模式,后30位才是尺寸 int customHeightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, customHeightSpec); }
还有一种思路就是在用LinearLayout包含listview,那么LinearLayout被设置为UNSPECFIED模式,此时listview必须设置精确的dp值。
如下图,当linearLayout为UNSPECIFIED时,listview必须是精确dp时才能避免也被设置为UNSPECIFIED模式。
这里并未涉及到更多的事件分发的机制。
下一篇我将分析ListView嵌套Scrollview产生的问题,来更好的理解事件分发机制。
感谢阅读!
- 事件分发机制-ScrollView嵌套ListView问题产生原理以及常见解决方式
- 利用事件分发机制解决ScrollView嵌套ListView滑动冲突
- Scrollview嵌套listview利用事件分发机制解决滑动冲突
- 63ScrollView嵌套ListView带来的问题以及解决方式
- ScrollView嵌套ListView带来的问题以及解决方式
- Android 事件分发实践(一),解决ScrollView嵌套ListView滑动的问题
- android事件分发ScrollView中嵌套listview
- ScrollView嵌套ListView(ViewGroup事件分发)
- 解决ScrollView嵌套ListView问题
- 解决ScrollView嵌套ListView问题
- 解决ScrollView嵌套ListView问题
- 解决ScrollView嵌套ListView问题
- 解决ScrollView嵌套ListView问题
- 解决ScrollView嵌套ListView问题
- 解决ScrollView嵌套ListView以及ListView嵌套ListView冲突的问题
- ScrollView 嵌套listview解决listview高度问题
- WebView嵌套在ScrollView引起的滑动问题,以及事件分发情况
- 解决ScrollView 嵌套 ListView GridView显示不全,以及默认不在ScrollView顶部的问题
- Shell编程面试题3_批量修改某一目录下的所有文件名
- Python numpy 操作
- idea 自定义 maven archtype
- 【JavaScript入门】初识JavaScript
- Android--小小记事本
- 事件分发机制-ScrollView嵌套ListView问题产生原理以及常见解决方式
- Spire.Email 教程:在C#,VB.NET中搜索电子邮件
- Node.js包(JXcore)
- java 通过 itext生成pdf添加,插入文字,图片
- MySQL 处理海量数据时的一些优化查询速度方法
- 【机器学习】查全率(Recall)/查准率(Precision)/F-Measure/AP(Average Precision)/MAP
- BluetoothAdapter简介
- linux配置主机名的映射
- Hibernate(五):Hibernate缓存