过两天同学要回家相亲结婚去了,昨晚作为送行之聚,喝的是稀里哗啦。早上起来喝了一杯牛奶后却无所事事,所以干脆把前几天做项目时总结的一些知识梳理一下。
其实标题与本文不算太相符,这里借标题引申出一个特殊的布局,进而再去挖掘这种布局的解决思路。这种布局很常见,拿美团首页来说,顶部是一些杂七麻八的布局显示一些分类模块和热门频道之类的,中间是listview,底部是加载更多和其他模块。
这种布局有一个特点就是滑动屏幕可以整体滑动而不是仅仅中间的listview可以滑动,不由得就让人突发奇想——scrollview嵌套listview。好想法,可是真正嵌套的时候却发现listview本来一次性请求有10条数据却显示一条甚至还不完整,并且滑动listview只能在显示一条数据的空间高度上滑动,好别扭吧。造成这种结果的原因是scrollview作为父级滑动控件,listview作为孩子,那么scrollview便会把listview的滑动效果给消费掉,造成listview不知道自身该有多高,也就不能展开了。google一下,android官方也不建议采用类似滑动嵌套滑动的这种奇怪布局。但是需求摆在这里,问题终归要解决。知道了上边的原因问题就好办了。
1.动态计算listview的高度
/**
* 动态设置ListView的高度
* @param listView
*/
private void setListViewHeightBasedOnChildren(ListView listView) {
if(listView == null)
return;
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));
listView.setLayoutParams(params);
}
2.自定义listview重写onMeasure()方法
public class ListViewForScrollView extends ListView {
public ListViewForScrollView(Context context) {
super(context);
}
public ListViewForScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ListViewForScrollView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
方法1和方法2都有一些弊端:
a.显示的时候先显示的是listview的首项,而不是scrollview的顶部,所以视图实例后现将scrollview滑动到顶端。
scrollview = (ScrollView) findViewById(R.id.scrollview);
scrollview.smoothScrollTo(0, 0);
b.最令我不能接受的,这样动态计算获取listview的高度,结果是有多少数据listview就显示什么样的高度, 既全部显示,你上下滑动的时候listview的重用机制就失效了,数据量大的话内存会一个劲的往上涨,这样做是不合理的。当然数据量少的话是可以采用的,尤其是方法2,由listview重写onMeasure一口气测量,速度更快。
c.第一种方法的item必须是LinearLayout,是利用LinearLayout的measure()方法来测量的。
3.自定义LinearLayout来取代listview
既然scrollview里嵌套listveiw的这种滑动嵌套滑动容易出现问题,我们何不用LinearLayout来取代listview。Linearlayout有一个addvie()方法,可以将每一个item添加进去形成LinearLayout的子项,这样我们不需要计算高度的问题了。但是同样有一个问题,还是不能重用。
4.为listview设定一个高度。
遇到数据量大的情况,我们事先已经知道,那么干脆初步给listview预设一个高度,然后在嵌套在scrollview中,这样既能重用数据,又能整体滑动,也不失为一种解决方法。
5.整体只用listview一个滑动控件
以上三种都是用scrollview嵌套listview。换种思维,我们知道listview里面有addHeaderView(),addFooterView(),addView()。可以利用三个方法在合适的时候将相应部分的数据添加进去,另外listview的所有属性都可以使用,包括重用机制。个人推荐。