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

时间: 2024-10-05 14:26:47

scrollView中可以自由滚动的listview的相关文章

解决android的ListView嵌套在ScrollView中不能被滚动的问题

使用滚动条容易带来一个后果,就是高度和宽度不受控制了, 之前就遇到一个已经有ScrollView的页面需要加个列表listView,然后就发现listView只看到前两行数据,下面的看不到,拉滚动条也只是滚动整个页面,而不是listview. 方法1:让外面的ScollView交出滚动权限,因为listview自身就有滚动条. 方法2:通过计算装在内容后的list高度,然后设置高度. 只要在你给你的listView设置适配器后,再重新设置listview的高度,思路是,算出每行的高度,给list

Android 设置ListView不可滚动 及在ScrollView中不可滚动的设置

http://m.blog.csdn.net/blog/yusewuhen/43706169 转载请注明出处: http://blog.csdn.net/androiddevelop/article/details/38815493 希望得到的效果是ListView不能滚动,但是最大的问题在与ListView Item还必有点击事件,如果不需要点击事件那就简单了,直接设置ListView.setEnable(false); 如果还需要点击事件,滚动与点击都是在ListView Touch处理机制

解决ScrollView中嵌套ListView滚动效果冲突问题

在ScrollView中嵌套使用ListView,ListView只会显示一行到两行的数据.起初我以为是样式的问题,一直在对XML文件的样 式进行尝试性设置,但始终得不到想要的效果.后来在网上查了查,ScrollView和ListView两个View都有滚动的效果,在嵌套使用时起了冲 突,一般不建议两者套用. 下面说说具体解决方案.方案的主要思路就是根据ListView子项重置其高度. 解决方案代码如下: java代码: [html] view plain copy print? /** * 重

PullScrollView详解(六)——延伸拓展(listview中getScrollY()一直等于0、ScrollView中的overScrollBy)

前言:经常说follow your heart.但等到真到这么一天的时候,却很艰难 相关文章: 1.<PullScrollView详解(一)--自定义控件属性>2.<PullScrollView详解(二)--Animation.Layout与下拉回弹>3.<PullScrollView详解(三)--PullScrollView实现>4.<PullScrollView详解(四)--完全使用listview实现下拉回弹(方法一)>5.<PullScroll

ScrollView中嵌套ListView时,listview高度显示的问题

方法一:直接更改listview的控件高度,动态获取(根据条目和每个条目的高度获取) 前几天因为项目的需要,要在一个ListView中放入另一个ListView,也即在一个ListView的每个ListItem中放入另外一个ListView.但刚开始的时候,会发现放入的小ListView会显示不完全,它的高度始终有问题.上网查了下,发现别人也有遇到这样的问题,而大多数人都不推荐这样的设计,因为默认情况下Android是禁止在ScrollView中放入另外的ScrollView的,它的高度是无法计

ScrollView中嵌套ScrollView或ListView并且内部ScrollView或ListView也可滑动

1.ScrollView中嵌套ScrollView并且内部ScrollView也可滑动 (1)ScrollView继承类 public class InnerScrollView extends ScrollView { Handler handler; public ScrollView parentScrollView; public InnerScrollView(Context context, AttributeSet attrs) { super(context, attrs); h

解决ScrollView中嵌套Listview,Listview中嵌套Listview显示不完整和滑动冲突的问题

在一个滑动控件或者是布局里面,添加另外一个可以滑动的控件,通常会造成一些莫名其妙的问题.今天主要介绍在工作中遇到的,在ScrollView布局中嵌套Listview显示不正常,和在Listview中嵌套Listview的滑动冲突的问题. 1.ScrollView布局中嵌套Listview显示不正常的解决方案 目前来说,解决这个问题有好几种解决方案,这里只介绍其中两种比较简单易行的其中两种. (1)自定义一个Listview,继承自Listview,代码如下: public class ListV

[转载]Android: 如何实现ScrollView中含有ListView?

原文地址:Android: 如何实现ScrollView中含有ListView?作者:mailofzxf ListView本身就含有ScrollView,因此把ListView放到ScrollView中会引起混乱(谁来响应滑动事件?)但有时又确有此需求,以实现ListView的内容连同其他内容的滚动. 要想把ListView嵌入ScrollView,有二个方法: 方法1:整体上使用一个ListView, 把不需滚动的部分放入ListView的Header或Footer中.注意: 一定要先添加He

ScrollView中嵌套两个ListView

做的项目中要使用两个ListView在同一个页面上下显示,因为数据源不同,不能通过在Adapter中设置标志位去区分显示,最后只能硬着头皮做一个ScrollView嵌套两个ListView,但按正常情况是不能同时显示的,会出现上面的ListView完全显示,下面的只显示一个Item,查了一些资料终于成功了 主要有一个ListViewUtility ,代码如下: import java.io.File; import android.content.Intent; import android.n