当前位置: 首页 > article >正文

【Android】NestedScrollView的简单用法与滚动冲突、滑动冲突

一、NestedScrollView

1. 什么是 NestedScrollView

NestedScrollView 是 Android 中一个用于处理垂直方向滚动的布局组件,它继承自 FrameLayout,同时支持嵌套滑动(Nested Scrolling)机制。相比于传统的 ScrollViewNestedScrollView 专为解决嵌套滚动冲突问题设计,能够与其他支持嵌套滑动的子视图(如 RecyclerViewViewPager 等)协同工作。

2. 定义

NestedScrollView 是 Android Jetpack 中的组件,用于容纳能够垂直滚动的视图。当页面布局的内容超过屏幕高度时,可以通过滚动展示全部内容。同时,NestedScrollView 在滚动的过程中与子视图可以进行事件协作。

3. 与ScrollView的区别

NestedScrollViewScrollView 的主要区别在于它具备“嵌套滑动”(Nested Scrolling)功能。在 Android 中,嵌套滑动是一种滚动冲突处理机制,允许父视图和子视图协同工作,共同处理滑动事件。这种机制非常有用,特别是当你在一个滚动视图中嵌套另一个滚动视图时,它能够有效避免滑动冲突。

  • ScrollView:不支持嵌套滑动,通常会出现父子滑动视图的事件冲突,导致滑动体验不佳。
  • NestedScrollView:内置嵌套滑动机制,能够更好地处理父子视图的滚动事件,使页面更加流畅。

4. 常见使用场景

  • 表单页面:在一个页面中,可能会有许多输入框、按钮等,当这些内容超过屏幕时,使用 NestedScrollView 可以让整个页面可滚动。
  • 嵌套滚动视图:当你在一个滚动视图(如 RecyclerView)中嵌套了另一个滚动视图(如 ViewPagerHorizontalScrollView)时,NestedScrollView 能避免滑动事件冲突。
  • CoordinatorLayout 结合NestedScrollView 可以与 CoordinatorLayout 搭配使用,处理如 CollapsingToolbarLayoutAppBarLayout 等复杂的滚动联动效果。

5. 简单使用方法

下面是一个CoordinatorLayout中使用NestedScrollView并嵌套RecyclerView的简单用法:

<androidx.coordinatorlayout.widget.CoordinatorLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:nestedScrollingEnabled="false" />

        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
  • CoordinatorLayout 是整个布局的容器,用于协调多个滚动视图的交互,尽管当前只涉及了 NestedScrollView
  • NestedScrollView 管理页面的垂直滚动,并能够与其他支持嵌套滚动的视图配合使用。它容纳了 RecyclerView 并负责控制滚动。
  • RecyclerView 展示的是具体的滚动内容,使用 android:nestedScrollingEnabled="false"NestedScrollView 完全负责滚动处理,避免滚动冲突。

二、滚动冲突和滑动冲突

1. 区别

滚动冲突(Scroll Conflict)

​ 滚动冲突发生在嵌套滚动视图中。例如,一个 ScrollView 内部嵌套了一个 RecyclerView,或者一个 ViewPager 内嵌了一个 ScrollView。当用户在一个滚动视图上滑动时,系统需要决定哪个视图应该接收滚动事件,从而可能导致滚动冲突。

滑动冲突(Touch Conflict)

​ 滑动冲突通常发生在多个视图或组件尝试处理相同的触摸事件时。例如,一个 ViewPager 和一个 RecyclerView 都可以响应滑动手势,这会导致滑动冲突。滑动冲突通常涉及触摸事件的处理,而不是滚动事件的嵌套。

2. 处理方式

处理滚动冲突

滚动冲突通常发生在嵌套的滚动视图中,例如在一个 ScrollView 中嵌套了一个 RecyclerView。以下是几种常见的解决方法:

  1. 禁用子视图的嵌套滚动
  • 描述:禁用子视图的嵌套滚动功能,让父视图完全控制滚动行为。

  • 示例

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:nestedScrollingEnabled="false" />
    
  1. 自定义 Behavior
  • 描述:创建自定义的 Behavior 类,控制滚动行为和滚动事件的传递。

  • 示例

    package com.example.nestedscrollviewtest;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.View;
    
    import androidx.annotation.NonNull;
    import androidx.coordinatorlayout.widget.CoordinatorLayout;
    import androidx.core.view.ViewCompat;
    import androidx.core.widget.NestedScrollView;
    
    public class CustomBehavior extends CoordinatorLayout.Behavior<NestedScrollView> {
    
        public CustomBehavior() {
            super();
        }
    
        public CustomBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull NestedScrollView child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
            // 控制是否响应嵌套滑动
            return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
        }
    
        @Override
        public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull NestedScrollView child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
            // 处理嵌套滑动前的事件
        }
    }
    
    
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    
        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="com.example.nestedscrollviewtest.CustomBehavior">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
    
                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/recyclerView"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:nestedScrollingEnabled="false" />
    
            </LinearLayout>
        </androidx.core.widget.NestedScrollView>
    
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    
    

处理滑动冲突

1. 外部拦截(onInterceptTouchEvent

  • 作用:用于拦截触摸事件,决定是否由当前视图处理该事件。
  • 如何工作:在父视图的 onInterceptTouchEvent 方法中,父视图会决定是否拦截事件并交给自己处理。如果返回 true,父视图将处理事件;如果返回 false,事件将传递给子视图处理。

NestedScrollView 中的外部拦截

NestedScrollView 作为一个容器视图,通常会处理其内部的滚动事件。为了确保它能够正确处理滚动事件,可以重写 NestedScrollViewonInterceptTouchEvent 方法来拦截触摸事件,并决定是否让其处理:

package com.example.nestedscrollviewtest;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;

import androidx.core.widget.NestedScrollView;

public class CustomNestedScrollView extends NestedScrollView {

    public CustomNestedScrollView(Context context) {
        super(context);
    }

    public CustomNestedScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomNestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 根据需要判断是否拦截事件
        // 例如,可以根据事件的类型或滚动方向来决定是否拦截
        return super.onInterceptTouchEvent(ev);
    }
}

2. 内部拦截(onTouchEvent

  • 作用:用于处理视图接收到的触摸事件。
  • 如何工作:在子视图的 onTouchEvent 方法中,子视图会处理传递给它的事件。如果子视图无法处理事件,它可以将事件传递给父视图或其他视图。

RecyclerViewonTouchEvent 方法负责处理其自己的触摸事件。通常,你不需要对 RecyclerViewonTouchEvent 进行特别处理,但要确保它正常工作。

public class CustomRecyclerView extends RecyclerView {

    public CustomRecyclerView(Context context) {
        super(context);
    }

    public CustomRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        // 默认情况下,RecyclerView 处理自己的触摸事件
        return super.onTouchEvent(e);
    }
}

3.拦截机制

滚动冲突和拦截机制

当你处理滚动冲突时(例如ScrollView 中嵌套 RecyclerView),内部拦截和外部拦截的目的都是在父视图和子视图之间决定谁来处理滚动事件。在这种情况下,使用拦截机制是为了解决多个滚动视图对事件的争夺

  • 内部拦截法:子视图决定是否处理事件。
    • 适用于子视图滚动逻辑复杂的情况,通常用于滚动冲突
  • 外部拦截法:父视图决定是否处理事件。
    • 适用于父视图需要优先处理事件的情况,常用于解决滚动冲突

滑动冲突和拦截机制

滑动冲突是由多个视图组件对触摸事件的竞争引发的,而不是滚动事件的嵌套。在滑动冲突的场景下,拦截机制也同样适用。比如在ViewPagerRecyclerView这类视图中,事件分发的冲突会涉及滑动方向和手势的识别。

  • 内部拦截法:子视图根据触摸事件的方向和类型,决定是否继续处理触摸事件或传递给父视图。
    • 适用于需要子视图有更多自定义处理的滑动场景。
  • 外部拦截法:父视图判断当前手势是否属于自己的处理范围,例如 ViewPager 判断是水平滑动,则拦截事件。
    • 常用于父视图需要根据手势类型做判断的滑动冲突场景。

以上就是本篇博客的所有内容


已经到底啦!!


http://www.kler.cn/a/298694.html

相关文章:

  • 【brew安装失败】DNS 查询 raw.githubusercontent.com 返回的是 0.0.0.0
  • SonarQube相关的maven配置及使用
  • 国产数据库TiDB从入门到放弃教程
  • Navicat 17 功能简介 | 比较模型工作区
  • 云手机:小红书矩阵搭建方案
  • Junit4单元测试快速上手
  • 聚观早报 | 红魔电竞平板新品发布;台积电8月份营收
  • LabVIEW步进电机控制方式
  • node.js入门基础
  • Learn OpenGL In Qt之着色器
  • 【C++】 Vector
  • mysql mgr 集群部署 单主模式和多主模式
  • [论文笔记] t-SNE数据可视化
  • Java笔试面试题AI答之JDBC(3)
  • framebuffer
  • Android13修改Setting实现电量低于30%的话不可执行Rest操作
  • ubuntu配置tftp、nfs
  • 【编程基础知识】Spring过滤器、拦截器、AOP区别
  • 《JavaScript 中数据类型判断、转换技巧及应用实例》
  • GitHub每日最火火火项目(9.10)
  • 最新版 | SpringBoot3如何自定义starter(面试常考)
  • Java数组的定义及遍历
  • 【局域网投屏】sunshine和moonlight投屏/屏幕共享/扩展屏
  • LabVIEW软件,如何检测连接到的设备?
  • 全频段覆盖的卫星通信模块-灵活应对多应用场景
  • Swift 中的函数:定义、使用与实践指南