前言
系列文章
這篇文章是這個系列的第六篇文章了,下面是前五篇文章:
按照慣例,放一下 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)
還是那句話,如果只是一個地方調用,怎么寫都是對的,但如果很多地方調用的話抽出來就很有必要了!
大家可以下載項目看下歷史提交,省了很多代碼。
精致的結尾
到這里本篇文章就要和大家說再見了,這篇文章雖然實現的功能很簡單,但也能提升一些用戶體驗!歡迎大家下載體驗。
能力一般、水平有限,對大家有幫助的話別忘了三連,有 Github 賬號的幫忙點個 Star ,感激不盡!
就這樣,下回再見!!!