勿忘初心做自己 发表于 2024-9-16 19:44:22

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

一、NestedScrollView

1. 什么是 NestedScrollView

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

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

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


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



[*]表单页面:在一个页面中,可能会有很多输入框、按钮等,当这些内容超过屏幕时,使用 NestedScrollView 可以让整个页面可滚动。
[*]嵌套滚动视图:当你在一个滚动视图(如 RecyclerView)中嵌套了另一个滚动视图(如 ViewPager 或 HorizontalScrollView)时,NestedScrollView 能避免滑动事件冲突。
[*]与 CoordinatorLayout 结合:NestedScrollView 可以与 CoordinatorLayout 搭配使用,处理如 CollapsingToolbarLayout、AppBarLayout 等复杂的滚动联动结果。
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。以下是几种常见的解决方法:

[*]禁用子视图的嵌套滚动


[*] 形貌:禁用子视图的嵌套滚动功能,让父视图完全控制滚动活动。
[*] 示例:
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:nestedScrollingEnabled="false" />


[*]自界说 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 作为一个容器视图,通常会处理其内部的滚动事件。为了确保它能够精确处理滚动事件,可以重写 NestedScrollView 的 onInterceptTouchEvent 方法来拦截触摸事件,并决定是否让其处理:
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 方法中,子视图会处理传递给它的事件。如果子视图无法处理事件,它可以将事件传递给父视图或其他视图。
RecyclerView 的 onTouchEvent 方法负责处理其自己的触摸事件。通常,你不必要对 RecyclerView 的 onTouchEvent 举行特殊处理,但要确保它正常工作。
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),内部拦截和外部拦截的目标都是在父视图和子视图之间决定谁来处理滚动事件。在这种环境下,使用拦截机制是为了解决多个滚动视图对事件的夺取。


[*]内部拦截法:子视图决定是否处理事件。

[*]适用于子视图滚动逻辑复杂的环境,通常用于滚动冲突。

[*]外部拦截法:父视图决定是否处理事件。

[*]适用于父视图必要优先处理事件的环境,常用于解决滚动冲突。

滑动冲突和拦截机制

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


[*]内部拦截法:子视图根据触摸事件的方向和范例,决定是否继续处理触摸事件或传递给父视图。

[*]适用于必要子视图有更多自界说处理的滑动场景。

[*]外部拦截法:父视图判定当前手势是否属于自己的处理范围,比方 ViewPager 判定是水平滑动,则拦截事件。

[*]常用于父视图必要根据手势范例做判定的滑动冲突场景。

以上就是本篇博客的所有内容
已经到底啦!!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Android】NestedScrollView的简单用法与滚动冲突、滑动冲突