Android 开发中高阶函数的 10 个实例
Kotlin 是一种现代编程语言,由于其表现力、简洁性和多功能性而变得越来越流行。它的关键特性之一是支持高阶函数,这使您可以编写更简洁、更灵活的代码。高阶函数是一种将一个或多个函数作为参数或返回一个函数作为结果的函数。在 Kotlin 中,我们可以使用 lambda 表达式、匿名函数或命名函数来定义高阶函数。
以下是 Android 开发人员如何在他们的项目中使用高阶函数:
事件监听器
事件监听器是 Android 开发中高阶函数的常见用例。例如,考虑以下将高阶函数作为参数来处理按钮单击事件的函数:
fun View.onClick(action: () -> Unit) {
setOnClickListener { action() }
}
此函数扩展View类并采用将在单击视图时执行的 lambda 表达式。该setOnClickListener
方法用于为视图设置点击监听器,当点击事件发生时执行lambda表达式。
使用此功能,我们可以轻松地以简洁易读的方式处理按钮单击事件:
button.onClick {
Toast.makeText(context, "Button clicked", Toast.LENGTH_SHORT).show()
}
在这里,我们传递了一个 lambda 表达式,该表达式在单击按钮时显示 toast 消息。该onClick
函数负责为按钮设置点击监听器,并在点击事件发生时执行 lambda 表达式。
视图动画
视图动画是 Android 开发中的一项常见任务,可以通过使用高阶函数使其更具可重用性和灵活性。View以下是使用高阶函数为不同属性设置动画的类的扩展函数示例:
fun View.animateProperty(
property: KProperty0<Float>,
fromValue: Float,
toValue: Float,
duration: Long,
onComplete: () -> Unit = {}
) {
val animator = ObjectAnimator.ofFloat(this, property.name, fromValue, toValue).apply {
setDuration(duration)
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
onComplete()
}
})
}
animator.start()
}
此函数在指定的持续时间内将视图的单个属性(例如其 X 或 Y 坐标)从起始值动画化到结束值。它还需要一个可选的回调函数,以便在动画完成时执行。
我们可以像这样在我们的代码中使用这个函数:
view.animateProperty(
View.TRANSLATION_X,
fromValue = 0f,
toValue = 100f,
duration = 500,
onComplete = { onAnimationComplete() }
)
此代码为对象的 X 平移属性设置动画View,从其当前位置 (0) 到新位置 (100),时间超过 500 毫秒。当动画完成时,onAnimationComplete()
将调用该函数。
RecyclerView
RecyclerView
是 Android 应用程序中常用的 UI 组件,用于显示项目列表或网格。它可以使用各种布局管理器和适配器进行定制,以适应不同的用例。在高阶函数的上下文中,RecyclerView
可以与高阶函数结合使用,以在代码中提供更多的灵活性和模块化。
将 RecyclerView
用作高阶函数的一个示例是创建一个接收项目列表的函数和一个将每个项目绑定到视图持有者的函数,并返回配置的 RecyclerView
适配器。这是一个示例实现:
fun <T> RecyclerView.bindData(
data: List<T>,
layoutRes: Int,
bindFunc: (View, T) -> Unit,
clickListener: ((T) -> Unit)? = null
) {
adapter = object : RecyclerView.Adapter<ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(layoutRes, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = data[position]
bindFunc(holder.itemView, item)
clickListener?.let { listener ->
holder.itemView.setOnClickListener { listener(item) }
}
}
override fun getItemCount() = data.size
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
}
}
在这个函数中,data
是RecyclerView中要显示的项目列表,layoutRes
是每个项目视图的布局资源ID,bindFunc
是将每个项目绑定到其对应的视图持有者的函数,clickListener
是处理项目点击事件的可选函数。该函数返回一个 RecyclerView 适配器,该适配器配置为使用提供的布局和绑定函数显示项目。
使用此函数,我们可以创建一个 RecyclerView 并使用单个函数调用用数据填充它,如下所示:
recyclerView.bindData(
data = listOf("item1", "item2", "item3"),
layoutRes = R.layout.list_item,
bindFunc = { view, item -> view.findViewById<TextView>(R.id.text_view).text = item },
clickListener = { item -> onItemClick(item) }
)
此代码创建一个 RecyclerView,将其数据设置为字符串列表,并使用绑定函数将每个项目视图的文本设置为相应的字符串。它还设置了一个单击侦听器,该侦听器onItemClick
使用单击的项目调用该函数。
线程
线程是许多编程语言中高阶函数的常见用例。可以使用高阶函数来简化在单独线程上运行代码的过程,从而更容易处理异步操作。
例如,在 Kotlin 中,我们可以定义一个以 lambda 为参数的函数,并在后台线程中运行它。此功能可用于避免阻塞 UI 线程并提供更流畅的用户体验。这是一个例子:
fun <T> runOnBackgroundThread(backgroundFunc: () -> T, callback: (T) -> Unit) {
val handler = Handler(Looper.getMainLooper())
Thread {
val result = backgroundFunc()
handler.post { callback(result) }
}.start()
}
此函数将两个函数作为参数:backgroundFunc
,它在后台线程上执行昂贵的计算,以及callback
,它在主线程上调用计算结果。
该函数在单独的线程上完成其工作后,runOnBackgroundThread
使用 aHandler
将callback函数backgroundFunc
发布到主线程。
使用这个高阶函数,我们可以在后台线程上运行昂贵的计算,并在主线程上用结果更新 UI,而不必担心线程的细节。
例如,我们可以runOnBackgroundThread
这样调用函数:
runOnBackgroundThread(
{ doExpensiveCalculation() },
{ onResultLoaded(it) }
)
此代码传入执行耗时计算的 lambda 表达式和处理计算结果的backgroundFunc
lambda 表达式。
权限处理
权限处理是移动应用程序开发中的一项常见任务,它通常涉及用于检查和请求权限的重复代码。简化此代码的一种方法是使用高阶函数。
例如,我们可以定义一个函数withPermissions
,该函数接受请求的权限列表和一个在授予权限时执行的回调函数。此函数将处理权限检查和请求,并仅在授予权限时调用回调函数。
这是 Kotlin 中的示例实现:
fun Activity.withPermissions(vararg permissions: String, callback: () -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val ungrantedPermissions = permissions.filter {
checkSelfPermission(it) == PackageManager.PERMISSION_DENIED
}
if (ungrantedPermissions.isEmpty()) {
// All permissions are granted, execute callback
callback()
} else {
// Request permissions
requestPermissions(ungrantedPermissions.toTypedArray(), 0)
}
} else {
// Pre-Marshmallow devices, execute callback
callback()
}
}
这个函数可以这样使用:
withPermissions(
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) {
// Code to execute when permissions are granted
}
在这个例子中,我们定义了一个函数withPermissions
,它接受可变数量的权限和一个回调函数作为参数。在函数内部,我们检查设备是否正在运行 Marshmallow 或更高版本,如果是,我们过滤权限列表以仅包含尚未授予的权限。如果已经授予所有权限,我们立即执行回调函数。否则,我们使用该方法请求未授予的权限requestPermissions
。最后,如果设备运行的是 Marshmallow 之前的 Android 版本,我们会立即执行回调函数而不检查权限。
输入验证
输入验证可以作为高阶函数实现,它将验证函数作为参数并返回可用于验证输入的函数。这是一个示例实现:
fun String.validate(validationFunc: (String) -> Boolean): Boolean {
return validationFunc(this)
}
validate
在这个例子中,我们定义了一个在类上调用的扩展函数String。该validate函数采用单个参数,这是一个验证函数,它采用输入String并返回Boolean指示输入是否有效的指示。该validate函数将验证函数应用于String调用它的对象并返回结果。
下面是一个使用示例:
val input = "example input"
val isInputValid = input.validate { input -> input.isNotEmpty() }
在这个例子中,我们创建了一个String名为 called 的对象input,然后调用validate它的函数,传递一个 lambda 来检查字符串是否不为空。如果输入有效则函数返回,否则validate返回。
委托
委托是面向对象编程中的一种技术,其中一个对象将其某些职责传递给另一个对象。在 Kotlin 中,委托是使用by关键字实现的。
作为委托作为高阶函数的示例,我们可以考虑一个lazy函数,该函数返回延迟初始化的属性的委托。该lazy函数以一个 lambda 函数作为参数,并返回一个委托,该委托将在第一次访问时调用 lambda 函数来惰性初始化该属性。
这是一个例子:
fun <T> lazyDelegate(initializer: () -> T) = lazy(initializer)::getValue
class Example {
val lazyProperty: String by lazyDelegate {
println("Initializing lazy property")
"Hello, World!"
}
}
fun main() {
val example = Example()
println(example.lazyProperty)
println(example.lazyProperty)
}
在此示例中,lazyDelegate
函数采用返回属性初始值的 lambda 函数。lazy它使用函数创建委托lazy并返回getValue
委托的函数。该getValue
函数将在第一次访问属性时调用 lambda 函数来初始化属性并返回值。
该类Example定义了一个lazyProperty
使用函数延迟初始化的属性lazyDelegate
。传递给函数的 lambda 函数向lazyDelegate
控制台打印一条消息并返回字符串“Hello, World!”。
在该main函数中,我们创建了该类的一个实例Example并访问了该lazyProperty
属性两次。我们第一次访问它时,会调用 lambda 函数来初始化属性,并在控制台打印消息“Initializing lazy property”。第二次访问时,属性已经初始化,所以不会再调用lambda函数,取值为“Hello, World!” 立即返回。
日志记录
日志记录是软件开发中用于记录有关应用程序行为或状态的信息的常用技术。它可用于调试问题、监控性能或跟踪用户行为。在许多情况下,日志记录可以作为高阶函数来实现。
下面是一个在 Kotlin 中作为高阶函数记录日志的例子:
fun <T> log(tag: String, message: String, function: () -> T): T {
Log.d(tag, message)
val result = function()
Log.d(tag, "Function result: $result")
return result
}
在这个例子中,log是一个高阶函数,它接受一个标签、一条消息和一个函数作为参数。该函数被执行,其返回值与消息和标签一起被记录下来。
这是此函数的示例用法:
val result = log("myTag", "Calculating result...") {
// Perform some expensive calculation
42
}
在此示例中,该log
函数用于在执行耗时的计算之前和之后记录一条消息。然后返回计算结果并将其存储在result
变量中。
Room Database
Room Database 是一个流行的库,用于在 Android 中使用 SQLite 数据库。Room 提供了一组注释和类,可帮助您定义数据库并与之交互。您可以使用高阶函数来简化使用 Room 的过程。
下面是一个高阶函数的例子,它接受一个 Room DAO 对象和一个执行数据库查询的 lambda 函数:
fun <T> runInTransaction(dao: MyDao, action: () -> T): T {
return dao.runInTransaction {
action()
}
}
在此示例中,该runInTransaction
函数采用一个MyDao对象和一个执行数据库操作的 lambda 函数。该runInTransaction
函数调用对象MyDao上的方法runInTransaction
,该方法在数据库事务中执行 lambda 函数。lambda 函数可以返回一个值,该值由runInTransaction
函数返回。
以下是如何使用该runInTransaction
函数执行数据库查询的示例:
val dao = MyDatabase.getInstance(context).myDao()
val result = runInTransaction(dao) {
dao.getSomeData()
}
在这个示例中,使用一个 MyDao
对象和一个调用 MyDao
对象上的 getSomeData
方法的 lambda 函数调用 runInTransaction
函数。runInTransaction
函数会为您处理数据库事务和错误处理,并返回 getSomeData
方法的结果。
Toast 处理
Toast 是在 Android 应用程序中向用户显示简短通知或反馈的常用方式。以下是如何使用高阶函数处理 toasts 的示例:
fun Context.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}
fun Fragment.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) {
activity?.showToast(message, duration)
}
showToast
在这个例子中,我们定义了一个为Context
和调用的扩展函数Fragment
。该函数接受一个message
字符串和一个duration
整数,后者指定 toast
应显示多长时间。
该showToast
函数Toast使用该makeText
方法创建一个新的,然后使用该show
方法显示它。该makeText
方法接受字符串message
和整数duration
,toast 应显示的位置Context
或Activity
。
使用这个高阶函数,我们可以轻松地在应用程序的任何位置显示Toast,如下所示:
showToast( "Hello, world!" )
我们还可以自定义吐司的持续时间:
showToast( "Hello, world!" , Toast.LENGTH_LONG)
结论
总而言之,高阶函数是函数式编程中的一个重要概念,它允许函数接受其他函数作为参数或将它们作为值返回。它们提供了一种灵活且可重用的方式来执行常见任务,例如线程、输入验证、日志记录、依赖项注入等。
在上面提供的示例中,我们已经看到如何使用高阶函数来简化代码并使其更具可读性和可维护性。它们允许关注点分离,并提供了一种跨应用程序不同部分重用代码的方法。无论是处理权限、输入验证、数据库操作还是导航,高阶函数都提供了一个强大的工具来简化复杂的任务。
虽然高阶函数可能需要一些时间才能掌握,但一旦理解,它们可以极大地提高代码的质量和可维护性。因此,它们是添加到任何开发人员工具包中的宝贵技术。
参考
https://medium.com/@summitkumar/10-practical-examples-of-higher-order-functions-in-android-development-84e9c6a4bdc3