前言
系列文章
这篇文章是这个系列的第六篇文章了,下面是前五篇文章:
按照惯例,放一下 Github 地址和 apk 下载地址吧!
apk 下载地址:www.pgyer.com/llj2
Github地址:github.com/zhujiang521…
前因后果
RecyclerView 大家平时都会使用,在本项目中 RecyclerView 使用方法基本都是下面这种:
基本都是文章的列表,包括有下拉刷新和上拉加载,下拉刷新和上拉加载用的是下面这个库:
下面是这个库的依赖:
api 'com.scwang.smart:refresh-layout-kernel:2.0.1' //核心必须依赖 api 'com.scwang.smart:refresh-header-classics:2.0.1' //经典刷新头 api 'com.scwang.smart:refresh-footer-classics:2.0.1' //经典加载
这个库的使用方法在下面会有提及,不过更建议去官方 Github 上看文档。
说到这里,终于要进入正题了!今天到底要实现一个什么功能呢?
故事发生在上周末,我无聊在刷着自己写的玩安卓,在看最新的一些文章,刷了好久,看了很多条目,突然想回到顶部,就只能一直往下滑了,想了下这也太不人性化了吧!好多软件都有一键置顶的功能,也是很方便的,说干就干,那就来给玩安卓也添加一个一键置顶的功能吧!
先来看看最终的实现效果吧!
开始实现
实现
其实核心实现非常简单,只需要下面的一行代码:
mToTopRecycleView.smoothScrollToPosition(0)
这个大家都会,我就不多解释了。
我展示一键置顶按钮的时机是只有在上滑的时候才会展示,平时不展示,下滑的时候也不展示,,当上滑到顶部的时候也不展示。为什么这样设置呢?因为我个人认为只有用户向上滑了才证明用户又可能想回到顶部。
那么下面就来看下怎样设置展示时机吧!
mToTopRecycleView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { if (!recyclerView.canScrollVertically(-1)) { // 上滑到顶部 } else if (dy < 0) { // 上滑 } else if (dy > 0) { // 下滑 } } })
其实到这里基本已经实现功能了,只需要在布局中添加一个按钮,然后给按钮添加下点击事件就行了。但是。。。。。。
凡事就怕但是,如果只是这一个页面需要一键置顶的话那就简单了,在这个页面布局中加一个按钮就好,但是文章列表有很多个页面啊,不止一个!而且都有下拉刷新和上拉加载的功能,本着程序员的懒劲,还是少写点吧,抽一个控件出来吧,别的地方如果需要使用的话直接用这个控件就行!那么就开始吧!
先来想一下这个控件的父类该是谁,肯定是一个 ViewGroup,其实布局并不难,那就使用 FrameLayout 吧!
class ToTopRecyclerView @JvmOverloads constructor( private val mContext: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : FrameLayout(mContext, attrs, defStyleAttr)
定义好类就完成了一大半了,为啥呢?万事开头难嘛!头都开了,还怕啥!
接下来在 init 方法中将写好的布局加载进来:
init{ View.inflate(mContext, R.layout.layout_to_top, this) }
来看下布局文件吧:
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.scwang.smart.refresh.layout.SmartRefreshLayout android:id="@+id/toTopSmartRefreshLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/toTopRecycleView" android:layout_width="match_parent" android:layout_height="match_parent" /> </com.scwang.smart.refresh.layout.SmartRefreshLayout> <ImageView android:id="@+id/toTopIvClick" android:layout_width="@dimen/dp_40" android:layout_height="@dimen/dp_40" android:layout_gravity="right|bottom" android:layout_margin="@dimen/dp_20" android:background="@drawable/to_top_bg" android:padding="@dimen/dp_8" android:src="@drawable/ic_baseline_vertical_align_top_24" android:visibility="gone" /> </merge>
为啥最外面是 merge 就不说了,这要是不会赶快再去看看基础吧。
下面就是 findViewById 了,这个太简单就不贴代码了,浪费时间。
接下来需要想一下咱们想让这个控件完成什么功能,思来想去一共有以下几个功能:
- 设置 RecyclerView 的 adapter
- 设置上拉加载和下拉刷新的事件
- 设置 RecyclerView 的 LayoutManager
可能有人会说,一键置顶不需要吗?如果需要的话那咱们写这个控件的意义何在啊!
下面就根据上面的列表顺序来一个一个写吧!
首先是设置 RecyclerView 的 adapter:
fun setAdapter(adapter: RecyclerView.Adapter<BaseListAdapter.ViewHolder>) { mToTopRecycleView.adapter = adapter }
代码很简单,只是提供给外部一个设置 adapter 的入口。
然后是设置上拉加载和下拉刷新的事件:
fun onRefreshListener(onRefreshListener: () -> Unit, onLoadMoreListener: () -> Unit) { mToTopSmartRefreshLayout.apply { setOnRefreshListener { reLayout -> reLayout.finishRefresh(measureTimeMillis { onRefreshListener.invoke() }.toInt()) } setOnLoadMoreListener { reLayout -> val time = measureTimeMillis { onLoadMoreListener.invoke() }.toInt() reLayout.finishLoadMore(if (time > mLoadTime) time else mLoadTime) } } }
这个方法有必要说下了,这就是文章开头所说那个库的使用方法。
这块使用了一个高阶函数,方法参数是两个函数,通过名称就可以知道第一个是刷新的函数,第二个是加载更多的函数。
最后是设置 RecyclerView 的 LayoutManager,为了省事我决定将这个函数写的方便一些,别的地方调用的时候只需要传入布尔值或者 int ,然后通过判断设置不同的 LayoutManager。
思来想去,本项目中的 RecyclerView 只用到了两种 LayoutManager,分别是 LinearLayoutManager(竖屏) 和 StaggeredGridLayoutManager(横屏),那么就用布尔值作为参数来判断使用哪种 LayoutManager 吧,下面是方法的代码:
fun setRecyclerViewLayoutManager(isLinearLayout: Boolean) { if (isLinearLayout) { mToTopRecycleView.layoutManager = LinearLayoutManager(context) } else { val spanCount = 2 val layoutManager = StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL) mToTopRecycleView.layoutManager = layoutManager layoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_NONE } }
OK,这就差不多了。
使用
实现完成了就该使用了,实践是检验真理的唯一标准!
先给首页尝试下,添加到布局中:
<com.zj.core.view.custom.ToTopRecyclerView android:id="@+id/homeToTopRecyclerView" android:layout_width="match_parent" android:layout_height="match_parent"/>
注意!上面写的下面就要进行调用了!
homeToTopRecyclerView.setAdapter(articleAdapter) homeToTopRecyclerView.onRefreshListener({ page = 1 getArticleList(true) }, { page++ getArticleList(true) }) homeToTopRecyclerView.setRecyclerViewLayoutManager(true)
还是那句话,如果只是一个地方调用,怎么写都是对的,但如果很多地方调用的话抽出来就很有必要了!
大家可以下载项目看下历史提交,省了很多代码。
精致的结尾
到这里本篇文章就要和大家说再见了,这篇文章虽然实现的功能很简单,但也能提升一些用户体验!