ScrollView嵌套ListView的二三事

来源:互联网 发布:人工挖孔偏桩计算软件 编辑:程序博客网 时间:2024/05/24 06:30

ScrollView嵌套ListView的二三事

博主懒,写博就当是为自己做一个错题本(?)吧,说说最近写APP遇到的一些事。

昨天写的时候遇到了这样一个需求,即界面外部可以滑动,里面嵌套着一个ListView,众所周知,ListView本身就是可以滑动的,但因为ListView的外部也是要滑动的,So第一反应就是ScrollingView嵌套一个ListView咯。

<?xml version="1.0" encoding="utf-8"?><android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    app:layout_behavior="@string/appbar_scrolling_view_behavior"    tools:context="com.sin.cainiao.ShowDetailActivity"    tools:showIn="@layout/activity_show_detail">    <ListView        android:layout_width="match_parent"        android:layout_height="wrap_content"></android.support.v4.widget.NestedScrollView>

顺理成章的代码,但读到这片文章的人相信都应该知道这么做会有什么后果了,嗯,ListView只显示了一行。

这就很尴尬了。

所以我跟大家一样去搜解决方法了,于是乎我获得了以下的代码

/*** 动态设置ListView的高度* @param listView*/public static void setListViewHeightBasedOnChildren(ListView listView) {    if(listView == null) return;    ListAdapter listAdapter = listView.getAdapter();    if (listAdapter == null) {        // pre-condition        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);}

恩 满怀信心拿去用

好气啊!根本没用啊!!!!Log输出已经把高度计算出来了,但是set并没有什么用(BTW在XML里面set具体的height也没用)

具体原因还不是很懂,但我从《android开发艺术》一书中看到过类似的问题,由于博主未能研究透,所以我觉得应该是scrollview的onMeasure比较奇特?以后可能会更新更详细的解释。

最后我在scrollview的源码里面看到了这么一段话

<p>You should never use a ScrollView with a {@link ListView}, because * ListView takes care of its own vertical scrolling.  Most importantly, doing this * defeats all of the important optimizations in ListView for dealing with * large lists, since it effectively forces the ListView to display its entire * list of items to fill up the infinite container supplied by ScrollView.

google已经公开吐槽了啊…然后我又看到了这样一句话

有的時候是不得已的….設計常常會堅持他們自己搞出來的無俚頭想法….RD只有苦幹的份….

深有体会 虽然自己是设计也是RD哈哈哈

回到正题 最后的解决方法是利用NestedScrollView和RecyleView合并起来,实际上一个是ScrollView的延伸,一个是ListView的延伸,它们的组合完美的解决了这个问题,原因是实现了NestedScrollingParent和child的接口

实现 NestedScrollingChild

   首先来说NestedScrollingChild。如果你有一个可以滑动的 View,需要被用来作为嵌入滑动的子 View,就必须实现本接口。在此 View 中,包含一个 NestedScrollingChildHelper 辅助类。NestedScrollingChild接口的实现,基本上就是调用本 Helper 类的对应的函数即可,因为 Helper 类中已经实现好了 Child 和 Parent 交互的逻辑。原来的 View 的处理 Touch 事件,并实现滑动的逻辑大体上不需要改变。   需要做的就是,如果要准备开始滑动了,需要告诉 Parent,你要准备进入滑动状态了,调用startNestedScroll()。你在滑动之前,先问一下你的 Parent 是否需要滑动,也就是调用dispatchNestedPreScroll()。如果父类滑动了一定距离,你需要重新计算一下父类滑动后剩下给你的滑动距 离余量。然后,你自己进行余下的滑动。最后,如果滑动距离还有剩余,你就再问一下,Parent 是否需要在继续滑动你剩下的距离,也就是调用dispatchNestedScroll()。  以上是一些基本原理,有了上面的基本思路,可以参考这篇 文章 ,这里面有原理的详细解析。如果还是不清楚, 这里 有对应的代码可以参考。

实现 NestedScrollingParent

    作为一个可以嵌入 NestedScrollingChild 的父 View,需要实现NestedScrollingParent,这个接口方法和NestedScrollingChild大致有一一对应的关系。同样, 也有一个 NestedScrollingParentHelper 辅助类来默默的帮助你实现和 Child 交互的逻辑。滑动动作是 Child 主动发起,Parent 就收滑动回调并作出响应。    从上面的 Child 分析可知,滑动开始的调用startNestedScroll(),Parent 收到onStartNestedScroll()回调,决定是否需要配合 Child 一起进行处理滑动,如果需要配合,还会回调onNestedScrollAccepted()。    每次滑动前,Child 先询问 Parent 是否需要滑动,即dispatchNestedPreScroll(),这就回调到 Parent 的onNestedPreScroll(),Parent 可以在这个回调中“劫持”掉 Child 的滑动,也就是先于 Child 滑动。   Child 滑动以后,会调用onNestedScroll(),回调到 Parent 的onNestedScroll(),这里就是 Child 滑动后,剩下的给 Parent 处理,也就是 后于 Child 滑动。   最后,滑动结束,调用onStopNestedScroll()表示本次处理结束。

以上是网上找到的原理解释。我能说这是设计的胜利么哈哈哈哈。

最后的最后 由于ScrollView只允许有一个子View,但由于业务需要的界面比较复杂,so我们可以创建一个Layout装起来,再在layout里边写哦~关于这点文档中也有提醒

A ScrollView * is a {@link FrameLayout}, meaning you should place one child in it * containing the entire contents to scroll; this child may itself be a layout * manager with a complex hierarchy of objects.  A child that is often used * is a {@link LinearLayout} in a vertical orientation, presenting a vertical * array of top-level items that the user can scroll through.

最后的最后,要记住使用RecyleView的时候要加上布局啊!!!要加上布局啊!!!要加上布局啊!!!

mRecyclerView.setLayoutManager(new LinearLayoutManager(this));   //Fuck this code
0 0