Android 持续滑动布局 ConsecutiveScrollerLayout 的使用
source link: http://mp.weixin.qq.com/s?__biz=MzIxNzU1Nzk3OQ%3D%3D&%3Bmid=2247490478&%3Bidx=1&%3Bsn=e3a287ea5d60223a7ba4444189328876
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
code小生 一个专注大前端领域的技术平台 公众号回复 Android
加入安卓技术群
作者:donkingliang
链接:https://www.jianshu.com/p/0783b0a37fa1
声明:本文已获 donkingliang
授权发表,转发等请联系原作者授权
在开发项目的时候,有时候会遇到一些比较复杂的页面,需要多个不同的列表或者滑动布局、甚至是 WebView
,组成一个完整的页面。要实现这样一个复杂的页面,在以前我们可能会通过布局嵌套的方式,在一个大的 ScrollView
下嵌套多个 RecyclerView
、 WebView
、 ScrollView
来实现。但是这种嵌套的方式不仅会严重影响布局的性能,而且处理滑动事件的冲突也是一件头疼的事,处理不好会严重影响用户操作的体验。
ConsecutiveScrollerLayout
正是为了解决这个难题而设计的滑动布局,它可以同时嵌套多个滑动布局(RecyclerView、WebView、ScrollView等)和普通控件(TextView、ImageView、LinearLayou、自定义View等),它把所有的子View看作是一个整体,由 ConsecutiveScrollerLayout
来统一处理布局的滑动,使得多个滑动布局就像一个整体一样连续滑动,它的效果就像是一个 ScrollView
一样。而且它支持嵌套所有的View,具有很好的通用性。
使用者不需要关心它是如何滑动的,也不需要考虑滑动的冲突问题,并且不用担心它会影响子 View
的性能。
下面是对 ConsecutiveScrollerLayout
的使用介绍和一写注意事项。
项目地址: https://github.com/donkingliang/ConsecutiveScroller
ConsecutiveScroller的设计思路和源码分析: https://www.jianshu.com/p/34d2c5cdb3fb
效果图
sample.gif sticky.gif引入依赖
在Project的build.gradle在添加以下代码
allprojects { repositories { ... maven { url 'https://jitpack.io' } } }
在Module的build.gradle在添加以下代码
implementation 'com.github.donkingliang:ConsecutiveScroller:2.6.2'
注意:如果你准备使用这个库,请务必认真阅读下面的文档。它能让你了解ConsecutiveScrollerLayout可以实现的功能,以及避免不必要的错误。
基本使用
ConsecutiveScrollerLayout的使用非常简单,把需要滑动的布局作为ConsecutiveScrollerLayout的直接子View即可。
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:background="@android:color/holo_red_dark" android:gravity="center" android:orientation="vertical"> </LinearLayout> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent"> </androidx.core.widget.NestedScrollView> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="fitXY" android:src="@drawable/temp" /> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> </ScrollView> <!-- 可以嵌套ConsecutiveScrollerLayout --> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/design_default_color_primary"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="" android:textColor="@android:color/black" android:textSize="18sp" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView3" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
关于margin
ConsecutiveScrollerLayout支持左右margin,但是不支持上下margin,子View间的间距可以通过Space设置。
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <!-- 使用Space设置上下边距 --> <Space android:layout_width="0dp" android:layout_height="20dp" /> <!-- ConsecutiveScrollerLayout支持左右margin,但是不支持上下margin --> <LinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" android:background="@android:color/holo_red_dark" android:gravity="center" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="LinearLayout" android:textColor="@android:color/black" android:textSize="18sp" /> </LinearLayout> <!-- 使用Space设置上下边距 --> <Space android:layout_width="0dp" android:layout_height="20dp" /> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
布局对齐方式
ConsecutiveScrollerLayout
的布局方式类似于垂直的 LinearLayout
,但是它没有gravity和子 view layout_gravity
属性。 ConsecutiveScrollerLayout
为它的子view提供了 layout_align
属性,用于设置子view和父布局的对齐方式。layout_align有三个值:左对齐(LEFT) 、右对齐(RIGHT) 和中间对齐(CENTER)。
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:padding="10dp" android:text="吸顶View - 1" android:textColor="@android:color/black" android:textSize="18sp" app:layout_isSticky="true" app:layout_align="LEFT"/> // 对齐方式 </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
嵌套Fragment
要想把一个 Fragment
嵌套在 ConsecutiveScrollerLayout
里。通常我们需要一个布局容器来承载我们的Fragment,或者直接把Fragment写在activity的布局里。如果Fragment是垂直滑动的,那么承载Fragment的容器需要是 ConsecutiveScrollerLayout
,以及Fragment的根布局也需要是垂直滑动的。我们推荐使用 ConsecutiveScrollerLayout
或者其他可垂直滑动的控件(比如:RecyclerView、NestedScrollView)作为Fragment的根布局。如果你的Fragment不是垂直滑动的,则可以忽略这个限制。
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <!-- 承载Fragment的容器 --> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent"/> <!-- MyFragment的根布局是垂直滑动控件 --> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.donkingliang.consecutivescrollerdemo.MyFragment"/> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
布局吸顶
要实现布局的吸顶效果,在以前,我们可能会写两个一摸一样的布局,一个隐藏在顶部,一个嵌套在 ScrollView
下,通过监听 ScrollView
的滑动来显示和隐藏顶部的布局。这种方式实现起来既麻烦也不优雅。 ConsecutiveScrollerLayout
内部实现了子View吸附顶部的功能,只要设置一个属性,就可以实现吸顶功能。而且支持设置多个子View吸顶,后面的View要吸顶时会把前面的吸顶View推出屏幕。
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <!-- 设置app:layout_isSticky="true"就可以使View吸顶 --> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:padding="10dp" android:text="吸顶View - 1" android:textColor="@android:color/black" android:textSize="18sp" app:layout_isSticky="true" /> <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:orientation="vertical" app:layout_isSticky="true"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="吸顶View - 2 我是个LinearLayout" android:textColor="@android:color/black" android:textSize="18sp" /> </LinearLayout> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:padding="10dp" android:text="吸顶View - 3" android:textColor="@android:color/black" android:textSize="18sp" app:layout_isSticky="true" /> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent"> </androidx.core.widget.NestedScrollView> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" android:padding="10dp" android:text="吸顶View - 4" android:textColor="@android:color/black" android:textSize="18sp" app:layout_isSticky="true" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView2" android:layout_width="match_parent" android:layout_height="wrap_content" /> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
常驻吸顶
如果你不希望吸顶的view被后面的吸顶view顶出屏幕,而且多个吸顶view排列吸附在顶部,你可以设置常驻吸顶模式:app:isPermanent="true"。
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" app:isPermanent="true" // 常驻吸顶 android:scrollbars="vertical"> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
关于吸顶功能的其他方法
// 设置吸顶到顶部的距离,在距离顶部一定距离时开始悬停吸顶 scrollerLayout.setStickyOffset(50); // 监听吸顶变化(普通模式) scrollerLayout.setOnStickyChangeListener(OnStickyChangeListener); // 监听吸顶变化(常驻模式) scrollerLayout.setOnPermanentStickyChangeListener(OnPermanentStickyChangeListener); // 获取当前吸顶view(普通模式) scrollerLayout.getCurrentStickyView(); // 获取当前吸顶view(常驻模式) scrollerLayout.getCurrentStickyViews();
局部滑动
ConsecutiveScrollerLayout
将所有的子View视作一个整体,由它统一处理页面的滑动事件,所以它默认会拦截可滑动的子View的滑动事件,由自己来分发处理。并且会追踪用户的手指滑动事件,计算调整 ConsecutiveScrollerLayout
滑动偏移。如果你希望某个子View自己处理自己的滑动事件,可以通过设置 layout_isConsecutive
属性来告诉父View不要拦截它的滑动事件,这样就可以实现在这个View自己的高度内滑动自己的内容,而不会作为 ConsecutiveScrollerLayout
的一部分来处理。
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <!--设置app:layout_isConsecutive="false"使父布局不拦截滑动事件--> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" app:layout_isConsecutive="false"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="下面的红色区域是个RecyclerView,它在自己的范围内单独滑动。" android:textColor="@android:color/black" android:textSize="18sp" app:layout_isSticky="true" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView1" android:layout_width="match_parent" android:layout_height="300dp" android:layout_marginLeft="50dp" android:layout_marginRight="50dp" android:layout_marginBottom="30dp" android:background="@android:color/holo_red_dark"/> </LinearLayout> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" android:text="下面的是个NestedScrollView,它在自己的范围内单独滑动。" android:textColor="@android:color/black" android:textSize="18sp" /> <androidx.core.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="250dp" app:layout_isConsecutive="false"> </androidx.core.widget.NestedScrollView> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
在这个例子中 NestedScrollView
希望在自己的高度里滑动自己的内容,而不是跟随 ConsecutiveScrollerLayout
滑动,只要给它设置 layout_isConsecutive="false"
就可以了。而 LinearLayout
虽然不是滑动布局,但是在下面嵌套了个滑动布局 RecyclerView
,所以它也需要设置 layout_isConsecutive="false"
。
ConsecutiveScrollerLayout
支持 NestedScrolling
机制,如果你的局部滑动的view实现了 NestedScrollingChild
接口(如:RecyclerView、NestedScrollView等),它滑动完成后会把滑动事件交给父布局。如果你不想你的子view或它的下级view与父布局嵌套滑动,可以给子view设置 app:layout_isNestedScroll="false"
。它可以禁止子view与 ConsecutiveScrollerLayout
的嵌套滑动
滑动子view的下级view
ConsecutiveScrollerLayout
默认情况下只会处理它的直接子view的滑动,但有时候需要滑动的布局可能不是 ConsecutiveScrollerLayout
的直接子view,而是子view所嵌套的下级view。比如 ConsecutiveScrollerLayout
嵌套 FrameLayout
, FrameLayout
嵌套 ScrollView
,我们希望 ConsecutiveScrollerLayout
也能正常处理ScrollView的滑动。为了支持这种需求, ConsecutiveScroller
提供了一个接口: IConsecutiveScroller
。子view实现 IConsecutiveScroller
接口,并通过实现接口方法告诉ConsecutiveScrollerLayout需要滑动的下级view, ConsecutiveScrollerLayout
就能正确地处理它的滑动事件。 IConsecutiveScroller
需要实现两个方法:
/** * 返回当前需要滑动的下级view。在一个时间点里只能有一个view可以滑动。 */ View getCurrentScrollerView(); /** * 返回所有可以滑动的子view。由于ConsecutiveScrollerLayout允许它的子view包含多个可滑动的子view,所以返回一个view列表。 */ List<View> getScrolledViews();
在前面提到的例子中,我们可以这样实现:
public class MyFrameLayout extends FrameLayout implements IConsecutiveScroller { @Override public View getCurrentScrollerView() { // 返回需要滑动的ScrollView return getChildAt(0); } @Override public List<View> getScrolledViews() { // 返回需要滑动的ScrollView List<View> views = new ArrayList<>(); views.add(getChildAt(0)); return views; } }
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <com.donkingliang.consecutivescrollerdemo.widget.MyFrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> </LinearLayout> </ScrollView> </com.donkingliang.consecutivescrollerdemo.widget.MyFrameLayout> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
这样 ConsecutiveScrollerLayout
就能正确地处理 ScrollView
的滑动。这是一个简单的例子,在实际的需求中,我们一般不需要这样做。
注意:
1、getCurrentScrollerView()和getScrolledViews()必须正确地返回需要滑动的view,这些view可以是经过多层嵌套的,不一定是直接子view。所以使用者应该按照自己的实际场景去实现者两个方法。
2、滑动的控件应该跟嵌套它的子view的高度保持一致,也就是说滑动的控件高度应该是match_parent,并且包裹它的子view和它本身都不要设置上下边距(margin和ppadding)。宽度没有这个限制。
对ViewPager的支持
如果你的 ViewPager
承载的子布局(或Fragment)不是可以垂直滑动的,那么使用普通的 ViewPager
即可。如果是可以垂直滑动的,那么你的ViewPager需要实现 IConsecutiveScroller
接口,并返回需要滑动的view对象。框架里提供了一个实现了 IConsecutiveScroller
接口自定义控件: ConsecutiveViewPager
。使用这个控件,然后你的 ConsecutiveViewPager
的子view(或Fragment的根布局)是可垂直滑动的view,如:RecyclerView、NestedScrollView、ConsecutiveScrollerLayout即可。这样你的 ViewPager
就能正确地跟 ConsecutiveScrollerLayout
一起滑动了。
<?xml version="1.0" encoding="utf-8"?> <com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/scrollerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical"> <com.google.android.material.tabs.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/white" app:tabGravity="fill" app:tabIndicatorColor="@color/colorPrimary" app:tabIndicatorHeight="3dp" app:tabMode="scrollable" app:tabSelectedTextColor="@color/colorPrimary" /> <com.donkingliang.consecutivescroller.ConsecutiveViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.donkingliang.consecutivescroller.ConsecutiveScrollerLayout>
布局吸顶时会覆盖在下面的布局的上面,有时候我们希望TabLayout吸顶悬浮在顶部,但是不希望它覆盖遮挡ViewPager的内容。ConsecutiveViewPager提供了setAdjustHeight调整自己的布局高度,让自己不被TabLayout覆盖。注意:只有ConsecutiveScrollerLayout是ConsecutiveScrollerLayout的最低部时才能这样做。
// 保证能获取到tabLayout的高度 tabLayout.post(new Runnable() { @Override public void run() { viewPager.setAdjustHeight(tabLayout.getHeight()); } });
其他注意事项
1、WebView在加载的过程中如果滑动的布局,可能会导致WebView与其他View在显示上断层,使用下面的方法一定程度上可以避免这个问题。
webView.setWebChromeClient(new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); scrollerLayout.checkLayoutChange(); } });
2、 SmartRefreshLayout
和 SwipeRefreshLayout
等刷新控件可以嵌套 ConsecutiveScrollerLayout
实现下拉刷新功能,但是 ConsecutiveScrollerLayout
内部嵌套它们来刷新子view,因为子view时 ConsecutiveScrollerLayout
滑动内容等一部分。除非你给 SmartRefreshLayout
或者 SwipeRefreshLayout
设置 app:layout_isConsecutive="false"
。
3、继承AbsListView的布局(ListView、GridView等),在滑动上可能会与用户的手指滑动不同步,推荐使用RecyclerView代替。
4、 ConsecutiveScroller
的 minSdkVersion
是 19,如果你的项目支持19 以下,可以设置:
<uses-sdk tools:overrideLibrary="com.donkingliang.consecutivescroller"/>
但是不要在 minSdkVersion
小于 19 的项目使用 AbsListView
的子类,因为 ConsecutiveScrollerLayout
使用了只有19以上才有的 AbsListView API
。
5、使用 ConsecutiveScrollerLayout
提供的 setOnVerticalScrollChangeListener()
方法监听布局的滑动事件。View所提供的 setOnScrollChangeListener()
方法已无效。
6、通过 getOwnScrollY()
方法获取 ConsecutiveScrollerLayout
的垂直滑动距离,View的 getScrollY()
方法获取的不是 ConsecutiveScrollerLayout
的整体滑动距离。
Recommend
-
93
作者:许方镇 前言 首次通过右滑来返回到上一个页面的操作是在 IOS7上出现。到目前android应用上支持这种操作的依然不多。分析其主要原因应该是android已有实体的返回按键,这样的功能变得不重要,但我觉得有这样的功能便于单手操作,能提升app的用户体验
-
4
Android SwipeRefreshLayout左右滑动冲突的解决 我有一个页面是需要下拉刷新的,所以在 <RecyclerView> 的外层包了 <SwipeRefreshLayout>,但是同时我希望 <RecyclerView> 里的组件,可以...
-
11
Android滑动时隐藏FAB 我在BaseClass里加入了如下代码,实现了在滑动 RecyclerView 的时候,隐藏浮动按钮(FAB): fun setUpFABHide(recyclerView: RecyclerView, fab: FloatingActionButton) { recyclerVie...
-
4
在android开发中,滑动对一个app来说,是非常重要的,流畅的滑动操作,能够给用户带来用好的体验,那么本次就来讲讲android中实现滑动有哪些方式。其实滑动一个View,本质上是移动一个View,改变其当前所属的位置,要实现View的滑动,就必须监听用户触摸的事件...
-
10
ASP.NET Core中使用滑动窗口限流 滑动窗口算法...
-
5
Android 快速实现 ViewPager 滑动页卡切换(可用作整个 app上导航) – Android开发中文站 你的位置:Android开发中文站 >
-
1
软硬件环境 Windows 10 64bit Anaconda3 with python 3.8 PyQt5 5.15 滑动条 QSlider 也是一个常见的控件,有水平滑动条和垂直滑动条之分,它通常用来获取用户输入,一般是数值,简单直观。 ...
-
3
本文已收录到 GitHub · AndroidFamily,有 Android 进阶知识体系,欢迎 Star。技术和职场问题,请关注公众号 [彭旭锐] 私信我提问。 ...
-
4
【OpenCV-Python】滑动条的创建和使用(createTrackbar()) 精选 原创 domi+1 2022-12-1...
-
6
Android滑动冲突是Android开发中常见的问题。在一个界面中,可能存在多个View可以响应滑动事件。如果这些View滑动方向一致,则会导致滑动冲突。本文将从原理、使用与优化三个方面,详细介绍Android
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK