零用钱的首页面中使用了这种布局,刚开始会疑惑怎么会有这种奇怪的布局,ScrollView和ListView都是滚动结构,按理来说,这两个控件在UI上可以实现相同的功能,ScrollView中只能放一个控件,通常都是LinearLayout,然后在LinearLayout中添加相应的控件来实现滚动效果。
为什么要使用这么奇怪的布局?
1. ScrollView中只能放一个控件,默认情况下Android是禁止在ScrollView中放入另外的ScrollView的,它的高度是无法计算。
2. ScrollView中嵌套了多个ListView,并且作为整体一起滚动,有了这样的设计需求,于是就有了ScrollView嵌套ListView的奇怪结构。
1. 最简单的布局
如果整个页面只有一个ListView的话,那么由于ListView本身带有滚动效果所以当加载的数据超过页面显示的范围时,可以通过上下滑动来查看所有的item。因此这种情况下,不需要添加ScrollView。
2.布局A+ListView
这种情况下,如果布局A定义在ListView的前面,那么当布局A所占的比例较大,或者ListView加载的数据较多时,都会导致ListView显示不完全。同样,由于ListView自身可以滚动,因此仍然可以通过上下滚动来查看ListView的所有item。
如图所示:
3.其它布局B+ListView
这种情况下,假设布局B定义在ListView的后面,那么就会出现两种情况:
(1)、ListView加载的数据不多,可以完全显示ListView的每一项,那么如果后面还有足够剩余的空间的话,布局B能正常显示;
(2)、ListView加载的数据加多,那么就会导致留给布局B的空间不足,或者根本就没有,布局B将会显示不完全或者完全不显示。
零用钱中则是在滚动布局中添加了三个ListView,我也在代码中尝试过自己在ScrollView中加入ListView,发现ListView的数据加载不全,查了一些资料,对项目中代码有了进一步了解。
设置ListView高度
ListView中的数据是可变的,实际高度需要实际测量。于是需要在代码设置ListView高度的方法。
public void setListViewHeightBasedOnChildren(ListView listView) {
HomeFragmentAdapter listAdapter = (HomeFragmentAdapter)listView.getAdapter();
if (listAdapter == null) {
return;
}
int totalHeight = listView.getPaddingTop() + listView.getPaddingBottom();
for (int i = 0, len = listAdapter.getCount(); i < len; i++) {
// listAdapter.getCount()返回数据项的数目
View listItem = listAdapter.getView(i, null, listView);
if (listItem instanceof ViewGroup) {
listItem.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
listItem.measure(0, 0); // 计算子项的高度
totalHeight += listItem.getMeasuredHeight();// 统计所有子项的总高度
}
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
// listView.getDividerHeight()获取子项间分隔符占用的高度
// params.height最后得到整个ListView完整显示需要的高度
// DANGEROUS , NEVER NEVER NEVER NEVER NEVER NEVER NEVER DELETE THIS !!!!!!!!!!!!!!!!!!!!!!
Toast.makeText(getActivity(), "i am the point " + params.height, Toast.LENGTH_SHORT);
listView.setLayoutParams(params);
}
调用方法为:
ListView listView = (ListView) findViewById(id);
YourAdapter adapter = new MyAdapter("初始化你的适配器");
listView.setAdapter(adapter);
setListViewHeightBasedOnChildren(listView);
上面这个方法就是设定ListView的高度了,在为ListView设置了Adapter之后使用,就可以解决问题了。但是这个方法有个细节需要注意:
Adapter中getView方法返回的View的必须由LinearLayout组成,因为只有LinearLayout才有measure()方法,如果使用其他的布局如RelativeLayout,在调用listItem.measure(0, 0)
;时就会抛异常,因为除LinearLayout外的其他布局的这个方法就是直接抛异常的。
需要手动把ScrollView滚动至最顶端 homeScroll.scrollTo(0, 0);