美文网首页android收集android技术专栏android
NestedScrollView 嵌套 ListView 实现滑

NestedScrollView 嵌套 ListView 实现滑

作者: nothingwxq | 来源:发表于2016-02-19 10:38 被阅读36144次

    引言

    最近,在做公司一个design折叠效果的时候遇到个问题,就是我们本身app的方法数太多了,dex分包技术还没搞定。不得不尽量缩减一些不必要的包、类。当我们引入RecyclerView的时候,恰好是压死骆驼的最后一根稻草,故不得不采用其他方案来代替RecyclerView 和 CollapsingToolbarLayout实现的折叠效果。本文试着采用 NestedScrollView 嵌套 ListView的方法来实现折叠效果。具体结果如图所示:

    布局文件

    如下所示:

    <android.support.design.widget.CoordinatorLayout
        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">
    
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="270dp"
            android:fitsSystemWindows="true">
    
            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#F03843"
                app:contentScrim="#F03843"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">
    
                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    app:layout_collapseMode="parallax"
                    app:layout_collapseParallaxMultiplier="0.7"/>
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="?attr/actionBarSize"
                    android:minHeight="?attr/actionBarSize"
                    app:contentInsetEnd="0dp"
                    app:contentInsetStart="0dp"
                    app:layout_collapseMode="pin">
    
                    <RelativeLayout
                        android:layout_width="match_parent"
                        android:layout_height="match_parent">
    
                        <ImageButton
                            android:id="@+id/toolbar_import_navigation"
                            android:layout_width="?attr/actionBarSize"
                            android:layout_height="?attr/actionBarSize"
                            android:layout_alignParentLeft="true"
                            android:layout_alignParentStart="true"
                            android:layout_centerVertical="true"
                            android:src="@drawable/icon_toolbar_navigation"/>
                        <TextView
                            android:id="@+id/toolbar_title"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_centerInParent="true"
                            android:gravity="center"
                            android:lineSpacingMultiplier="1.5"
                            android:text="@string/select_category"
                            android:textColor="@color/dark_gray"
                            android:textSize="@dimen/text_40px"/>
                        <TextView
                            android:id="@+id/toolbar_import"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_alignParentEnd="true"
                            android:layout_alignParentRight="true"
                            android:layout_centerVertical="true"
                            android:layout_marginRight="16dp"
                            android:lineSpacingMultiplier="1.5"
                            android:text="@string/vocabulary_import"
                            android:textColor="@color/dark_gray"
                            android:textSize="@dimen/text_32px"/>
                    </RelativeLayout>
                </android.support.v7.widget.Toolbar>
                <android.support.design.widget.TabLayout
                    android:id="@+id/tabs"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:tabGravity="fill" />
            </android.support.design.widget.CollapsingToolbarLayout>
        </android.support.design.widget.AppBarLayout>
    
        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fillViewport="true"
            android:layout_marginTop="2dp"
            android:id="@+id/vocabulary_nested_scroll"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/parent_list_view"
                android:orientation="vertical">
                <NoScrollListView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/list" />
            </LinearLayout>
        </android.support.v4.widget.NestedScrollView>
    </android.support.design.widget.CoordinatorLayout>
    

    上述布局需要注意的是:

    • 必须采用CoordinatorLayout作为外层包裹,至于原因是由于使用Behavior需要。
    • AppBarLayout做一个兼容。
    • android:fitsSystemWindows,经本人代码测试,表示CollapsingToolbarLayout的上边界是否扩展到statusbar,这里如果想使用透明的statusbar,这里更新下应该该布局的根布局的该属性为true,不过这里兼容性还是存在问题的,特别是android 4.4版本的折叠布局的透明statusbar,不知道改为有没更好的方式。
    • app:contentScrim 表示CollapsingToolbarLayout折叠以后,toolbar的颜色。
    • app:layout_scrollFlags="scroll|exitUntilCollapsed" 表示CoordinatorLayout的依赖元素滚动的时候,进行折叠。
      scroll - 想滚动就必须设置这个。 enterAlways - 实现quick return效果, 当向下移动时,立即显示View(比如Toolbar). exitUntilCollapsed - 向上滚动时收缩View,但可以固定Toolbar一直在上面。 enterAlwaysCollapsed - 当你的View已经设置minHeight属性又使用此标志时,你的View只能以最小高度进入,只有当滚动视图到达顶部时才扩大到完整高度。(参见: http://android.jobbole.com/82193/
    • layout_collapseMode,pin表示不动,parallax视差效果
    • 将 app:layout_behavior="@string/appbar_scrolling_view_behavior"指定给NestedScrollView,即当该控件滑动的时候,其他CollapsingToolbarLayout内的子view做相应的改变如(视差)或pin(不变)。

    解决NestedScrollView嵌套listView问题。

       貌似网上一抓一大把,本文采用其中之一方案,复写listView。
    
    public class NoScrollListView extends ListView {
        public NoScrollListView(Context context) {
            super(context);
        }
    
        public NoScrollListView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public NoScrollListView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
        }
    }
    

    解决NestedScrollView嵌套的listView滚动时无响应bug。

    private void adjustParentView() { //兼容NestedscrollView
        int actionBarHeight = 0;
        TypedValue tv = new TypedValue();
        if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
            actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
        }
        Rect outRect = new Rect();  //状态栏高度
        getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect);
        mParentView.setMinimumHeight(DeviceUtils.screenHeight() - actionBarHeight - outRect.top);
    }
    

    注:直接使用 RecyclerView更省事省力。

    题外话:直接使用RecyclerView折叠动画不平滑问题

    在stackoverflow上找到如下解决方案,大致是由于google官方留的Behavior坑。

    public final class FlingBehavior extends AppBarLayout.Behavior {
        private static final int TOP_CHILD_FLING_THRESHOLD = 3;
        private boolean isPositive;
    
        public FlingBehavior() {
        }
    
        public FlingBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
            if (velocityY > 0 && !isPositive || velocityY < 0 && isPositive) {
                velocityY = velocityY * -1;
            }
            if (target instanceof RecyclerView && velocityY < 0) {
                final RecyclerView recyclerView = (RecyclerView) target;
                final View firstChild = recyclerView.getChildAt(0);
                final int childAdapterPosition = recyclerView.getChildAdapterPosition(firstChild);
                consumed = childAdapterPosition > TOP_CHILD_FLING_THRESHOLD;
            }
            return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
        }
    
        @Override
        public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
            isPositive = dy > 0;
        }
    }
    

    最后在AppBarLayout如下添加代码:

     <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="210dp"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:layout_behavior="com.youdao.vocabulary.widget.FlingBehavior">
    

    题外话2

    java.lang.IllegalStateException: View can not be anchored to the the parent CoordinatorLayout
    当我们给一个view设置app:layout_anchor时,在最新的23.2.0包会出现问题,解决办法是用android:layout_gravity="bottom|end" 替换。

    题外话3

    23.2.0中在CoordinatorLayout中使用Toolbar ,toolbar无法与顶部对齐,即顶部存在垂直间距。解决方法在AppBarLayout添加 android:fitsSystemWindows="true"

    相关文章

      网友评论

      • WuRIGENc:toolbar的返回按钮怎么去不掉啊?
      • 唐小鹏:重写ListView不能滑动,这样如果listview 要做加载更多的功能,是不是就冲突了,谢谢
      • f89a48ac1670:onMeasure这么改ListView里的item view都不回收了吧,内存受得了吗
      • chen_666:厉害了我的哥
      • 216fa9f0a5c3:在吗 能加加314302257@qq.com聊一下吗? 就关于滑动冲突的问题 无言感激
      • af272c6e74df:如果在NestedScrollView中放tablayout和viewpager,在viewpager内放listview,通过重写解决它们之间的冲突问题后,发现tablayout无法置顶了怎么办?
        nothingwxq: @天天童话丶 里面有pin属性~~
        af272c6e74df:给NestedScrollView设置app:layout_behavior="@string/appbar_scrolling_view_behavior"属性是可以实现tab置顶的,但是我重写viewpager和listview解决滑动冲突后tablayout就跟着滑动到上面去了,怎么办?你说把tablayout放在CollapsingToolbarLayout中不就被叠起来了吗?怎么置顶?
        nothingwxq:@天天童话丶 你放scrollView中,肯定没法置顶呀,需要放CollapsingToolbarLayout中,本文还有xml配置,可以参考下。
      • f4e96710dd51:但是如果ListView的Item数量刚好有屏幕这么高的时候就滑不了了(说不清楚)。。比如图上item数量一共才3条,刚好超过屏幕一点点,就滑不了,如果item数量大于3条,就正常了,怎么解决。。。这个怎么破?
        nothingwxq: @f4e96710dd51 是指的collapsingtoolbar无法滚动么?可以试下ajustparent那个方法。
      • 66be1619e8a2:DeviceUtils是哪里来的?
        66be1619e8a2:@nothingwxq 我现在用的是pulltorefresh扩展的recyclerview,但是遇到一个问题:滑上去没问题,滑下来的话标题就不能展开了.
        nothingwxq:@chemin520 我们项目中用的工具包。
      • 丶Lost_Deer:能给个源码地址吗,谢谢了,滑动冲突那块不太懂
        nothingwxq:@丶Lost_Deer 不好意思,原来的demo没保留,最终版用在了前公司的项目上了,没法给你。
      • 4f327c36f388:请问解决滑动时无响应的那一段代码要写在哪里
        nothingwxq:@王少123 对应Activity中,最好在onWindowFocusChanged。
      • 爱孔孟:O(∩_∩)O哈哈~

      本文标题:NestedScrollView 嵌套 ListView 实现滑

      本文链接:https://www.haomeiwen.com/subject/cqxvkttx.html