SlidableLayout 致力于打造通用、易用和流畅的上下滑动翻页布局。专注于通用的上下切换场景,包括但不限于直播间切换、阅读图书翻页、短视频应用等。
- 通用的基本场景,可以上下滑切换
View
或者Fragment
- 使用适配器模式,继承
SlideAdapter
、SlideViewAdapter
或者SlideFragmentAdapter
来自定义业务逻辑 - 只复用两个
View
(Fragment
), 上下滑只是轮流切换两个View
的位置,没有多余的性能消耗 - 充足的时序回调,可以在滑动过程中掌握 开始可见 ,完全可见,完全不可见 的时机
- 支持无限滑动
- 支持嵌套滑动,可与其他实现
NestedScrolling
机制的布局配合使用,比如 SwipeRefreshLayout 等刷新加载布局
目前网络上大部分上下滑方案都是围绕 ViewPager
或者 RecyclerView
+ SnapHelper
。
这是我的个人见解:为什么我不用ViewPager或RecyclerView来做上下滑布局。
<com.yy.mobile.widget.SlidableLayout
android:layout_width="match_parent"
android:layout_height="match_parent"/>
SlidableLayout
本身实现了 NestedScrollingChild 接口,因此可以在外层嵌套其他滑动布局,比如自定义你的下拉刷新与上拉加载。
以滑动切换 Fragment
为例子,先自定义继承 SlideFragmentAdapter
并实现对 UI 的绑定,以及是否可以滑动的判断:
class MyAdapter(fm: FragmentManager) : SlideFragmentAdapter(fm) {
private val data = listOf("a", "b", "c", "d")
private var currentIndex = 0
/**
* 能否向 [direction] 的方向滑动。
*
* @param direction 滑动的方向
* @return 返回 true 表示可以滑动, false 表示不可滑动。
* 如果有嵌套其他外层滑动布局(比如下拉刷新),当且仅当返回 false 时会触发外层的嵌套滑动。
*/
override fun canSlideTo(direction: SlideDirection): Boolean {
val index =
when (direction) {
SlideDirection.Next -> currentIndex + 1 //能否滑向下一个
SlideDirection.Prev -> currentIndex - 1 //能否滑向上一个
else -> currentIndex
}
return index in 0 until data.size
}
/**
* 创建要显示的 [Fragment]。
* 一般来说,该方法会在 [SlidableLayout.setAdapter] 调用时触发一次,创建当前显示的 [Fragment],
* 会在首次开始滑动时触发第二次,创建滑动目标的 [Fragment]。
*/
override fun onCreateFragment(context: Context): Fragment {
return DemoFragment()
}
/**
* 把 [direction] 方向那个数据与 [fragment] 绑定。做一些 ui 的显示操作。
*/
override fun onBindFragment(fragment: Fragment, direction: SlideDirection) {
val index =
when (direction) {
SlideDirection.Next -> currentIndex + 1 //绑定下一个的数据
SlideDirection.Prev -> currentIndex - 1 //绑定上一个的数据
SlideDirection.Origin -> currentIndex
}
//bind data to the ui
(fragment as DemoFragment).currentData = data[index]
}
/**
* 滑动结束后回调
*/
override fun finishSlide(direction: SlideDirection) {
super.finishSlide(direction)
// 修正当前的索引
currentIndex =
when (direction) {
SlideDirection.Next -> currentIndex + 1 //已经滑向下一个
SlideDirection.Prev -> currentIndex - 1 //已经滑向上一个
SlideDirection.Origin -> currentIndex //原地回弹
}
}
}
通过 setAdapter
方法就会把 Fragment
显示到 SlideableLayout
上:
slidable_layout.setAdapter(MyAdapter(supportFragmentManager))
更详细的适配器使用可以参照 demo 。
在 SlideAdapter
中,通过 onCreateView
或者 onCreateFragment
创建滑动切换的 View
或者 Fragment
。这些自定义的 View
或者 Fragment
可以实现 SlidableUI
接口,来监听滑动的时机回调:
class DemoFragment : Fragment(), SlidableUI {
override fun startVisible(direction: SlideDirection) {
// 滑动开始,当前视图将要可见
// 可以在该回调中实现数据与视图的绑定,比如显示占位的图片
}
override fun completeVisible(direction: SlideDirection) {
// 滑动完成,当前视图完全可见
// 可以在该回调中开始主业务,比如开始播放视频
}
override fun invisible(direction: SlideDirection) {
// 滑动完成,当前视图完全不可见
// 可以在该回调中做一些清理工作,比如关闭播放器
}
override fun preload(direction: SlideDirection) {
// 已经完成了一次 direction 方向的滑动,用户很可能会在这个方向上继续滑动
// 可以在该回调中实现下一次滑动的预加载,比如开始下载下一个视频或者准备好封面图
}
}
-
根目录的 build.gradle 中添加
allprojects { repositories { ... maven { url 'https://jitpack.io' } } }
-
对应要使用的模块中添加依赖
dependencies { implementation 'com.github.YvesCheung:SlidableLayout:x.y.z' }
Copyright 2019 YvesCheung
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.