有关Scrollview嵌套ListView的那些事
同样作为滑动控件的scrollview与listview,似乎本不应该嵌套使用,可作为研发的我们通常无法左右UI妹子们的奇(fan)思(ren)妙(lei)想。作为一个负责任的研发,必须时刻做好兵来将挡的准备。
首先我们来看这样一张设计稿:
整个页面整体是一个带下拉刷新的布局,这里用到了被大家最广泛使用的PullToRefreshLayout,同时由于UI设计风格的不断演变,这个页面的内容也不断增多,于是乎在PullToRefreshLayout内部又放了一个Scrollview来滑动显示更多内容,在最近的一期任务中,新增了车辆操作记录这一项,即图片最下方的那个listview,于是乎这一个小小的页面竟然嵌套了PullToRefreshLayout ,Scrollview 与ListView三个滑动控件。
起初接到设计的时候并没有觉得什么,无非就是再加一个ListView么,简单的很,直接开码,item布局,adapter,分分钟搞定,当服务端接口开发完成,准备联调的时候,瞬间傻眼。无论接到的返回体数组的size为几,listview的显示永远只是可怜的一条。经过一番调查终于发现当listview嵌套在Scrollview中时无法正确计算自己的高度,无法正确计算listview实际的高度,但确刚刚好显示了一条数据,那是不是意味着可以通过listview的adapter获取item的个数,然后根据总item个数与单个item的高度计算出listview的实际高度呢?
手动计算listview高度
public void setListViewHeightBasedOnItem(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);
if (null != listItem) {
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
}
ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
listView.setLayoutParams(params);
}
退出程序,Shift+F10,回车
再次观察运行结果,Perfect,listview的高度被完美的计算出来了。然而帅不过三秒,又出现了新的问题,那就是由于页面内容过多超出了一屏,导致每次进入这个页面,scrollview都自动滚动到了底部,这是什么鬼,好在还没有交付测试组,赶紧整改。
起初走了一些弯路,以为是scrollview的问题,于是写了一个手动让scrollview滑动到顶部的方法。
当开始layout的时候让scrollview滑动到顶部
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
scrollView.post(new Runnable() {
public void run() {
scrollView.fullScroll(ScrollView.FOCUS_UP);
Glide.with(context).load(viewData.pictureUrl)
.error(context.getResources().getDrawable(R.drawable.bg_nopicture_loading))
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.placeholder(context.getResources().getDrawable(R.drawable.bg_nopicture_loading))
.fitCenter()
.dontAnimate()
.into(viewAdapter.carPicture);
viewAdapter.parent.setVisibility(View.VISIBLE);
}
});
}
});
但这不是正路,顶多算是野路子,作为一个追求真理的好少年,这种方法当然不可行。最后在不懈努力之下,找到了原因,还是源于嵌套滑动。因为listview是一个滑动控件,具有滑动属性,所以,在手动计算listview高度的时候listview获取焦点,在listview滑动的驱动下,页面滑动到了listview的底部。解决方案是将listview不获取焦点。
让listview不获取焦点
viewController.getActionListView().setFocusableInTouchMode(false);
viewController.getActionListView().setFocusable(false);
我们还有第二种方法:
让顶部view获取焦点
public static void scrollToTop(View view){
view.setFocusable(true);
view.setFocusableInTouchMode(true);
view.requestFocus();
}
让页面最上边的view获取焦点