最近因为项目的需要,要在一个 ScrollView 中放入一个ListView,虽然不推荐这样的设计,尽量不要让两者嵌套,但有时候还是要不得已而为之。发现 ScrollView中放入ListView后,后者的高度有问题,无论怎么设置高度,大约只显示一行半的高度,除非将ListView的高度设定死,但ListView高度根据数据的不同,高度肯定也不同,如果设置死的话滑动又成问题,想来,ListView本身应该具备滑动功能,两个具备滑动功能的控件嵌套在一起的话,可能就会出问题了吧,父容器可能将子控件的滑动事件覆盖掉了吧,再说,两个控件的高度都无法计算,所以不推荐嵌套在一起使用。
解决思路也很简单, 就是在设置完ListView的Adapter后,根据ListView的子项目重新计算ListView的高度,然后把高度再作为LayoutParams设置给ListView,这样它的高度就正确了。代码如下:
public void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
((MarginLayoutParams) params).setMargins(10, 10, 10, 10);
listView.setLayoutParams(params);
}
在设置ListView的Adapter后调用此方法即可让ListView正确的显示在 ScrollView中。但是要注意的是,ListView的每个Item必须是LinearLayout,不能是其他的,因为其他的Layout(如RelativeLayout)没有重写onMeasure(),所以会在onMeasure()时抛出异常。
解决办法二
重写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);
}
详细代码如下:
(1)方法一:
ListViewTest1Activity.java
package com.biao.listview;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.ListAdapter;
import android.widget.ListView;
/**
* 手工计算listview高度,再设置回去
*
* @date 2014-5-5 下午4:34:57
* @version V1.0
*/
public class ListViewTest1Activity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scrollview_listview_1);
ListView listView = (ListView) findViewById(R.id.moreItemsListView);
MyAdapter adapter = new MyAdapter(this, map());
listView.setAdapter(adapter);
setListViewHeightBasedOnChildren(listView);
}
/**
* 计算listview高度的方法
* @param listView
*/
public void setListViewHeightBasedOnChildren(ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight
+ (listView.getDividerHeight() * (listAdapter.getCount() - 1));
((MarginLayoutParams) params).setMargins(10, 10, 10, 10);
listView.setLayoutParams(params);
}
/**
* 测试数据
*/
private List<HashMap<String, String>> map() {
List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
for (int i = 0; i < 20; i++) {
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", "测试" + i);
data.add(map);
}
return data;
}
}
scrollview_listview_1.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FFF4F4F4" android:orientation="vertical" > <ScrollView android:layout_width="fill_parent" android:layout_height="fill_parent" android:fadingEdge="none" > <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center_horizontal" android:orientation="vertical" > <ListView android:id="@+id/moreItemsListView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:cacheColorHint="#00000000" android:divider="@null" android:dividerHeight="0.0dip" android:fadingEdge="none" /> </LinearLayout> </ScrollView> </LinearLayout>
(2)方法二:
ListViewTest2Activity.java
package com.biao.listview;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
/**
* 使用改写过的onMeasure的listview来重新计算listview高度
*
* @date 2014-5-5 下午4:35:42
* @version V1.0
*/
public class ListViewTest2Activity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scrollview_listview_2);
ListView listView = (MyListView) findViewById(R.id.moreItemsListView);
MyAdapter adapter = new MyAdapter(this, map());
listView.setAdapter(adapter);
}
/**
* 测试数据
*/
private List<HashMap<String, String>> map() {
List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
for(int i = 0 ; i < 20 ; i++){
HashMap<String, String> map = new HashMap<String, String>();
map.put("name", "测试" + i);
data.add(map);
}
return data;
}
}
改写listview
MyListVew.java
package com.biao.listview;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;
/**
* 唯一有用的是改写了onMeasure,重新计算高度
*
* @date 2014-5-5 下午4:46:12
* @version V1.0
*/
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 重写listview高度计算
*
* 个人理解,widthMeasureSpec,heightMeasureSpec由父类传过来,这里将高度设置最大为Integer.MAX_VALUE/2的足够高的高度,
* 让listview可以完全展现出来,展现完后,再由父类包裹起来,那整个listview就可以展现出来了
*
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2 , MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
布局文件scrollview_listview_2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFF4F4F4"
android:orientation="vertical" >
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fadingEdge="none" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:orientation="vertical" >
<com.biao.listview.MyListView
android:id="@+id/moreItemsListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000"
android:divider="@null"
android:dividerHeight="0.0dip"
android:fadingEdge="none" />
</LinearLayout>
</ScrollView>
</LinearLayout>
效果图:
比较方便是重写onMeasure方式,这方式同样适用于scrollview嵌套gridview的情况。
代码见附件