CoordinatorLayout+NestedScrollView+GridView踩坑爬坑之旅详解

前言:本次基于Android Studio3.4.1、SDK28、JDK1.8的环境编写测试

1. 伪代码描述xml布局

<android.support.design.widget.CoordinatorLayout>

    <android.support.design.widget.AppBarLayout>

        <LinearLayout app:layout_scrollFlags = "scroll">

            <!--这里放置响应滑动发生变化的组件内容-->
            

        </LinearLayout>

    </android.support.design.widget.AppBarLayout>

    
    <android.support.v4.widget.NestedScrollView
          app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <!--自定义GridView-->
        <com.xx.xx.CustomGridView>

        </com.xx.xx.CustomGridView>

    </android.support.v4.widget.NestedScrollView>


</android.support.design.widget.CoordinatorLayout>

上面的代码是最终经过爬坑正常运行成功的代码,只要在相应的位置上添加你要响应滑动发生的组件即可 

 2.CoordinatorLayout+AppBarLayout注意用法

这两个组件是进行搭配使用的,主要能够实现内部子控件响应发生滑动的子控件的事件,常见的搭配如ToolBar,能够实现比较生动的效果上滑下拉的动画效果

在能够实现滑动事件的控件上加上属性app:layout_behavior="@string/appbar_scrolling_view_behavior",在想要响应该子控件滑动事件的其他控件上加入app:layout_scrollFlags = "scroll"即可

对于app:layout_scrollFlags中传入的几个比较重要值概述:

scroll:

Child View 伴随着滚动事件而滚出或滚进屏幕。注意两点:第一点,如果使用了其他值,必定要使用这个值才能起作用;第二点:如果在这个child View前面的任何其他Child View没有设置这个值,那么这个Child View的设置将失去作用 

snap:

简单理解,就是Child View滚动比例的一个吸附效果。也就是说,Child View不会存在局部显示的情况,滚动Child View的部分高度,当我们松开手指时,Child View要么向上全部滚出屏幕,要么向下全部滚进屏幕,有点类似ViewPager的左右滑动。

enterAlways:

快速返回模式。其实就是向下滚动时Scrolling View和Child View之间的滚动优先级问题。对比scrollscroll | enterAlways设置,发生向下滚动事件时,前者优先滚动Scrolling View,后者优先滚动Child View,当优先滚动的一方已经全部滚进屏幕之后,另一方才开始滚动。

还有其他两种模式,详情gif演示请看这篇文章CoordinatorLayout滑动五大属性描述

发生滑动事件的控件的这个属性app:layout_behavior="@string/appbar_scrolling_view_behavior"必须要设置,不然就是出现像FramLayout组件重叠的现象,该属性意思是通知布局中包含滑动组件

接下来我们假设AppBarLayout中包裹着ToolBar为例,ToolBar要响应其他子控件的滑动事件:

3.踩坑之旅第一处(外层没使用NestedScrollView)

1.直接使用GridView时如果没有增加属性android:nestedScrollingEnable = "true"时只能实现GridView滑动,而ToolBar则不会响应其滑动事件跟随滑动,笔者猜测在CoordinatorLayout中不能识别这种滑动事件,而设置该属性就能够实现ToolBar与GridView一起滑动,当GridView发生滚动时,ToolBar也会随着一起滚动,消失或者重新在屏幕中显示出现

2.但是当我们长按住GridView并且在GridView顶部来回滑动时会出现ToolBar不会随着GridView一起滚动的情况,所以请看踩坑之旅第二处

3.使用RecyclerView好像是不会出现下面这些坑,不过笔者没有测试....

4.踩坑之旅第二处(GridView只显示一行)

在CoordinatorLayout布局中,在GridView外侧嵌套一层NestedScrollView,而且由于二者件发生滑动冲突,导致外面包裹的NestedScrollView不知道内部GridView的实际高度,所以导致里面的GridView只能显示一行(具体为什么只显示一行,笔者没有深入源码去阅读,如果面前的您知道的话还望评论麻烦赐教)

在网上看到了有如下两种解决方案:

1.在NestedScrollView控件中增加属性android:fillViewport = "true",GridView设置属性android:nestedScrollingEnable = "false"时,确实能实现ToolBar跟随GridView滑动,但是由于我们设置GridView属性android:nestedScrollingEnable = "false"导致GridView无法实现内部滑动,android:fillViewport = "true"表示GridView里面的内容可以铺满当前屏幕,但其导致了NestedScrollView滑动失灵,所以这一切导致了GridVIew只能显示当前铺满屏幕的item,需要滑动显示的由于组件不能滑动则无法显示了,而如果我们保持fillViewport属性不变,而android:nestedScrollingEnable = "true"设置时,由于我们允许GridView内部滑动,这就又导致了踩坑之旅第一处的那种情况:长点击GridView在顶部来回滑动ToolBar不跟着一起滑动的情况,所以这种解决方式忽略

2.重写GridView的onMeasue(),该方法是测量组件的长度和宽度的,重写的代码如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
      super.onMeasure(widthMeasureSpec, expandSpec);
}

我们知道组件的宽和高是由父类的MeasureSpec和组件的LayoutParam来决定的,并且保存在组件的MeasureSpec中,此处就指定了组件的测量模式为AT_MOST,对应height属性为wrap_content,这样就可以实现GridView的内容会平铺在屏幕上,并且随着滑动显示 

5.踩坑之旅第三处(监听滑动失效)

当我们采用了重写GridView方式解决问题后,如果这个时候我们恰好调用了GridView的监听事件:

// 监听listview滚到最底部
mIndexList.setOnScrollListener(new OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
            // 当不滚动时
            case OnScrollListener.SCROLL_STATE_IDLE:
                // 判断滚动到底部
                if (view.getLastVisiblePosition() == (view.getCount() - 1)) {
                }
              break;
        }
    }
 
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
           int visibleItemCount, int totalItemCount) {
    }
});

view.getLastVisiblePosition()本来获取的应该是GridView中此时显示在屏幕中的item的最后一个位置,但是由于你重写了GridView的onMeasure()方法, 导致view.getLastVisiblePosition()会返回GridView加载的所有子item的最后一个位置,即使它们并没有被显示在屏幕上,所以此时你利用view.getLastVisiblePosition()监听当前GridView是否滑动到底部的方法是行不通的,这个时候只能选择其他方式

6.踩坑之旅第四处(提高滑动效率)

 

当我们实现了类似于下面的:

<android.support.v4.widget.NestedScrollView
          app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <!--自定义GridView-->
        <com.xx.xx.CustomGridView>

        </com.xx.xx.CustomGridView>

    </android.support.v4.widget.NestedScrollView>

由于NestedScrollView本身能够实现滑动,自定义的GridView也能实现滑动,所以为了避免冲突(界面显示可能存在问题),我们把GridView的属性android:nestedScrollingEnable = "false",这样会使GridView滑动更加流畅

7.踩坑之旅第五处(NPE)

GridVIew可以调用方法getChildAt(int position)方法获取指定位置的item,但是getChildAt(int position)方法使用需要注意:其position显示当前可见区域的第几个元素,即假设GridView加载了100个item,而当前屏幕只能显示20个,剩下的需要滑动才能显示,那么position的值不能大于20,否则报错空指针异常,这是由于GridVIew的复用机制导致的。此时我们可以调用方法getFirstVisiblePosition方法,这个放回返回的就是此时显示在屏幕上的第一个item在加载的整个100个item中的位置,所以我们使用下面方法:

gridview.getChildAt(position - getFirstVisiblePosition())

就可以正常获取想要的position对应的item进行接下来的操作

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值