Android ScrollView中的ListView只显示一个条目问题

来源:互联网 发布:淘宝视频要求 编辑:程序博客网 时间:2024/05/14 11:04

在ScrollView中放ListView时,ListView默认只显示一个item。
本文将根据这个问题来分析出现这个问题的原因及解决办法。

xml代码如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical" android:layout_width="match_parent"    android:layout_height="match_parent">    <ScrollView        android:layout_width="match_parent"        android:layout_height="match_parent">        <LinearLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:orientation="vertical">            <ListView                android:id="@+id/lv"                android:layout_width="wrap_content"                android:layout_height="wrap_content">            </ListView>            <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="看我干啥,找挨揍啊!"/>            <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="没脸啊 还看啊"/>        </LinearLayout>    </ScrollView></LinearLayout>

Activity中的代码:

private void initView() {        ListView listView = (ListView)findViewById(R.id.lv);        ArrayList list  = new ArrayList();        for(int i=0;i<30;i++){            list.add("item"+i);        }        ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,list);        listView.setAdapter(adapter);    }

效果图
这里写图片描述

那么为什么出现这样的现象呢?
在之前的博客Android UI绘制流程(二)曾经介绍过影响子View大小的因素不只是子View的MeasureSpec,还和父View的MeasureSpec相关。
要分析ListView的大小,首先要从ListView的父View来着手。所以首先来看ViewGroup中的getChildMeasureSpec()方法。

可以看到,在此方法中

 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {        int specMode = MeasureSpec.getMode(spec);        int specSize = MeasureSpec.getSize(spec);        int size = Math.max(0, specSize - padding);        int resultSize = 0;        int resultMode = 0;        switch (specMode) {        case MeasureSpec.AT_MOST:            if (childDimension >= 0) {                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;                            } else if (childDimension == LayoutParams.MATCH_PARENT) {                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            }            break;        }        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);    }

此方法决定Child的宽和高的MeasureSpec,而决定此MeasureSpec的因素是父View的MeasureSpec和子View的MeasureSpec。

1 子View的MeasureSpec能确定,即ListView中设置的:

 android:layout_width="match_parent" android:layout_height="match_parent"

2 而父View的Measurespec怎么确定呢?
同样的道理,父View的MeasureSpec是由父View的父View即ScrollView和父View即LinearLayout本身设定的宽高来共同决定的。所以追本溯源,来看ScrollView中的onMeasure()方法。

 @Override    protected void measureChild(View child, int parentWidthMeasureSpec,            int parentHeightMeasureSpec) {        ViewGroup.LayoutParams lp = child.getLayoutParams();        int childWidthMeasureSpec;        int childHeightMeasureSpec;        childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft                + mPaddingRight, lp.width);        final int verticalPadding = mPaddingTop + mPaddingBottom;        childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(                Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding),                MeasureSpec.UNSPECIFIED);        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);    }

可以看到其中的这段:

childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(                Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - verticalPadding),                MeasureSpec.UNSPECIFIED);

即ScrollView中的每个子布局的高度的SpecMode都是UNSPECIFIED的。即LinearLayout的heightSpecMode==UNSPECIFIED,现在已知LinearLaout的heightSpecMode了,下面再回头来看之前getChildMeasureSpec()中的这段:

 case MeasureSpec.UNSPECIFIED:            if (childDimension >= 0) {                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;                resultMode = MeasureSpec.UNSPECIFIED;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;                resultMode = MeasureSpec.UNSPECIFIED;            }            break;

这段得出结论,当LinearLayout的heightSpecMode==UNSPECIFIED时,无论在 ListView中写match_parent还是wrap_content其实结果都一样,因为决定ListView的heightSpecSize并不是此参数,而是resultSize。而此时ListViewheightSpecMode=UNSPECIFIED。现在ListView的SpecMode已经去定了,我们先不考虑resultSize具体是多少(后面可以发现,其实当ListView中的SpecMode=UNSPECIFIED时,这个值并不能影响ListView的高度),先来看ListView中的onMeasure()方法:

 @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        childHeight = child.getMeasuredHeight();        if (widthMode == MeasureSpec.UNSPECIFIED) {            widthSize = mListPadding.left + mListPadding.right + childWidth +                    getVerticalScrollbarWidth();        } else {            widthSize |= (childState & MEASURED_STATE_MASK);        }        if (heightMode == MeasureSpec.UNSPECIFIED) {            heightSize = mListPadding.top + mListPadding.bottom + childHeight +                    getVerticalFadingEdgeLength() * 2;        }        setMeasuredDimension(widthSize, heightSize);        mWidthMeasureSpec = widthMeasureSpec;    }

这段代码不全,我只把需要的截取出来了。根据上面代码中的:

if (heightMode == MeasureSpec.UNSPECIFIED) {            heightSize = mListPadding.top + mListPadding.bottom + childHeight +                    getVerticalFadingEdgeLength() * 2;        }

可以得出,当ListVIew的heightMoe==MeasureSpec.UNSPECIFIED时,其高度heightSize的高度为childHeight。至此已经找到了问题的源头。

解决办法:自定义ListView,并在onMeasure方法中修改heightMeasureSpec。这样就可以让ListView默认全部展开。

public class MyListView extends ListView {    public MyListView(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int expandedSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandedSpec);    }}

再布局中使用自定义控件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical" android:layout_width="match_parent"    android:layout_height="match_parent">    <ScrollView        android:layout_width="match_parent"        android:layout_height="match_parent">        <LinearLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:orientation="vertical">            <com.example.practice_click.MyListView                android:id="@+id/lv"                android:layout_width="wrap_content"                android:layout_height="wrap_content">            </com.example.practice_click.MyListView>            <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="看我干啥,找挨揍啊!"/>            <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="没脸啊 还看啊"/>        </LinearLayout>    </ScrollView></LinearLayout>

效果图
这里写图片描述

0 0
原创粉丝点击