深入理解布局容器绘制,解决ListView嵌套listview,或者ScrollView嵌套listview,gridview的高度问题解决方法
来源:互联网 发布:ipad1越狱后安装软件 编辑:程序博客网 时间:2024/06/10 10:23
当然,在做的时候可能有很多方法,当然这也不是最好的方法。
首先、我们要熟知android界面是怎么绘制出来的。今天就简单的谈一下,我们都知道,布局容器绘制经过三个步骤,就是测量,布局,绘制,对应到代码中就是
onMeasure-----------onLayout-----------onDraw。哪具体是怎么绘制的呢?下面是系统源码。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mOrientation == VERTICAL) { measureVertical(widthMeasureSpec, heightMeasureSpec); } else { measureHorizontal(widthMeasureSpec, heightMeasureSpec); } }以LinearLayout为例,从源码可以看出,系统先判断布局方向,才开始测量。for (int i = 0; i < count; ++i) { final View child = getVirtualChildAt(i); if (child == null) { mTotalLength += measureNullChild(i); continue; } if (child.getVisibility() == View.GONE) { i += getChildrenSkipCount(child, i); continue; } if (hasDividerBeforeChildAt(i)) { mTotalLength += mDividerHeight; } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); totalWeight += lp.weight; final boolean useExcessSpace = lp.height == 0 && lp.weight > 0; if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) { final int totalLength = mTotalLength; mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin); skippedMeasure = true; } else { if (useExcessSpace) { lp.height = LayoutParams.WRAP_CONTENT; } final int usedHeight = totalWeight == 0 ? mTotalLength : 0; measureChildBeforeLayout(child, i, widthMeasureSpec, 0, heightMeasureSpec, usedHeight); final int childHeight = child.getMeasuredHeight(); if (useExcessSpace) { lp.height = 0; consumedExcessSpace += childHeight; } final int totalLength = mTotalLength; mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child)); if (useLargestChild) { largestChildHeight = Math.max(childHeight, largestChildHeight); } } if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) { mBaselineChildTop = mTotalLength; } if (i < baselineChildIndex && lp.weight > 0) { throw new RuntimeException("A child of LinearLayout with index " + "less than mBaselineAlignedChildIndex has weight > 0, which " + "won't work. Either remove the weight, or don't set " + "mBaselineAlignedChildIndex."); } boolean matchWidthLocally = false; if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) { matchWidth = true; matchWidthLocally = true; } final int margin = lp.leftMargin + lp.rightMargin; final int measuredWidth = child.getMeasuredWidth() + margin; maxWidth = Math.max(maxWidth, measuredWidth); childState = combineMeasuredStates(childState, child.getMeasuredState()); allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; if (lp.weight > 0) { weightedMaxWidth = Math.max(weightedMaxWidth, matchWidthLocally ? margin : measuredWidth); } else { alternativeMaxWidth = Math.max(alternativeMaxWidth, matchWidthLocally ? margin : measuredWidth); } i += getChildrenSkipCount(child, i); } if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) { mTotalLength += mDividerHeight; }很明显:
1、系统将会拿出嵌套的容器组件的子控件,进行测量然后记录总的高度或宽度,要注意要么测量高度,要么测量宽度取决于布局方向,当然相对布局没有该方法,他是通过相对比较来绘制的。if (child == null) { mTotalLength += measureNullChild(i); continue; }2、判断非容器类控件是否是GONE。如果是就不进行测量if (child.getVisibility() == View.GONE) { i += getChildrenSkipCount(child, i); continue; }
3、判断是否包含边线,如果有记录线的高度if (hasDividerBeforeChildAt(i)) { mTotalLength += mDividerHeight; }
4、最后才去测量,控件真正的高度。并作记录final LayoutParams lp = (LayoutParams) child.getLayoutParams();
5、将所得的宽和高,和最大尺寸作对比。
但这些似乎好像和正提没关系。那为什么ListView嵌套listview,或者ScrollView嵌套listview,gridview无法绘制高度呢?
原来,在设计中他只会测量非容器布局的高度,因此当这样嵌套因为都是要动态测量的所以获取不到。就达不到我们想要的效果
第一步中测量非容器布局的源码如下:
View getVirtualChildAt(int index) { return getChildAt(index); }
那我们怎样解决呢?
下面就让我教你怎么做:
当ListView嵌套listview,或者ScrollView嵌套listview,gridview外层的布局测量到内层发现没有非容器布局,因此高度就是自身高度,而测量又是从最内(上)层开始测量的,测量后并不给自身设置布局参数。
因此呢,内层listview是能测量到item高度的,而外层已经限制了宽和高,所以就不能达到效果。为了达到效果我们要让外层容器认为内层是个非容器布局,是一个有固定高度的容器。具体我们就要必须给内层容器设置布局参数,具体如下:这是封装后的listview,解决办法,没封装,当然就不需要holder了public class LvHeightUtil { public static void setListViewHeightBasedOnChildren(ListView listView, BaseHolder holder) { ListAdapter listAdapter = listView.getAdapter(); if (listAdapter == null) { return; } int totalHeight = 0; for (int i = 0; i < listAdapter.getCount(); i++) {// View listItem = listAdapter.getView(i, holder.mItemRootView, listView);// listItem.measure(0, 0);// totalHeight += listItem.getMeasuredHeight(); totalHeight +=150;//如果是lv嵌套lv某种条件下需要固定item高度,不然测不出来。 } ViewGroup.LayoutParams params = listView.getLayoutParams(); params.height = UIUtil.dip2px(totalHeight) + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); listView.setLayoutParams(params); } public static void setGridViewHeightBasedOnChildren(GridView gridView ,BaseHolder holder) { ListAdapter adapter = gridView.getAdapter(); if (adapter == null) { return; } int totalHeight = 0; for (int i = 0; i < adapter.getCount()/2; i++) { View listItem = adapter.getView(i, holder.mItemRootView, gridView); listItem.measure(0, 0); totalHeight += listItem.getMeasuredHeight(); } ViewGroup.LayoutParams params = gridView.getLayoutParams(); params.height = totalHeight; gridView.setLayoutParams(params); }}
listView源码protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Sets up mListPadding super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int childWidth = 0; int childHeight = 0; int childState = 0; mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { final View child = obtainView(0, mIsScrap); // Lay out child directly against the parent measure spec so that // we can obtain exected minimum width and height. measureScrapChild(child, 0, widthMeasureSpec, heightSize); childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (recycleOnMeasure() && mRecycler.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { mRecycler.addScrapView(child, 0); } } if (widthMode == MeasureSpec.UNSPECIFIED) { widthSize = mListPadding.left + mListPadding.right + childWidth + getVerticalScrollbarWidth(); } else { widthSize |= (childState & MEASURED_STATE_MASK); } if (heightMode == MeasureSpec.UNSPECIFIED) { heightSize = mListPadding.top + mListPadding.bottom + childHeight + getVerticalFadingEdgeLength() * 2; } if (heightMode == MeasureSpec.AT_MOST) { // TODO: after first layout we should maybe start at the first visible position, not 0 heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1); } setMeasuredDimension(widthSize, heightSize); mWidthMeasureSpec = widthMeasureSpec; }看了这些,相信你也能解决这些小问题了。
0 0
- 深入理解布局容器绘制,解决ListView嵌套listview,或者ScrollView嵌套listview,gridview的高度问题解决方法
- ScrollView嵌套GridView或者ListView
- 解决scrollview 嵌套gridview或者listview 冲突的问题
- ScrollView 嵌套listview解决listview高度问题
- ScrollView中嵌套ListView或者GridView时的滑动冲突 || ListView中嵌套GridView,GridView只显示一行的问题解决
- 解决ScrollView嵌套ListView和GridView冲突只显示一行Item的高度的方法
- 解决ScrollView嵌套ListView和GridView冲突的方法
- 解决ScrollView嵌套ListView和GridView冲突的方法
- 解决ScrollView嵌套ListView和GridView冲突的方法
- 解决ScrollView嵌套ListView和GridView冲突的方法
- 解决ScrollView嵌套ListView和GridView冲突的方法
- 解决ScrollView嵌套ListView和GridView冲突的方法
- 解决ScrollView嵌套ListView和GridView冲突的方法
- ScrollView嵌套ListView的问题解决方法
- ScrollView嵌套GridView、ListView
- ScrollView嵌套ListView、GridView
- ScrollView 嵌套 ListView GridView
- ScrollView嵌套ListView的问题解决
- 如何调试异步加载的js文件
- maven中maven dependencies中依赖出现了问题
- 1081. Rational Sum (20)-PAT甲级
- 数组
- 题目1251:序列分割
- 深入理解布局容器绘制,解决ListView嵌套listview,或者ScrollView嵌套listview,gridview的高度问题解决方法
- dubbo总结(2)——dubbo的使用场景
- 磁盘文件最优存储
- 题目1252:回文子串
- MySQL 5.7.17 免安装版本的安装与配置
- 【BNUOJ】Borrow Classroom
- 手把手git教程(08)--开发分支常用处理流程
- webstorm实用功能
- 如何获取短信内容