前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NestedScrollView 嵌套 ListView 实现滑动折叠效果

NestedScrollView 嵌套 ListView 实现滑动折叠效果

作者头像
用户1127566
发布2018-06-06 16:08:58
3.3K0
发布2018-06-06 16:08:58
举报
文章被收录于专栏:Android 开发学习Android 开发学习

引言

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

布局文件

如下所示:

代码语言:javascript
复制
<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问题。

代码语言:javascript
复制
   貌似网上一抓一大把,本文采用其中之一方案,复写listView。
代码语言:javascript
复制
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。

代码语言:javascript
复制
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坑。

代码语言:javascript
复制
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如下添加代码:

代码语言:javascript
复制
 <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"

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.02.19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 布局文件
  • 解决NestedScrollView嵌套listView问题。
  • 解决NestedScrollView嵌套的listView滚动时无响应bug。
  • 题外话:直接使用RecyclerView折叠动画不平滑问题
  • 题外话2
  • 题外话3
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档