ListView设置adapter时getView被多次调用
2015-12-16 11:49
323 查看
今天在做项目的时候,发现ListView在高度为wrap_content时,adater中的getView方法居然被调用了三次,于是在网上搜索
发现也有网友与我碰到的问题相同,但有的只是说把wrap_content改为match_parent即可,但是亲测不行,这里就不吐槽一些只是转发别人的博客的网友了
不过还是发现了一些问题,那就是ListView被指定为wrap_content时无法确定高度,所以才会调用多次getView
看了下ListView的代码
这里就是当ListView高度被指定为wrap_content走的代码
下面先看下 measureHeightOfChildren 方法
多的代码先不看了 先看这行
调用了AbsListView中的obtainView方法
这里调用了adapter 的getView方法
好了,下面看下obtainView 在ListView中被调用了多次,都是什么时候被调用的,上面这次是指定高度为wrap_content时 在onMeasure方法中调用 的
这个看注释是将子view添加到list中调用的
别的不看了,还有好多,到这里就是说,当ListView的高度被指定为wrap_content时getView应该至少会被调用两次
解决方法在网上搜到一个
参考:/article/1914154.html
亲测在ListView外部套一层RelativeLayout,ListView 高度设置为match_parent 生效
发现也有网友与我碰到的问题相同,但有的只是说把wrap_content改为match_parent即可,但是亲测不行,这里就不吐槽一些只是转发别人的博客的网友了
不过还是发现了一些问题,那就是ListView被指定为wrap_content时无法确定高度,所以才会调用多次getView
看了下ListView的代码
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); }
这里就是当ListView高度被指定为wrap_content走的代码
下面先看下 measureHeightOfChildren 方法
/** * Measures the height of the given range of children (inclusive) and * returns the height with this ListView's padding and divider heights * included. If maxHeight is provided, the measuring will stop when the * current height reaches maxHeight. * * @param widthMeasureSpec The width measure spec to be given to a child's * {@link View#measure(int, int)}. * @param startPosition The position of the first child to be shown. * @param endPosition The (inclusive) position of the last child to be * shown. Specify {@link #NO_POSITION} if the last child should be * the last available child from the adapter. * @param maxHeight The maximum height that will be returned (if all the * children don't fit in this value, this value will be * returned). * @param disallowPartialChildPosition In general, whether the returned * height should only contain entire children. This is more * powerful--it is the first inclusive position at which partial * children will not be allowed. Example: it looks nice to have * at least 3 completely visible children, and in portrait this * will most likely fit; but in landscape there could be times * when even 2 children can not be completely shown, so a value * of 2 (remember, inclusive) would be good (assuming * startPosition is 0). * @return The height of this ListView with the given children. */ final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition, final int maxHeight, int disallowPartialChildPosition) { final ListAdapter adapter = mAdapter; if (adapter == null) { return mListPadding.top + mListPadding.bottom; } // Include the padding of the list int returnedHeight = mListPadding.top + mListPadding.bottom; final int dividerHeight = ((mDividerHeight > 0) && mDivider != null) ? mDividerHeight : 0; // The previous height value that was less than maxHeight and contained // no partial children int prevHeightWithoutPartialChild = 0; int i; View child; // mItemCount - 1 since endPosition parameter is inclusive endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition; final AbsListView.RecycleBin recycleBin = mRecycler; final boolean recyle = recycleOnMeasure(); final boolean[] isScrap = mIsScrap; for (i = startPosition; i <= endPosition; ++i) { child = obtainView(i, isScrap); measureScrapChild(child, i, widthMeasureSpec); if (i > 0) { // Count the divider for all but one child returnedHeight += dividerHeight; } // Recycle the view before we possibly return from the method if (recyle && recycleBin.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { recycleBin.addScrapView(child, -1); } returnedHeight += child.getMeasuredHeight(); if (returnedHeight >= maxHeight) { // We went over, figure out which height to return. If returnedHeight > maxHeight, // then the i'th position did not fit completely. return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1) && (i > disallowPartialChildPosition) // We've past the min pos && (prevHeightWithoutPartialChild > 0) // We have a prev height && (returnedHeight != maxHeight) // i'th child did not fit completely ? prevHeightWithoutPartialChild : maxHeight; } if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) { prevHeightWithoutPartialChild = returnedHeight; } } // At this point, we went through the range of children, and they each // completely fit, so return the returnedHeight return returnedHeight; }
多的代码先不看了 先看这行
child = obtainView(i, isScrap);
调用了AbsListView中的obtainView方法
/** * Get a view and have it show the data associated with the specified * position. This is called when we have already discovered that the view is * not available for reuse in the recycle bin. The only choices left are * converting an old view or making a new one. * * @param position The position to display * @param isScrap Array of at least 1 boolean, the first entry will become true if * the returned view was taken from the scrap heap, false if otherwise. * * @return A view displaying the data associated with the specified position */ View obtainView(int position, boolean[] isScrap) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView"); isScrap[0] = false; // Check whether we have a transient state view. Attempt to re-bind the // data and discard the view if we fail. final View transientView = mRecycler.getTransientStateView(position); if (transientView != null) { final LayoutParams params = (LayoutParams) transientView.getLayoutParams(); // If the view type hasn't changed, attempt to re-bind the data. if (params.viewType == mAdapter.getItemViewType(position)) { final View updatedView = mAdapter.getView(position, transientView, this); // If we failed to re-bind the data, scrap the obtained view. if (updatedView != transientView) { setItemViewLayoutParams(updatedView, position); mRecycler.addScrapView(updatedView, position); } } // Scrap view implies temporary detachment. isScrap[0] = true; return transientView; } final View scrapView = mRecycler.getScrapView(position); final View child = mAdapter.getView(position, scrapView, this); if (scrapView != null) { if (child != scrapView) { // Failed to re-bind the data, return scrap to the heap. mRecycler.addScrapView(scrapView, position); } else { isScrap[0] = true; child.dispatchFinishTemporaryDetach(); } } if (mCacheColorHint != 0) { child.setDrawingCacheBackgroundColor(mCacheColorHint); } if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); } setItemViewLayoutParams(child, position); if (AccessibilityManager.getInstance(mContext).isEnabled()) { if (mAccessibilityDelegate == null) { mAccessibilityDelegate = new ListItemAccessibilityDelegate(); } if (child.getAccessibilityDelegate() == null) { child.setAccessibilityDelegate(mAccessibilityDelegate); } } Trace.traceEnd(Trace.TRACE_TAG_VIEW); return child; }
这里调用了adapter 的getView方法
final View updatedView = mAdapter.getView(position, transientView, this);
好了,下面看下obtainView 在ListView中被调用了多次,都是什么时候被调用的,上面这次是指定高度为wrap_content时 在onMeasure方法中调用 的
/** * Obtain the view and add it to our list of children. The view can be made * fresh, converted from an unused view, or used as is if it was in the * recycle bin. * * @param position Logical position in the list * @param y Top or bottom edge of the view to add * @param flow If flow is true, align top edge to y. If false, align bottom * edge to y. * @param childrenLeft Left edge where children should be positioned * @param selected Is this position selected? * @return View that was added */ private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) { View child; if (!mDataChanged) { // Try to use an existing view for this position child = mRecycler.getActiveView(position); if (child != null) { // Found it -- we're using an existing child // This just needs to be positioned setupChild(child, position, y, flow, childrenLeft, selected, true); return child; } } // Make a new view for this position, or convert an unused view if possible child = obtainView(position, mIsScrap); // This needs to be positioned and measured setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child; }
这个看注释是将子view添加到list中调用的
别的不看了,还有好多,到这里就是说,当ListView的高度被指定为wrap_content时getView应该至少会被调用两次
解决方法在网上搜到一个
参考:/article/1914154.html
亲测在ListView外部套一层RelativeLayout,ListView 高度设置为match_parent 生效
相关文章推荐
- 处理多重共线性
- 详解SQL Server连接(内连接、外连接、交叉连接)
- MAVEN教程——入门
- 下一代Android渠道打包工具
- 递归删除文件夹目录下所有文件
- 利用spring,实现package下的类扫描
- String类型字符串的操作
- 3D Touch入门
- 微信朋友圈技术之道:三个人的后台团队与每日十亿的发布量
- MDK —— configuration wizard
- jquery限制文本框只能输入数字、JQuery 限制文本框只能输入数字和小数点
- (android:windowIsTranslucent)影响(android:windowAnimationStyle)Activity切换动画无效
- HashMap实现原理
- 【Linux】vi/vim的使用
- 手机蓝牙通讯
- preg_match长字符串匹配失败问题
- loadrunner常见问题分析及理论小知识
- Centos下防止ssh暴力破解密码的方法
- Adding item animations to ListView
- sql基础的基础