Reputation: 417
I have a recycler view adapter in android. Part of my adapter class looks like this:
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
}
I created an interface tat looks like this:
interface ItemLongClick {
// Function Declaration For When An Item Is Long Clicked//
fun longClicked(context: Context, position: Int, view: View)
}
Instead of writing my on long click code in the adapter class I want to differ it to the activity that is calling the adapter class. I know one way of doing this is to make a kotlin interface then call it in the other class like this
userAdapter.setItemLongClick(object: ItemLongClick {
override fun longClicked(context: Context, position: Int, view: View) {
}
})
But this looks messy. I know java interfaces work with SAM but I don't want to do that either. What I want is for the onLongClick to be a Lambda but I'm not sure how to set up a Kotlin lambda expression to make this work and I can't find a good example anywhere.
Thanks in advance
Upvotes: 5
Views: 2980
Reputation: 16359
As the Kotlin documentation for the Kotlin 1.4 release points out:
Before Kotlin 1.4.0, you could apply SAM (Single Abstract Method) conversions only when working with Java methods and Java interfaces from Kotlin. From now on, you can use SAM conversions for Kotlin interfaces as well. To do so, mark a Kotlin interface explicitly as functional with the fun modifier.
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))
}
You can read more about functional interfaces here.
Upvotes: 2
Reputation: 2813
In below code I using filterable adapter to do search on list. Here I am using lambda as callback to notify to view model when no data is found for the search.
Instantiating Adapter in ViewModel. And passing lambda
var matterAdapter = MatterAdapter(matterList) {
//todo - got callback
}
Adapter
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()
}
}
Upvotes: 0
Reputation: 544
I had an adapter that i need to change the data based on a switch and i did something like this:
ListAdapter(private val context: Context, private val switchListener: (Boolean) -> Unit)
Then where i binded the header of my sectioned list i had:
private fun bindHeader(holder: HeaderViewHolder) {
holder.switch.setOnCheckedChangeListener { _, isChecked ->
callbackSwitchListener(isChecked)
}
}
And in my fragment:
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
}
Where the fun switchControl changed the data based on the boolean.
I'm not sure if this is what you need, i'm in a bit of a hurry, but this is called high order functions in kotlin, if i'm not mistaken.
Upvotes: 2
Reputation: 81588
You have two options:
1.) replace interface with typealias
typealias ItemLongClick = (Context, Int, View) -> Unit
2.) add an extension function for setting the interface as a lambda instead of with anonymous object
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)
}
})
}
Now you can call
userAdapter.setItemLongClick { context, position, view ->
...
}
Upvotes: 8