广告
返回顶部
首页 > 资讯 > 精选 >Android嵌套滚动和协调滚如何实现
  • 870
分享到

Android嵌套滚动和协调滚如何实现

2023-07-02 10:07:57 870人浏览 安东尼
摘要

这篇文章主要介绍了Android嵌套滚动和协调滚如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Android嵌套滚动和协调滚如何实现文章都会有所收获,下面我们一起来看看吧。Android的嵌套滚动的几种

这篇文章主要介绍了Android嵌套滚动和协调滚如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Android嵌套滚动和协调滚如何实现文章都会有所收获,下面我们一起来看看吧。

Android的嵌套滚动的几种实现方式

什么叫嵌套滚动?什么叫协调滚动?

只要是涉及到滚动那必然父容器和子容器,按照原理来说子容器先滚动,当子容器滚不动了再让父容器滚动,或者先让父容器滚动,父容器滚不动了再让子容器滚动,这种就叫嵌套滚动。代表为 NestedScrollView 。

如果只是子容器滚动,父容器中的其他控件在子容器滚动过程中做一些布局,透明度,动画等操作,这种叫协调滚动。代表为 CoordinatorLayout 。

这里我们从嵌套滚动的实现方式开始讲起。

一、嵌套滚动 NestedScrollingParent/Child

最近看到一些文章又开始讲 NestedScrollingParent/Child 的嵌套滚动了,这...属实是怀旧了。

依稀记得大概是2017年左右吧,谷歌出了一个 NestedScrollingParent/Child 嵌套滚动,当时应该是很轰动的。Android 开发者真的苦于嵌套滚动久矣。

NestedScrolling 机制能够让父view和子view在滚动时进行配合,其基本流程如下:

  • 当子view开始滚动之前,可以通知父view,让其先于自己进行滚动;

  • 子view自己进行滚动

  • 子view滚动之后,还可以通知父view继续滚动

要实现这样的交互,父View需要实现 NestedScrollingParent 接口,而子View需要实现 NestedScrollinGChild 接口。

作为一个可以嵌入 NestedScrollingChild 的父 View,需要实现 NestedScrollingParent,这个接口方法和 NestedScrollingChild 大致有一一对应的关系。同样,也有一个 NestedScrollingParentHelper 辅助类来默默的帮助你实现和 Child 交互的逻辑。滑动动作是 Child 主动发起,Parent 就收滑动回调并作出响应。

  • 从上面的 Child 分析可知,滑动开始的调用 startNestedScroll(),Parent 收到 onStartNestedScroll() 回调,决定是否需要配合 Child 一起进行处理滑动,如果需要配合,还会回调 onNestedScrollAccepted()。

  • 每次滑动前,Child 先询问 Parent 是否需要滑动,即 dispatchNestedPreScroll(),这就回调到 Parent 的 onNestedPreScroll(),Parent 可以在这个回调中“劫持”掉 Child 的滑动,也就是先于 Child 滑动。

  • Child 滑动以后,会调用 onNestedScroll(),回调到 Parent 的 onNestedScroll(),这里就是 Child 滑动后,剩下的给 Parent 处理,也就是 后于 Child 滑动。

  • 最后,滑动结束,调用 onStopNestedScroll() 表示本次处理结束。

更详细的教程大家可以看看鸿洋的文章。

这里我做一个简单的示例,后面的效果都是基于这个布局实现。

public class MyNestedScrollChild extends LinearLayout implements NestedScrollingChild {    private NestedScrollingChildHelper mScrollingChildHelper;    private final int[] offset = new int[2];    private final int[] consumed = new int[2];    private int lastY;    private int mShowHeight;    public MyNestedScrollChild(Context context) {        super(context);    }    public MyNestedScrollChild(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //第一次测量,因为布局文件中高度是wrap_content,因此测量模式为ATMOST,即高度不能超过父控件的剩余空间        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        mShowHeight = getMeasuredHeight();        //第二次测量,对高度没有任何限制,那么测量出来的就是完全展示内容所需要的高度        heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    public boolean onTouchEvent(MotionEvent e) {        switch (e.getAction()) {            case MotionEvent.ACTION_DOWN:                lastY = (int) e.getRawY();                break;            case MotionEvent.ACTION_MOVE:                int y = (int) (e.getRawY());                int dy = y - lastY;                lastY = y;                if (startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL) //如果找到了支持嵌套滚动的父类                        && dispatchNestedPreScroll(0, dy, consumed, offset)) {//父类进行了一部分滚动                    int remain = dy - consumed[1];//获取滚动的剩余距离                    if (remain != 0) {                        scrollBy(0, -remain);                    }                } else {                    scrollBy(0, -dy);                }        }        return true;    }    //scrollBy内部会调用scrollTo    //限制滚动范围    @Override    public void scrollTo(int x, int y) {        int MaxY = getMeasuredHeight() - mShowHeight;        if (y > MaxY) {            y = MaxY;        }        if (y < 0) {            y = 0;        }        super.scrollTo(x, y);    }    private NestedScrollingChildHelper getScrollingChildHelper() {        if (mScrollingChildHelper == null) {            mScrollingChildHelper = new NestedScrollingChildHelper(this);            mScrollingChildHelper.setNestedScrollingEnabled(true);        }        return mScrollingChildHelper;    }    @Override    public void setNestedScrollingEnabled(boolean enabled) {        getScrollingChildHelper().setNestedScrollingEnabled(enabled);    }    @Override    public boolean isNestedScrollingEnabled() {        return getScrollingChildHelper().isNestedScrollingEnabled();    }    @Override    public boolean startNestedScroll(int axes) {        return getScrollingChildHelper().startNestedScroll(axes);    }    @Override    public void stopNestedScroll() {        getScrollingChildHelper().stopNestedScroll();    }    @Override    public boolean hasNestedScrollingParent() {        return getScrollingChildHelper().hasNestedScrollingParent();    }    @Override    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);    }    @Override    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);    }    @Override    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {        return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);    }    @Override    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {        return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);    }}

定义Parent实现文本布局置顶效果:

public class MyNestedScrollParent extends LinearLayout implements NestedScrollingParent {    private ImageView img;    private TextView tv;    private MyNestedScrollChild nsc;    private NestedScrollingParentHelper mParentHelper;    private int imgHeight;    private int tvHeight;    public MyNestedScrollParent(Context context) {        super(context);        init();    }    public MyNestedScrollParent(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    private void init() {        mParentHelper = new NestedScrollingParentHelper(this);    }    //获取子view    @Override    protected void onFinishInflate() {        img = (ImageView) getChildAt(0);        tv = (TextView) getChildAt(1);        nsc = (MyNestedScrollChild) getChildAt(2);        img.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                if (imgHeight <= 0) {                    imgHeight = img.getMeasuredHeight();                }            }        });        tv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                if (tvHeight <= 0) {                    tvHeight = tv.getMeasuredHeight();                }            }        });        super.onFinishInflate();    }    //在此可以判断参数target是哪一个子view以及滚动的方向,然后决定是否要配合其进行嵌套滚动    @Override    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {        if (target instanceof MyNestedScrollChild) {            return true;        }        return false;    }    @Override    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {        mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);    }    @Override    public void onStopNestedScroll(View target) {        mParentHelper.onStopNestedScroll(target);    }    //先于child滚动    //前3个为输入参数,最后一个是输出参数    @Override    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {        if (showImg(dy) || hideImg(dy)) {//如果需要显示或隐藏图片,即需要自己(parent)滚动            scrollBy(0, -dy);//滚动            consumed[1] = dy;//告诉child我消费了多少        }    }    //后于child滚动    @Override    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {    }    //返回值:是否消费了fling    @Override    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {        return false;    }    //返回值:是否消费了fling    @Override    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {        return false;    }    @Override    public int getNestedScrollAxes() {        return mParentHelper.getNestedScrollAxes();    }    //--------------------------------------------------    //下拉的时候是否要向下滚动以显示图片    public boolean showImg(int dy) {        if (dy > 0) {            if (getScrollY() > 0 && nsc.getScrollY() == 0) {                return true;            }        }        return false;    }    //上拉的时候,是否要向上滚动,隐藏图片    public boolean hideImg(int dy) {        if (dy < 0) {            if (getScrollY() < imgHeight) {                return true;            }        }        return false;    }    //scrollBy内部会调用scrollTo    //限制滚动范围    @Override    public void scrollTo(int x, int y) {        if (y < 0) {            y = 0;        }        if (y > imgHeight) {            y = imgHeight;        }        super.scrollTo(x, y);    }}

页面的布局如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout 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:orientation="vertical"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@color/white">    <com.guadou.lib_baselib.view.titlebar.EasyTitleBar        android:layout_width="match_parent"        android:layout_height="wrap_content"        app:Easy_title="NestedParent/Child的滚动" />    <com.guadou.kt_demo.demo.demo8_recyclerview.scroll8.MyNestedScrollParent        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        <ImageView            android:layout_width="match_parent"            android:layout_height="150dp"            android:contentDescription="我是测试的图片"            android:src="@mipmap/ic_launcher" />        <TextView            android:layout_width="match_parent"            android:layout_height="50dp"            android:gravity="center"            android:background="#ccc"            android:text="我是测试的分割线" />        <com.guadou.kt_demo.demo.demo8_recyclerview.scroll8.MyNestedScrollChild            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:orientation="vertical">            <TextView                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:text="@string/scroll_content" />        </com.guadou.kt_demo.demo.demo8_recyclerview.scroll8.MyNestedScrollChild>    </com.guadou.kt_demo.demo.demo8_recyclerview.scroll8.MyNestedScrollParent></LinearLayout>

二、嵌套滚动 NestedScrollView

NestedScrollingParent/Child 的定义也太过复杂了吧,如果只是一些简单的效果如 ScrollView 嵌套 LinearLayout 这样的简单效果,我们直接可以使用 NestedScrollView 来实现

因此,我们可以简单的把 NestedScrollView 类比为 ScrollView,其作用就是作为控件父布局,从而具备嵌套滑动功能。

<?xml version="1.0" encoding="utf-8"?><LinearLayout 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:background="@color/white"    android:orientation="vertical">    <com.guadou.lib_baselib.view.titlebar.EasyTitleBar        android:layout_width="match_parent"        android:layout_height="wrap_content"        app:Easy_title="NestedScrollView的滚动" />    <androidx.core.widget.NestedScrollView        android:layout_width="match_parent"        android:layout_height="wrap_content">        <LinearLayout            android:orientation="vertical"            android:layout_width="match_parent"            android:layout_height="wrap_content">            <ImageView                android:layout_width="match_parent"                android:layout_height="150dp"                android:contentDescription="我是测试的图片"                android:src="@mipmap/ic_launcher" />            <TextView                android:layout_width="match_parent"                android:layout_height="50dp"                android:gravity="center"                android:background="#ccc"                android:text="我是测试的分割线" />            <ScrollView                android:layout_width="match_parent"                android:layout_height="wrap_content">                <TextView                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:text="@string/scroll_content" />            </ScrollView>        </LinearLayout>    </androidx.core.widget.NestedScrollView></LinearLayout>

三、嵌套滚动-自定义布局

除了使用官方提供的方式,我们还能使用自定义View的方式,自己处理事件与监听。

使用自定义ViewGroup的方式,添加全部的布局,并测量与排版,并且对事件做拦截处理。内部是如LinearLayout的垂直布局,实现了 ScrollingView 支持滚动,并处理滚动。有源码,大概2800行代码,这里就不方便贴出来了。

如何使用:

<?xml version="1.0" encoding="utf-8"?><LinearLayout 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:background="@color/white"    android:orientation="vertical">    <com.guadou.lib_baselib.view.titlebar.EasyTitleBar        android:layout_width="match_parent"        android:layout_height="wrap_content"        app:Easy_title="自定义View实现的滚动" />    <com.guadou.kt_demo.demo.demo8_recyclerview.scroll10.ConsecutiveScrollerLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:scrollbars="vertical">        <ImageView            android:layout_width="match_parent"            android:layout_height="150dp"            android:contentDescription="我是测试的图片"            android:src="@mipmap/ic_launcher" />        <TextView            app:layout_isSticky="true"   //可以实现吸顶效果            android:layout_width="match_parent"            android:layout_height="50dp"            android:gravity="center"            android:background="#ccc"            android:text="我是测试的分割线" />        <ScrollView            android:layout_width="match_parent"            android:layout_height="wrap_content">            <TextView                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:text="@string/scroll_content" />        </ScrollView>    </com.guadou.kt_demo.demo.demo8_recyclerview.scroll10.ConsecutiveScrollerLayout></LinearLayout>

关于“Android嵌套滚动和协调滚如何实现”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Android嵌套滚动和协调滚如何实现”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

--结束END--

本文标题: Android嵌套滚动和协调滚如何实现

本文链接: https://www.lsjlt.com/news/341290.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android嵌套滚动和协调滚如何实现
    这篇文章主要介绍了Android嵌套滚动和协调滚如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Android嵌套滚动和协调滚如何实现文章都会有所收获,下面我们一起来看看吧。Android的嵌套滚动的几种...
    99+
    2023-07-02
  • Android嵌套滚动与协调滚动如何实现
    本文小编为大家详细介绍“Android嵌套滚动与协调滚动如何实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“Android嵌套滚动与协调滚动如何实现”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。一、Coord...
    99+
    2023-07-02
  • Android嵌套滚动和协调滚动的多种实现方法
    目录Android的嵌套滚动的几种实现方式一、嵌套滚动 NestedScrollingParent/Child二、嵌套滚动 NestedScrollView三、嵌套滚动-自定义布局总...
    99+
    2022-11-13
  • Android嵌套滚动与协调滚动的实现方式汇总
    目录Android的协调滚动的几种实现方式一、CoordinatorLayout + Behavior二、CoordinatorLayout + AppBarLayout三、Moti...
    99+
    2022-11-13
  • Android中协调滚动布局的实现代码
    目录使用 AppbarLayout 和 MotionLayout 实现常用的布局效果一、AppbarLayout + ViewPager二、AppbarLayout + Recycl...
    99+
    2022-11-13
  • Android自定义ViewGroup嵌套与交互实现幕布全屏滚动
    目录前言一、布局的测量与布局二、全屏滚动逻辑三、抽取Adapter与LayoutManager四、自定义属性后记自定义 ViewGroup 全屏选中效果 前言 事情是这个样子的,前几...
    99+
    2023-01-17
    Android ViewGroup全屏滚动 Android ViewGroup嵌套交互
  • Android滚动截屏如何实现
    要实现Android滚动截屏,可以使用以下步骤:1. 获取屏幕的宽度和高度。可以通过以下代码获取:```DisplayMetrics...
    99+
    2023-08-24
    Android
  • Android中TextView实现垂直滚动和上下滚动效果
    布局里面就是两个自定义的TextView,上面的左右滑动的是AutoHorizontalScrollTextView; 下面上下滚动的是AutoVerticalScrollT...
    99+
    2022-06-06
    动效 Android
  • Android中如何实现视差滚动
    这篇文章将为大家详细讲解有关Android中如何实现视差滚动,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是视差滚动?视差滚动原本是一个天文学术语,当我们观察星空的时候,离我们比较远的星星移动速度比较...
    99+
    2023-06-22
  • 如何使用CSS实现无滚动条滚动
    小编给大家分享一下如何使用CSS实现无滚动条滚动,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!第一种:伪对象选择器在webkit内核的浏览器里可以定义滚动条样式。...
    99+
    2023-06-08
  • vue监听滚动事件如何实现滚动监听
    这篇文章主要为大家展示了“vue监听滚动事件如何实现滚动监听”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“vue监听滚动事件如何实现滚动监听”这篇文章吧。在vu...
    99+
    2022-10-19
  • Android代码实现AdapterViews和RecyclerView无限滚动
    应用的一个共同的特点就是当用户欢动时自动加载更多的内容,这是通过用户滑动触发一定的阈值时发送数据请求实现的。 相同的是:信息实现滑动的效果需要定义在列表中最后一个可见项,和某...
    99+
    2022-06-06
    recyclerview Android
  • bootstrap如何实现滚动条
    本篇内容介绍了“bootstrap如何实现滚动条”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2022-10-19
  • react如何实现滚动条
    这篇“react如何实现滚动条”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“react如何实现滚动条”文章吧。react实现...
    99+
    2023-07-04
  • Android如何实现页面嵌套
    在Android中,可以使用多种方式实现页面嵌套,以下是其中几种常用的方式:1. 使用Fragment:Fragment是Andro...
    99+
    2023-08-09
    Android
  • 如何实现CSS隐藏滚动条并可以滚动内容
    这篇文章主要介绍“如何实现CSS隐藏滚动条并可以滚动内容”,在日常操作中,相信很多人在如何实现CSS隐藏滚动条并可以滚动内容问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何...
    99+
    2022-10-19
  • 移动端如何实现内滚动
    这篇文章主要为大家展示了“移动端如何实现内滚动”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“移动端如何实现内滚动”这篇文章吧。发现需求如果在一个区域内只允许部分区域产生滚动的效果,而其余部分不能...
    99+
    2023-06-08
  • DIV如何实现自动滚动功能及滚动条颜色修改
    这篇文章将为大家详细讲解有关DIV如何实现自动滚动功能及滚动条颜色修改,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1.DIV 自动滚动<script t...
    99+
    2022-10-19
  • Javascript如何实现字幕滚动
    小编给大家分享一下Javascript如何实现字幕滚动,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Javascript实现字幕...
    99+
    2022-10-19
  • jQuery如何实现滚动效果
    这篇文章主要介绍了jQuery如何实现滚动效果,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1. 图片轮播:原理如下: 假设有三张图片,三张...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作