日常开发中很少会碰到ScrollView中嵌套listview或webview的情况,而且谷歌官方也不推荐这么做,但是也不是一定不会有这样的需求,毕竟定需求的不是我们程序员,而是产品经理。比如像下面这种需求:
可以看到,整个页面有一个共同的头部,下面有两个tab,左边tab下是个可以滚动的webview,右边是个listview。要求listview和webview在默认情况下不滚动,但外部整个页面可以滚动,当外层页面滚动到底部时,也就是两个tab的位置大概位于actionbar下方的时候,要求listview和webview自己能滑动而外层不动,当listview或webview下拉到顶部时,又让外层接管滑动,此时共同的头部可以拉下来。
碰到这种情况,一般的情况肯定是外层套个scrollview,两个tab里分别放个fragment,左右两个fragment分别放置listview。但是很不幸,这样做之后发现listview根本连显示都显示不了,更别提可以滑动了。读者一试便知。原因就在与scrollview和listview存在滑动事件的冲突,那么如何解决这个问题呢?网上有人给出了一个方法:手动计算listview的高度然后显示地设置他的高度。你只需要在listview.setAdapter()方法后调用如下代码:
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));Log.d(TAG, "params.height: "+params.height);listView.setLayoutParams(params);}
很不幸!这段代码只有在没有fragment的情况下才有效果,现在的情况是scrollview在宿主Activity中,而listview却在其中一个fragment中,按理说fragment也是放在Activity中那就相当于listview也在Activity中,但是实际情况就是显示不出来listview,具体什么原因我暂时也不清楚。
那既然在没有fragment的情况下才可以显示listview,那如果不用fragment怎么达到上述切换的效果呢?很简单!在Activity中切换的位置放置个空的容器FrameLayout,然后在切换tab的时候动态添加进想要的view,无论是webvie还是listview甚至是更复杂的view。其实在fragment出现之前,解耦Activity就采用的是这中方式。
我们采用mvc的思想抽象出一个controller基类,功能类似于一个简单的fragment,里面可以绑定view视图和model数据。
public abstract class BaseController{public View mRootView;public Context mContext;public BaseController(Context context){this.mContext = context;// 在构造中 就加载显示的viewmRootView = initView(context);}/*** 初始化view的方法让子类去实现* @return*/protected abstract View initView(Context context);/*** 加载数据的方法,子类可以实现,也可以不实现*/public void initData(){}/*** 暴露出去的获得根view的方法* @return*/public View getRootView(){return mRootView;}}
然后继承这个基类分别创建LeftController 和 RightController,我们先以RightController为例,来把显示listview显示出来。
public class RightController extends BaseController {private static final String TAG = "RightController";private ListView mListView;private List<String> mDatas;public RightController(Context context) {super(context);}@Overrideprotected View initView(Context context) {// TextView readView = new TextView(context);// readView.setText("right-页面");View rootView = View.inflate(context, R.layout.controller_right, null);mListView = (ListView) rootView.findViewById(R.id.lv);prepareData();RightAdapter rightAdapter = new RightAdapter(context, mDatas);mListView.setAdapter(rightAdapter);setListViewHeightBasedOnChildren(mListView);return rootView;}private void prepareData() {mDatas = new ArrayList<>();for (int i = 0; i < 20; i++) {mDatas.add("item_" + i);}}/*** 在scrollview中完整显示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++) {for (int i = 0; i < 10; 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() * 5);// (listAdapter.getCount() - 1));Log.d(TAG, "params.height: "+params.height);listView.setLayoutParams(params);}}
而在Activity的tab切换事件中只需这样两句代码即可把controller的view动态添加进Activity中:
@Overridepublic void onChecked

本文详细探讨了在ScrollView中嵌套Fragment,而Fragment内部包含ListView或WebView时遇到的滑动冲突问题。通过分析问题的原因,提出了几种有效的解决策略,包括使用NestedScrollView替代ScrollView、设置滚动监听和协调滚动等方法,帮助开发者优化用户体验。
1953





