zoukankan      html  css  js  c++  java
  • scrollView中可以自由滚动的listview

    直接在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

  • 相关阅读:
    永久破解之phpstorm
    CPU线程和Java线程
    kvm自动扩容内存
    swoole http server 信息
    mac 安装PHP redis扩展phpredi-5.0.2
    el-tree,很恶心的树,就从默认展开节点开始吧
    转开发啦,就从最基础的数据转换开始吧
    Java集合系列-HashSet
    Java基础系列-单例的7种写法
    Java基础系列-Lambda
  • 原文地址:https://www.cnblogs.com/yiludugufei/p/5434100.html
Copyright © 2011-2022 走看看