直接在scrollView中写listview等可滚动控件会出现子控件高度计算的问题,为了解决这个问题,找到的方案是重写listview中的onmeasure方法:
@Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); }
但是这样造成的问题是listview会显示出所有的内容,如果listview的数量有100条,那么就很酸爽了.其实说到底还是listview不能滚动,只要让listview可以滚动,问题可以完美解决.
@Override 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); .... 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; }
再来看看measureHeightOfChildren是怎么实现的
final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition, 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, maxHeight); 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; }
如此看来,就知道,我们可以在listview的onmeasure方法中判断一下,如果listview是写死的高度,那么就将这个死的高度作为listview的最大高度值传给super.measure();否则,传入INTEGER.MAX_VALUE >> 2,这样一来,如果xml文件里面的listview的高度是wrapconten,那么就会显示所有的listview,如果写死,那么listview就是写死的高度,接下来的问题是,如果listview的高度是死的,怎么让listview自由滚动,很简单,android系统提供给我们一个方法,可以拦截或者不拦截touch事件.
getParent().requestDisallowInterceptTouchEvent(true); true:touch事件由子控件处理 false:touch事件由父类处理
最后附上完整代码:
package com.bbd.picturesel.widgets; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.ListView; /** * Created by penny on 2016/4/25. */ public class ScrollListView extends ListView { public ScrollListView(Context context) { this(context, null); } public ScrollListView(Context context, AttributeSet attributeSet) { this(context, attributeSet, 0); } public ScrollListView(Context context, AttributeSet attributeSet, int defStyleAttr) { super(context, attributeSet, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int maxsize = measureHeight(Integer.MAX_VALUE >> 2, heightMeasureSpec); int expandSpec = MeasureSpec.makeMeasureSpec(maxsize, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } /** * @param defaultMaxSize listview的最大高度默认值 * @param heightMeasureSpec * @return 计算之后的listview的最大高度 */ private int measureHeight(int defaultMaxSize, int heightMeasureSpec) { int result = 0; int measureMode = MeasureSpec.getMode(heightMeasureSpec); int measureSize = MeasureSpec.getSize(heightMeasureSpec); if (measureMode == MeasureSpec.EXACTLY) { result = measureSize; } else { result = defaultMaxSize; if (measureMode == MeasureSpec.AT_MOST) { result = Math.min(defaultMaxSize, measureSize); } } return result; } /** * 判断listview是否处于最底部 * * @return */ private boolean isBottom() { boolean isBottom = false; int firstItemId = getFirstVisiblePosition(); int currentScreenCount = getChildCount(); int total = getCount(); if (firstItemId + currentScreenCount >= total) { isBottom = true; } return isBottom; } /** * 判断listview是否处于最顶部 * * @return */ private boolean isTop() { int firstVisiblePosition = getFirstVisiblePosition(); if (firstVisiblePosition == 0) { return true; } else return false; } private float y = 0; private float touchDown = 0; @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: touchDown = ev.getRawY(); getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: y = ev.getRawY(); if (isTop() && isBottom()) {//listview已全部显示,不自由滚动 getParent().requestDisallowInterceptTouchEvent(false); } else if (isTop()) {//listview目前在最上部 if (y - touchDown > 1) { getParent().requestDisallowInterceptTouchEvent(false); } else { getParent().requestDisallowInterceptTouchEvent(true); } } else if (isBottom()) {//listview目前在最下部 if (y - touchDown > 1) {//由listview处理touch事件 getParent().requestDisallowInterceptTouchEvent(true); } else {//由父控件处理touch事件 getParent().requestDisallowInterceptTouchEvent(false); } } break; default: break; } return super.dispatchTouchEvent(ev); } }
感谢原作者:http://blog.csdn.net/wanghao200906/article/details/51084975
时间: 2024-10-05 14:26:47