Kotlin — Part 7:无限滑动:高阶函数&Lambdas

感谢之前的所有文章我们有一个简单的 Reddit 客户端,显示从 Reddit 获取的 Top 新闻.现在我们需要用户不只看到前10个新闻.我们将为这个 App 添加无限滑动加载更多数据.

全部章节:

Kotlin — Part 0:关于这个系列
Kotlin — Part 1:配置 Android Studio
Kotlin — Part 2:语法,空安全,静态类型
Kotlin — Part 3:扩展函数、Android 扩展、委托属性
Kotlin — Part 4:RecyclerView— Kotlin 适配器委托&数据类
Kotlin — Part 5:Kotlin,RxJava&RxAndroid
Kotlin — Part 6:API-Retrofit&Kotlin)
Kotlin — Part 7:无限滑动:高阶函数& Lambdas
Kotlin — Part 8:方向改变(序列化&数据类)
Kotlin — Part 9:单元测试与 Kotlin(Mockito,RxJava)

Github 仓库:https://github.com/imuhao/KedditBySteps

无限滑动的实现是受Mohamed Sobhy的启发,但是不幸的是它将不再可用.我做了几个改变提供高阶函数的例子,通过一个 lambda 表达式参数

高阶函数

一个高阶函数是一个函数接受一个函数作为参数或返回一个函数.

让我们查看Kotlin 如何运行一个函数接受一个函数作为参数或返回.

这个函数”logExecution“允许你将一个函数作为参数,然后再在这个函数的之前和之后执行这个函数.

1
2
3
4
5
fun logExecution(func: () -> Unit) {
Log.d("tag", "before executing func")
func()
Log.d("tag", "after executing func")
}

func:()->Unit

你已经知道,” func“是这个参数的名字 “()->Unit“是它的类型,在这种情况func将是一个函数,它不接受任何参数,没有返回任值.

你可以通过 lambda 表达式调用这个函数,它必须没有接受和返回任何值.像下面的方法:

1
logExecution( { Log.d("tag", "I'm a function") } )

但是 Kotlin 运行你在只有一个参数时删除括号或者如果最后这个参数是函数:

1
logExecution { Log.d("tag", "I'm a function") }

如果我们改变 logExecution 函数接受另一个参数,我们将函数参数放在最后,我们可以这样做

1
2
3
4
// 添加一个 tag 参数
fun logExecution(tag: String, func: () -> Unit) { ... }
// 调用这个方法
logExecution("tag") { Log.d("tag", "I'm a function") }

或者:

1
2
3
logExecution("tag"){
Log.d("tag","I'm a function")
}

你也可以使这个函数接受和返回一个值

1
2
3
fun logExecution(func: (String, String) -> Int) {
val thisIsAnInt = func("Hello", "World")
}

你可以看到 Kotlin 的高阶函数给你更大的功能.这将允许你在函数执行之前执行一些初始化,或关闭游标等操作.它可以做的更多.

Async 函数例子

让我们查看一个实用的例子.我们将创建一个新的函数,它将接受一个函数,执行它到其它线程.

1
2
3
fun runAsync(func:()->Unit){
Thread(Runnable{func()}.start()
}

现在我们可以在主线程中执行这个函数:

1
2
3
runAsync {
// i.e.: 做一些长时间操作在其他线程
}

在 Lollipop 例子

可能你需要运行一些代码在 Lollipop 设备,而不像做一些常规的检查,你可以使用这个函数:

1
2
3
4
5
fun isLollipopOrAbove(func: () -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
func()
}
}

使用这个方法:

1
2
3
isLollipopOrAbove {
// run lollipop specific code safely
}

无限滑动

现在让我们看看如何利用这一新概念,创建我们自己的无线滑动行为;

RecyclerView 允许你设置一个 ScrollListener, 所以我创建了InfiniteScrollListener.

1
2
3
4
5
class InfiniteScrollListener(func: () -> Unit,
var layoutManager: LinearLayoutManager
) : RecyclerView.OnScrollListener() {

}

在这里我们做的是接受一个函数作为参数,每当我们的 RecyclerView 滑动到底部时.事实上,它将在滑动到底部之前调用,你可以设置一个阈值,使我们的函数在底部 item 多少之前调用.这是一个很好的用户体验,避免用户查看到我们请求数据时所有的加载时间.

我们作为参数传递过来的函数用来加载更多的新闻和更新 NewsAdapter.

1
2
3
news_list.addOnScrollListener(
InfiniteScrollListener({ requestNews() }, linearLayout)
)

requestNews()是我们之前的代码,我更新它使用分页,请求下一个从 Reddit 获取的有效数据.

Commit

在这里你可以看到无限滑动的具体实现:
(https://github.com/imuhao/KedditBySteps/commit/b2d42a59a1631b85e1ee6f724f30a1e607fab09b)[https://github.com/imuhao/KedditBySteps/commit/b2d42a59a1631b85e1ee6f724f30a1e607fab09b]

总结

我们现在已经可以滑动从 Reddit 加载更多数据,所有的这些实现你可以送前面的 commit 中查看.

高阶函数是一个不可思议的特征,希望通过这个例子你可以更加清晰.