下拉刷新这种功能早就不是什么新鲜的东西了,几乎所有的应用里都会有这个功能。不过市面上现有的下拉刷新功能在风格上都各不相同,并且和 Material Design 还有些格格不人的感党。因此,Google为了让 Android 的下拉刷新风格能有一个统一的标准,于是在 Material Design 中制定了一个官方的设计规范。当然,我们并不需要去深入了解这个规范到底是什么样的,因为Google早就提供好了现成的控件,我们只需要在项目中直接使用就可以了。
SwipeRefreshLayout 就是用于实现下拉刷新功能的核心类,它是由 AndroidX库提供的。我们把想要实现下拉刷新功能的控件放置到 SwipeRefreshLayout 中,就可以迅速让这个控件支持下拉刷新。那么在 MaterialTest 项目中,应该支持下拉刷新功能的控件自然就是 RecyclerView 了。
由于 SwipeRefreshLayout 的用法也比较简单,下面我们就直接开始使用了。修改activity_main.xml 中的代码,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
...
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
...
</androidx.coordinatorlayout.widget.CoordinatorLayout>
...
</androidx.drawerlayout.widget.DrawerLayout>
可以看到,这里我们在 RecyclerView 的外面又嵌套了一层 SwipeRefreshLayout,这样 RecyclerView 就自动拥有下拉刷新功能了。另外需要注意,由于 RecyclerView 现在变成了 SwipeRefreshLayout 的子控件,因此之前使用 app:layout_behavior 声明的布局行为现在也要移到 SwipeRefreshLayout 中才行。
不过这还没有结束,虽然 RecyclerView 已经支持下拉刷新功能了,但是我们还要在代码中处理具体的刷新逻辑才行。修改 MainActivity 中的代码,如下所示:
class MainActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
swipeRefresh.setColorSchemeResources(R.color.colorPrimary)
swipeRefresh.setOnRefreshListener {
refreshFruits(adapter)
}
}
private fun refreshFruits(adapter: FruitAdapter) {
thread {
Thread.sleep(2000)
runOnUiThread{
initFruits()
adapter.notifyDataSetChanged()
swipeRefresh.isRefreshing = false
}
}
}
private fun initFruits(){
fruitList.clear()
repeat(50){
val index = (0 until fruits.size).random()
fruitList.add(fruits[index])
}
}
...
}
这段代码应该还是比较好理解的,首先调用SwipeRefreshLayout 的 setColorSchemeResources() 方法来设置下拉刷新进度条的颜色,这里我们就使用主题中的 colorPrimary 作为进度条的颜色了。接着调用 setOnRefreshListener() 方法来设置一个下拉刷新的监听器,当触发了下拉刷新操作的时候就会回调这个监听器的 onRefresh() 方法,然后我们在这里去处理具体的刷新逻辑就可以了。
通常情况下,onRefresh() 方法中应该是去网络上请求最新的数据,然后再将这些数据展示出来。这里简单起见,我们就不和网络进行交互了,而是调用一个 refreshFruits() 方法进行本地刷新操作。refreshFruits() 方法中先是开启了一个线程,然后将线程沉睡两秒钟。之所以这么做,是因为本地刷新操作速度非常快,如果不将线程沉睡的话,刷新立刻就结束了,从而看不到刷新的过程。沉睡结東之后,这里使用了 runOnUiThread()方法将线程切换回主线程,然后调用 initFruits() 方法重新生成数据,接着再调用 FruitAdapter 的 notifyDataSetChanged() 方法通知数据发生了变化,最后调用 SwipeRefreshLayout 的 setRefreshing() 方法并传人 false,用于表示刷新事件结束,并隐藏刷新进度条。
现在可以重新运行一下程序了,在屏幕的主界面向下拖动,会有一个下拉刷新的进度条出现松手后就会自动进行刷新了,效果如图所示。
下拉刷新的进度条只会停留两秒钟,之后就会自动消失,界面上的水果数据也会随之更新。
这样我们就把下拉刷新的功能也成功实现了,并且这就是 Material Design 中规定的最标准的下拉刷新效果,还有什么会比这个更好看呢?目前我们的项目中已经应用了众多 Material Design 的效果,Material 库中的常用控件也学了大半了。不过本章的学习之旅还没有结束,在最后的尾声部分,我们再来实现一个非常震撼的 Material Design 效果—可折叠式标题栏。