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

  • 相关阅读:
    流处理 —— Spark Streaming中的Window操作
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.8 提供带注解的限定符元数据
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.7 为自动检测组件提供作用域
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.6 给自动检测组件命名
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.5 在组件中定义bean的元数据
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.4 使用过滤器自定义扫描
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.3 自动检测类和注册bean的定义
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.2 元注解
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10.1 @Component和深层的构造型注解
    Spring框架参考手册(4.2.6版本)翻译——第三部分 核心技术 6.10 类路径扫描和被管理的组件
  • 原文地址:https://www.cnblogs.com/yiludugufei/p/5434100.html
Copyright © 2011-2022 走看看