如何使用 lambda 而不是 kotlin 接口
Posted
技术标签:
【中文标题】如何使用 lambda 而不是 kotlin 接口【英文标题】:How to use a lambda instead of a kotlin interface 【发布时间】:2019-06-21 19:43:27 【问题描述】:我在 android 中有一个回收器视图适配器。我的适配器类的一部分如下所示:
private lateinit var itemLongClick: ItemLongClick
override fun onCreateViewHolder(parent: ViewGroup, a: Int): RecyclerAdapter.ViewHolder
// Define And Initialize The Custom View And Its Holder//
val myView = LayoutInflater.from(parent.context).inflate(customLayout, parent, false)
val viewHolder = ViewHolder(myView)
// What Happens When A List Item Is Long Clicked//
myView.setOnLongClickListener view ->
// Differ Action To Class Instance//
itemLongClick.longClicked(context, viewHolder.layoutPosition, view)
// End Function//
true
// Returns The Custom View//
return viewHolder
fun setItemLongClick(itemLongClick: ItemLongClick)
// Sets The Value For this.itemLongClick//
this.itemLongClick = itemLongClick
我创建了一个界面,看起来像这样:
interface ItemLongClick
// Function Declaration For When An Item Is Long Clicked//
fun longClicked(context: Context, position: Int, view: View)
我不想在适配器类中编写我的长按代码,而是想将它与调用适配器类的活动区分开来。我知道这样做的一种方法是制作一个 kotlin 接口,然后像这样在另一个类中调用它
userAdapter.setItemLongClick(object: ItemLongClick
override fun longClicked(context: Context, position: Int, view: View)
)
但这看起来很乱。我知道 java 接口可以与 SAM 一起使用,但我也不想这样做。我想要的是将 onLongClick 设为 Lambda,但我不确定如何设置 Kotlin lambda 表达式以使其正常工作,而且我在任何地方都找不到好的示例。
提前致谢
【问题讨论】:
【参考方案1】:你有两个选择:
1.) 用 typealias 替换接口
typealias ItemLongClick = (Context, Int, View) -> Unit
2.) 添加一个扩展函数,用于将接口设置为 lambda 而不是匿名对象
inline fun UserAdapter.setItemLongClick(crossinline longClick: (Context, Int, View) -> Unit)
setItemLongClick(object: ItemLongClick
override fun longClicked(context: Context, position: Int, view: View)
longClick(context, position, view)
)
现在你可以打电话了
userAdapter.setItemLongClick context, position, view ->
...
【讨论】:
【参考方案2】:我有一个适配器,我需要根据开关更改数据,我做了这样的事情:
ListAdapter(private val context: Context, private val switchListener: (Boolean) -> Unit)
然后我绑定了我的分段列表的标题:
private fun bindHeader(holder: HeaderViewHolder)
holder.switch.setOnCheckedChangeListener _, isChecked ->
callbackSwitchListener(isChecked)
在我的片段中:
private fun setupRecyclerView()
fabricationDataListAdapter =
FabricationDataListAdapter(context!!) isChecked: Boolean -> switchControl(isChecked)
val layoutManager = ListLayoutManager(context!!)
this.recycler_view_all.layoutManager = layoutManager
this.recycler_view_all.adapter = fabricationDataListAdapter
有趣的 switchControl 根据布尔值更改数据的地方。
我不确定这是否是你需要的,我有点着急,但如果我没记错的话,这在 kotlin 中称为高阶函数。
【讨论】:
【参考方案3】:正如 Kotlin 1.4 版本的 Kotlin documentation 指出的那样:
在 Kotlin 1.4.0 之前,您只能在使用来自 Kotlin 的 Java 方法和 Java 接口时应用 SAM(单一抽象方法)转换。从现在开始,您也可以对 Kotlin 接口使用 SAM 转换。为此,请使用 fun 修饰符将 Kotlin 接口显式标记为函数式。
fun interface Operation1
operator fun invoke(x: String): String
fun interface Operation2
fun doSomething(x: Int): String
val operation1 = Operation1 "$it world!"
val operation2 = Operation2 "$it world!"
fun main()
// Usage: First sample.
println(operation1("Hello"))
println(operation2.doSomething(0))
// Usage: Second sample.
println(Operation1 "$it world!" ("Hello"))
println(Operation2 "$it!" .doSomething(0))
您可以阅读更多关于功能接口的信息here。
【讨论】:
【参考方案4】:在下面的代码中,我使用可过滤适配器在列表中进行搜索。在这里,我使用 lambda 作为回调,以在没有找到搜索数据时通知视图模型。
在 ViewModel 中实例化适配器。并通过 lambda
var matterAdapter = MatterAdapter(matterList)
//todo - got callback
适配器
class MatterAdapter (var filteredList : MutableList<AndroidViewModel>, val funcNoSearchData : () -> Unit) : DataBindingRecyclerViewAdapter(filteredList), Filterable
private var mViewModelMap: MutableMap<Class<*>, Int> = mutableMapOf()
private var originalList : MutableList<AndroidViewModel> = mutableListOf()
private val mFilter = ItemFilter()
init
mViewModelMap.put(MatterRowViewModel::class.java, R.layout.row_matter)
override fun getViewModelLayoutMap(): MutableMap<Class<*>, Int>
return mViewModelMap
override fun getFilter(): Filter
return mFilter
private inner class ItemFilter : Filter()
override fun performFiltering(constraint: CharSequence): FilterResults
val filterString = constraint.toString().toLowerCase()
val results = FilterResults()
val list = originalList
val count = list.size
val nlist = ArrayList<AndroidViewModel>(count)
var filterableString: String
for (i in 0 until count)
filterableString = (list.get(i) as MatterRowViewModel).matter.casestitle!!
if (filterableString.toLowerCase().contains(filterString))
nlist.add(list.get(i))
results.values = nlist
results.count = nlist.size
return results
override fun publishResults(constraint: CharSequence, results: Filter.FilterResults)
filteredList.clear()
filteredList.addAll(results.values as ArrayList<AndroidViewModel>)
// sends empty search callback to viewmodel
if(filteredList.size == 0)
funcNoSearchData()
notifyDataSetChanged()
fun resetSearch()
filteredList.clear()
filteredList.addAll(originalList)
notifyDataSetChanged()
fun refreshData()
originalList = ArrayList(filteredList)
notifyDataSetChanged()
【讨论】:
以上是关于如何使用 lambda 而不是 kotlin 接口的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Kotlin 中将函数接收器类型与 SAM 接口一起使用