Android使用NestedScrollView 内嵌RecycleView滑动冲突问题解决

目录
  • 场景描述
  • 实现思路
  • 问题和优化
  • 优化

场景描述

使用NestedScrollView 内嵌RecycleView时,当用户上滑时,NestedScrollView需要首先响应上滑事件,直到ScrollView无法滑动,再由RecycleView进行响应滑动事件

效果演示:

实现思路

参考谷歌开发者文档中关于view group事件分发的思路,自定义CustomScrollView继承 NestedScrollView 重写onInterceptTouchEvent方法,在拦截到上滑事件时,判断当前的scrollerView是否已经滑动到顶部;

如果ScrollView没有处于顶部,返回true,代表处理并消耗后续一系列触摸事件,包括一系列的Action Move获取到的point到Action Up事件,这中间的事件值都交给 ScrollView处理滑动;

如果ScrollView已经处于顶部,不再拦截上滑事件,会由NestedScrollView默认分发给子控件RecycleView进行处理

public class CustomScrollView extends NestedScrollView {

    public CustomScrollView(@NonNull Context context) {
        super(context);
    }
    private float maxSlideDis;//向上滑动的最大滑动距离,没有超过这个距离时,拦截并处理掉向上滑动的事件
    //在activity或fragment中,根据布局参数进行设置
    private float mDownY;
    private float mSlop;
    public CustomScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }
    public void setMaxSlideDis(float maxSlideDis) {
        this.maxSlideDis = maxSlideDis;
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Logger.log("CustomScrollView onInterceptTouchEvent " + ev.getAction());
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownY = ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                float dis = ev.getRawY() - mDownY;
                if (dis < 0 && Math.abs(dis) >= mSlop) {
                    //当触摸事件是向上滑动并且滑动距离超过屏幕的最小滑动单位时
                    return needScrollParent();
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
    //scroller 是否已经滑动到了最高点
    public boolean needScrollParent() {
        Logger.log("CustomScrollView maxSlideDis = " + maxSlideDis + " getScrollY =" + getScrollY());
        return getScrollY() < maxSlideDis;
    }
    public CustomScrollView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

}

问题和优化

到这一步已经实现先滑动scrollerView再滑动recycleView,但是存在一个问题,当上滑scrollerView的过程中,当scroll view滑动到最顶部不松手指,recycleView不会开始向上滚动,必须等手指离开屏幕,再重新上滑recycle view才可以让其滚动,就像上文gif所展示的;

要明白这个问题的原因,首先要搞清楚一个事件系列 和 view分发机制流程

事件系列: 从ACTION_DOWN 到ACTION_UP 中包含的所有事件,包括其中的所有的ACTION_MOVE事件,为一个事件系列

对view事件分发机制不熟悉的同学可以看下这个分发机制伪代码

public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if(onInterceptTouchEvent(ev)){
        consume = onTouchEvent(ev);
    } else {
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}  

onInterceptTouchEvent方法要注意: 接收到某一个事件系列中的事件值后,如果返回false或者true,那么后续都不再回调这个方法,这个true或false所代表的状态会持续到这个事件系列结束

因此scroller view的onInterceptTouchEvent 返回true拦截了ACTION_MOVE的前几个事件值后,该事件系列的后续事件值也会直接被scroller view的onTouch消耗掉, 并且onInterceptTouchEvent不再会收到该事件系列的后续ev值的回调,因为在dispatchTouchEvent中,直接将后续事件给到了onTouch进行消耗;

导致后续的滑动事件一直被scrollerview所消耗,

而我们想要下面这样丝滑的滑动体验,就要将scroller view滑动到顶部后的后续事件值分发给子视图进行处理:

在一次滑动事件中完成从scroller view到recycleveiw的滑动,手指不离开屏幕

优化

鉴于上述问题,我们把事件的“拦截”放到scroller的dispatchTouchEvent方法中;因为view分发机制首先将每个事件值都传入到dispatchTouchEvent,并在其中通过onInterceptTouchEvent 的返回值来决定是否将该事件分发给子视图的dispatchTouchEvent;

如果scroller view需要滑动,就直接调用onTouch进行消耗事件,如果scroller view 不需要滑动了,就让dispatchTouchEvent默认调用到onInterceptTouchEvent来判断后续事件,这里onInterceptTouchEvent中判断如果scroller view不需要滑动,直接返回false,name后续的ACTION_MOVE值都会被分发给子veiw处理

代码:

 @Override
 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Logger.log("CustomScrollView dispatchTouchEvent ACTION  " + ev.getAction());

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownY = ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                float dis = ev.getRawY() - mDownY;
                if (dis < 0 && Math.abs(dis) >= mSlop) {
                    //当触摸事件是向上滑动并且滑动距离超过屏幕的最小滑动单位时
                    if (needScrollParent()) {
                        onTouchEvent(ev);
                        Logger.log("dispatchTouchEvent 需要拦截");
                        return true;
                    } else {
                        Logger.log("dispatchTouchEvent 不需要拦截");
                        //这里返回 super.dispatchTouchEvent(ev); 在super.dispatchTouchEvent(ev)中会调用下面的onInterceptTouchEvent 方法来判断
                    }
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Logger.log("CustomScrollView onInterceptTouchEvent " + ev.getAction());
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDownY = ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                float dis = ev.getRawY() - mDownY;
                if (dis < 0 && Math.abs(dis) >= mSlop) {
                    //当触摸事件是向上滑动并且滑动距离超过屏幕的最小滑动单位时,不拦截滑动事件
                    //这里的目的是取消dispatchTouchEvent中对该事件系列的拦截,当scroller view不需要拦截时,
                    //会执行到这里,
                    return false;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

到此这篇关于Android使用NestedScrollView 内嵌RecycleView滑动冲突问题解决的文章就介绍到这了,更多相关 NestedScrollView 内嵌RecycleView内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • android RecycleView实现下拉刷新和上拉加载

    android 的下拉刷新,上拉加载功能,翻页时显示从第1页开始到当前页面的内容,并且当前显示的是当前页的内容: 1. PullRefreshRecyclerView.java : 翻页控件 public class PullRefreshRecyclerView extends LinearLayout implements SwipeRefreshLayout.OnRefreshListener, View.OnTouchListener { private SwipeRefreshLayo

  • Android 使用RecycleView列表实现加载更多的示例代码

    1.界面布局 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent&q

  • android RecycleView实现多级树形列表

    本文实例为大家分享了android RecycleView实现多级树形列表的具体代码,供大家参考,具体内容如下 实现多级树状列表: 1. Node.java public class Node<T, B> implements Serializable { /** * 传入的实体对象 */ public B bean; /** * 设置开启的图片 */ public int iconExpand = -1; /** * 设置关闭的图片 */ public int iconNoExpand =

  • Android移动开发recycleView的页面点击跳转设计实现

    目录 一.目的 二.具体代码和页面介绍 三.仓库代码 一.目的 回顾前两次的过程和效果以及本次任务以及最终效果视图: (1)第一次实现界面设计和界面跳转,示例如下: (2)第二次是在页面中设计出自己喜欢的布局并加以实现,我实现的是瀑布流式的布局,如下: (3)第三次就是这次的任务:Activity页面跳转(实现recycleView的页面进行点击跳转),效果如下: 二.具体代码和页面介绍 1.编辑详情页面 即点击后的界面的样式,代码以及样式图如下: <?xml version="1.0&q

  • Android RecycleView实现Item拖拽效果

    基于公司产品的优化需求,其中一个需求涉及到RecycleView的拖拽,以及拖拽后item位置的持久化,目的是可以用户自定义界面偏好,并在用户下次进入本界面后,之前设置的偏好仍然有效.我写了一个小Demo用作演示效果. 先看效果(只看效果,不看颜值) 步骤1.建接口文件ItemTouchHelperViewHolder,该接口文件中描述的是选中和放开当前Item调用的方法. public interface ItemTouchHelperViewHolder { void onItemSelec

  • Android RecycleView和线型布局制作聊天布局

    目录 一.首先在主布局中,用帧布局来填充 RecycleView 和 两个模拟发送消息的Button 二.在一个布局中,加载左边好友发送消息的布局,然后是自己发送消息的右边布局 三.在MsgRecyclerViewActivity 中绑定控件和适配器 四.设置适配器 总结 一.首先在主布局中,用帧布局来填充 RecycleView 和 两个模拟发送消息的Button <?xml version="1.0" encoding="utf-8"?> <L

  • Android之RecycleView实现指定范围的拖动效果

    1 问题 在RecycleView里面实现指定位置的拖动效果,(这里是实现线性布局的,不是网格布局的) @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { Log.i(TAG, "onMove viewHolder.getAdapterPosition() is:" + viewHo

  • Android RecycleView滑动停止后自动吸附效果的实现代码(滑动定位)

    最近有个需求 要求列表 滑动后第一条 需要和顶部对齐 上网找了找  发现 官方支持 Recycle + LinearSnapHelper 可以实现 但我实际操作加上后 发现会卡顿 滑动卡顿 没有以前那种流畅感了 想了想  算了 懒得看源码  还是自己写一个得了 效果图 : 代码如下 注释很清楚了 package com.example.testapp import androidx.appcompat.app.AppCompatActivity import android.os.Bundle

  • Android使用NestedScrollView 内嵌RecycleView滑动冲突问题解决

    目录 场景描述 实现思路 问题和优化 优化 场景描述 使用NestedScrollView 内嵌RecycleView时,当用户上滑时,NestedScrollView需要首先响应上滑事件,直到ScrollView无法滑动,再由RecycleView进行响应滑动事件 效果演示: 实现思路 参考谷歌开发者文档中关于view group事件分发的思路,自定义CustomScrollView继承 NestedScrollView 重写onInterceptTouchEvent方法,在拦截到上滑事件时,

  • Android实现界面内嵌多种卡片视图(ViewPager、RadioGroup)

    Android实现界面内嵌多种卡片视图,具体内容如下 效果如图所示: 1.选择某个界面时,对应的第几个小圆点亮: 通过selector制造圆点和进行更改小圆点被选择和未被选择时的颜色: <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item an

  • Android Studio配置内嵌JDK的方法

    今天发现JDK环境变量没有配置好. 我没有专门去下载java,在下载Android Studio时,会自带内嵌的JDK. 打开File-OtherSettings-DefaultProjectStructure,可看到AS已自动勾选Use embedded JDK,而且是官方推荐的做法. 以下是配置环境变量的操作. java 1.8以后就无需CLASSPATH环境变量了. 我们只需配置JAVA_HOME和PATH即可. JAVA_HOME C:\AndroidStudio\jre (JDK安装目

  • Android App中ViewPager所带来的滑动冲突问题解决方法

    叙述 滑动冲突可以说是日常开发中比较常见的一类问题,也是比较让人头疼的一类问题,尤其是在使用第三方框架的时候,两个原本完美的控件,组合在一起之后,忽然发现整个世界都不好了. 关于滑动冲突 滑动冲突分类: 滑动冲突,总的来说就是两类. 1.同方向滑动冲突 比如ScrollView嵌套ListView,或者是ScrollView嵌套自己 2.不同方向滑动冲突 比如ScrollView嵌套ViewPager,或者是ViewPager嵌套ScrollView,这种情况其实很典型.现在大部分应用最外层都是

  • Android应用中内嵌SQLite数据库的基本操作指南

    一.首先写一个类继承SQLiteOpenHelper类 重写他的方法指定db的名称.版本,重写oncreat和onUpgrade方法,写SQL语句创建表 public class MySQLiteOpenhelper extends SQLiteOpenHelper { private static String name = "person.db"; private static int version = 1; public MySQLiteOpenhelper(Context c

  • Android嵌套滑动冲突的解决方法

    android在嵌套滑动的时候会产生滑动冲突.之前我也碰到,但是以前的笔记本丢失了,所以只能重新再写一章. 一.会产生滑动冲突的情况 那么什么时候会产生滑动冲突呢?比如你有个activity,activity的上半部分是一个布局,下半部分是一个可滑动控件(RecyclerView.ListView等),或者下半部分是个viewpager,里面的fragment布局是一个可滑动控件,这样的页面就会产生滑动冲突. 二.以前的做法 虽然我以前的笔记丢失了,但是当时的解决问题的思路我依然记得. (1)重

  • Android滑动冲突的完美解决方案

    关于滑动冲突 在Android开发中,如果是一些简单的布局,都很容易搞定,但是一旦涉及到复杂的页面,特别是为了兼容小屏手机而使用了ScrollView以后,就会出现很多点击事件的冲突,最经典的就是ScrollView中嵌套了ListView.我想大部分刚开始接触Android的同学们都踩到过这个坑,下面跟着小编一起来看看解决方案吧.. 同方向滑动冲突 比如ScrollView嵌套ListView,或者是ScrollView嵌套自己 这里先看一张效果图 上图是在购物软件上常见的上拉查看图文详情,关

  • Android滑动冲突的完美解决

    Android滑动在智能手机上是必备的操作,但是在开发的时候,你是否和我一样,经常会遇到滑动冲突的问题,比如最简单需要在ListView里面添加一个侧滑动作,这时候冲突时必然的,那我们该如何解决这个问题呢? 先来说一下滑动冲突都有那些,该怎么解决. 场景一:类似于ViewPager嵌套Fragmnet并且在Fragmnet中嵌套了一个ListView的效果,可以通过左右滑动来切换或者触发其他view的显示.但是在ViewPager内部已经处理了这个冲突,所以我们会发现ViewPager嵌套Fra

  • 浅谈Android View滑动冲突的解决方法

    引言 这一篇文章我们就通过介绍滑动冲突的规则和一个实例来更加深入的学习View的事件分发机制. 1.外部滑动方向和内部滑动方向不一致 考虑这样一种场景,开发中我们经常使用ViewPager和Fragment配合使用所组成的页面滑动效果,很多主流的应用都会使用这样的效果.在这种效果中,可以使用左右滑动来切换界面,而每一个界面里面往往又都是ListView这样的控件.本来这种情况是存在滑动冲突的,只是ViewPager内部处理了这种滑动冲突.如果我们不使用ViewPager而是使用ScrollVie

  • 浅谈Android实践之ScrollView中滑动冲突处理解决方案

    1. 前言 在Android开发中,如果是一些简单的布局,都很容易搞定,但是一旦涉及到复杂的页面,特别是为了兼容小屏手机而使用了ScrollView以后,就会出现很多点击事件的冲突,最经典的就是ScrollView中嵌套了ListView.我想大部分刚开始接触Android的同学们都踩到过这个坑,这一篇文章就从最近做的一个项目讲起,然后在过程中提供一些解决冲突的思路. 2. 项目起始 项目有一个页面,涉及到了ViewPager,MapView,ListView,也就是说在一个页面中,会有这三个V

随机推荐