I have RecyclerView adapter in Kotlin and when a user clicks on categoryPhoto, I want to open a new activity. How should I implement this?
class CategoryAdapter(private val categoryList: List<Category>, private val context: Context) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var categoryName = view.text_view_category_name
var categoryPhoto = view.image_view_category
var cardView = view.card_view_category
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MyViewHolder(parent.inflate(R.layout.category_list_row))
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
val displayMetrics = context.resources.displayMetrics
val finalHeight = displayMetrics.widthPixels / 2
holder.cardView.layoutParams.height = finalHeight
holder.categoryName.text = category.oc
holder.categoryPhoto.loadUrl(category.icon)
}
override fun getItemCount(): Int {
return categoryList.size
}}
Just add click listener as parameter to constructor of your adapter.
class CategoryAdapter(
private val categoryList: List<Category>,
private val context: Context,
private val onClickListener: (View, Category) -> Unit
) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
...
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
...
holder.itemView.setOnClickListener { view ->
onClickListener.invoke(view, category)
}
}
...
}
Then you can use as following:
fun initList() {
adapter = CategoryAdapter(
categoryList = ...,
context = ...,
onClickListener = { view, category -> openActivity(view, category) }
}
Off-top. Some optional improvements for code above
Create typealias for lambda. Make your code more readable.
typealias MyCategoryClickListener = (View, Category) -> Unit
class CategoryAdapter(
private val categoryList: List<Category>,
private val context: Context,
private val onClickListener: MyCategoryClickListener
) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
Omit invoke call of listener. Lambda can be called just like function.
holder.itemView.setOnClickListener { view ->
onClickListener(view, category)
}
Replace lambda with reference when creating adapter
fun initList() {
adapter = CategoryAdapter(
categoryList = ...,
context = ...,
onClickListener = this::openActivity)
}
fun openActivity(view: View, category: Category) {
...
}
You can do it in your onBindViewHolder(...)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
val displayMetrics = context.resources.displayMetrics
val finalHeight = displayMetrics.widthPixels / 2
holder.cardView.layoutParams.height = finalHeight
holder.categoryName.text = category.oc
holder.categoryPhoto.loadUrl(category.icon)
holder.categoryPhoto.setOnClickListener { view ->
// categoryPhoto clicked.
// start your activity here
}
}
Do like this
class RecyclerListAdapter: RecyclerView.Adapter {
var context: Context? = null
var listData: ArrayList? = null
Step 1: Activity ref..................................
var activityref:MainActivity?=null
constructor(context: Context?, listData: ArrayList<ItemDetails>?, activityref: MainActivity?) : super() {
this.context = context
this.listData = listData
this.activityref = activityref
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewsHolder {
val view = LayoutInflater.from(context).inflate(R.layout.row_list, parent, false)
return ViewsHolder(view)
}
override fun getItemCount(): Int {
return listData!!.size
}
override fun onBindViewHolder(holder: ViewsHolder?, position: Int) {
holder?.item=listData?.get(position)
holder!!.first!!.setText(holder.item?.First)
holder.second!!.setText(holder.item?.Second)
holder.third!!.setText(holder.item?.Third)
Step 2 OnClick on item.....................
holder.third!!.setOnClickListener{
activityref?.OnItemClicked(holder.item!!)
}
}
class ViewsHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
var item:ItemDetails?=null
var first: TextView? = null;
var second: TextView? = null;
var third: TextView? = null;
init {
first = itemView?.findViewById(R.id.first)
second = itemView?.findViewById(R.id.second)
third = itemView?.findViewById(R.id.third)
}
}
}
Related
I'm new to Kotlin. I have a MainActivity and a list view inside.
When an item is clicked in the list view, I want to update a textView inside MainActivity.
However because data should be passed from ListView to MainActivity, I did something weird:
MainActivity.kt:
data class Country(val imgRes:Int, val name:String)
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private val data = arrayListOf<Country>()
val imgRes = intArrayOf()
val data1 = arrayOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
for(i in imgRes.indices) {
val country = Country(imgRes[i], data1[i])
data.add(country)
}
val adapter = RecyclerAdapter(data, object:RecyclerAdapter.OnItemClickListener {
override fun onItemClick(v: View, pos: Int) {
binding.textView.text = (v as TextView).text
}
})
binding.recycler1.adapter = adapter
binding.recycler1.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
}
}
ListView.kt:
class RecyclerAdapter(private val dataSet:List<Country>, private val listener:OnItemClickListener) : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
public interface OnItemClickListener {
fun onItemClick(v:View, pos:Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = RowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun getItemCount(): Int {
return dataSet.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
inner class ViewHolder(val binding: RowBinding) : RecyclerView.ViewHolder(binding.root) {
val rowImageView = binding.rowImageView
val rowTextView = binding.rowTextView
init {
binding.root.setOnClickListener {
listener.onItemClick(rowTextView, adapterPosition)
}
}
fun bind(pos: Int) {
with(binding) {
rowImageView.setImageResource(dataSet[pos].imgRes)
rowTextView.text = dataSet[pos].name
}
}
}
}
I declared interface(OnItemClickListener) at ListView.kt and redefine it at MainActivity.kt.
It works fine but is there any better way to do this?
You can use higher order functions instead of interface.
Define this function top of your Adapter:
var onClick: ((text: String) -> Unit)? = null
in ViewHolder:
binding.root.setOnClickListener {
onClick?.invoke(dataSet[position].text)
}
in MainActivity:
adapter.onClick = { name ->
binding.textView.text = name
}
You can also find more information about higher orders in here
<include android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="#layout/yourlayout" />
Try this in your Adapter:
var itemClickListener: ((position: Int, name: String) -> Unit)? = null
Bindviewholder:
itemClickListner.invoke(1,"anyvalue")
in Activity:
adapter.itemClickListener = {
position, name ->
Toast.makeText(requireContext(),"position is $position name is $name ",Toast.LENGTH_SHORT).show()
}
in Adapter:
private var mListener: OnItemClickListener,
interface OnItemClickListener {
fun onClick(position: Int, routerArray: ArrayList<Router>)
}
binding.yourView.setOnClickListener {
mListener.onClick(adapterPosition, routerArray)
}
in Activity:
override fun onClick(position: Int, yourArray: ArrayList<Model>) {
binding.textView.text = yourArray[position].text
}
I have a recyclerview in my project, where a user will be able to drag and drop it to a different view(card view).
I am following mvvm pattern and using dataBinding. I use the longclick function from within the activity,i created an interface in adapter class for this.
inorder to use the drag and drop i need to get the adapter position of the item in activity, which i am unable to get.
here is the adapter class
class StatementAdapter(
private val context: Context,
private val statementList: ArrayList<Statement>
) :
RecyclerView.Adapter<StatementAdapter.StatementViewHolder>() {
private var mListener: OnItemLongClickListener? = null
interface OnItemLongClickListener {
fun onItemLongClick(statement: Statement)
}
fun setOnItemLongClickListener(listener: OnItemLongClickListener) {
mListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatementViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val statementBinding: StatementBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.statement_list, parent, false)
return StatementViewHolder(statementBinding, mListener)
}
override fun getItemCount(): Int {
return statementList.size
}
override fun onBindViewHolder(holder: StatementViewHolder, position: Int) {
val statementViewModel = statementList[position]
holder.bind(statementViewModel)
}
class StatementViewHolder(
private val statementBinding: StatementBinding,
private val listener: OnItemLongClickListener?
) : RecyclerView.ViewHolder(statementBinding.root) {
fun bind(statementViewModel: Statement) {
this.statementBinding.statementModel = statementViewModel
itemView.setOnLongClickListener {
listener?.onItemLongClick(
statementViewModel
)
true
}
statementBinding.executePendingBindings()
}
}
}
Here is the activity
class TrueOrFalseActivity : AppCompatActivity(), StatementAdapter.OnItemLongClickListener {
private lateinit var trueOrFalseBinding: ActivityTrueOrFalseBinding
private var rvStatement: RecyclerView? = null
private var statementAdapter: StatementAdapter? = null
private val dragMessage = "Added"
private val inBucket = "in bucket"
private val offBucket = "not in bucket"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
trueOrFalseBinding = ActivityTrueOrFalseBinding.inflate(layoutInflater)
setContentView(trueOrFalseBinding.root)
rvStatement = findViewById(R.id.rvStatement)
val statementViewModel = ViewModelProvider(this).get(StatementViewModel::class.java)
statementViewModel.generateStatement()
statementViewModel.newMStatementList.observe(this) {
statementAdapter = StatementAdapter(this#TrueOrFalseActivity, it)
rvStatement!!.layoutManager = LinearLayoutManager(this#TrueOrFalseActivity)
rvStatement!!.adapter = statementAdapter
statementAdapter?.setOnItemLongClickListener(this)
}
}
override fun onItemLongClick(statement: Statement) {
val item = ClipData.Item(dragMessage)
val dragData = ClipData(
dragMessage,
arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN),
item
)
val myShadow = MyDragShadowBuilder(this)
// Here where i need to get the item position
}
}
class MyDragShadowBuilder(trueOrFalseActivity: TrueOrFalseActivity) {
}
I am following the following documentation
https://developer.android.com/guide/topics/ui/drag-drop.html#AboutDragging
in this, the "view" is the parameter, but in my case it is arecyclerview item.
how do i solve this?
Could someone help me with this error?
class PokemonAdapter(
private val context: Context,
private val pokemons: MutableList<PokemonItem> = mutableListOf(),
var onItemClickListener: (pokemon: PokemonItem) -> Unit = {}
) : RecyclerView.Adapter<PokemonAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PokemonAdapter.ViewHolder {
val viewCriada = LayoutInflater.from(context).inflate(
R.layout.item_pokemon,
parent,
false
)
return ViewHolder(viewCriada)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.vincula(pokemons[position])
}
override fun getItemCount() = pokemons.size
fun add(pokemons: List<PokemonItem>) {
this.pokemons.clear()
this.pokemons.addAll(pokemons)
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
private lateinit var pokemon: PokemonItem
private val campoNome by lazy { itemView.item_pokemon_nome }
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemon
campoNome.text = pokemonItem.nome
}
}
}
Here it appears you're setting pokemon to itself but it has not actually been set yet:
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemon // < -- This line
campoNome.text = pokemonItem.nome
}
Perhaps you meant this:
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemonItem
campoNome.text = pokemonItem.nome
}
the problem is in the function vincula
this.pokemon = pokemonItem
I am new to android. I am working on SQL CRUD operations. The app runs without problems but in my adapter, I just replace the bindItems method with another way of initializing Ui elements in order to use variables in the Update & Delete operation.
Unfortunately, the app stops and give me FATAL EXCEPTION: Main error.
I don't know how to fix it, please help me.
This is the Adapter with bindItems method (The app works fine)
class AdapterRecord (): RecyclerView.Adapter<AdapterRecord.MyViewHolder>() {
private var context: Context? = null
private var notesList: List<Note>? = null
constructor(context: Context?, notesList: List<Note>?) : this() {
this.context = context
this.notesList = notesList
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(note: Note) {
val tvTitle = itemView.findViewById(R.id.tvTitle) as TextView
val tvDescription = itemView.findViewById(R.id.tvDescription) as TextView
tvTitle.text = note.name
tvDescription.text = note.description
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.note_ticket, parent,false)
return MyViewHolder(itemView)
}
override fun getItemCount() : Int {
return notesList!!.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bindItems(notesList!!.get(position))
}
}
And this is the adapter without bindItems (The app stopped working with exception)
class AdapterRecord (): RecyclerView.Adapter<AdapterRecord.MyViewHolder>() {
private var context: Context? = null
private var notesList: List<Note>? = null
constructor(context: Context?, notesList: List<Note>?) : this() {
this.context = context
this.notesList = notesList
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvTitle = itemView.findViewById<TextView>(R.id.tvTitle)
val tvDescription = itemView.findViewById<TextView>(R.id.tvDescription)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.note_ticket, parent,false)
return MyViewHolder(itemView)
}
override fun getItemCount() : Int {
return notesList!!.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val note = notesList!!.get(position)
//get data
val id = note.id
val name = note.name
val description = note.description
//set data
holder.tvTitle.text = name
holder.tvDescription.text = description
}
}
I tried to resolve it and I succeeded.
I create a new class to declare Ui elements but before declaring there is set on content method to the layout which add notes to database then the app works fine.
This is the class :
class UiElements : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note)
val tvTitle = findViewById<TextView>(R.id.tvTitle)
val tvDescription = findViewById<TextView>(R.id.tvDescription)
val moreBtn = findViewById<ImageButton>(R.id.moreBtn)
}
}
I have an adapter in which the items each have 3 buttons, that generate a dialog that then performs an action. I have a sense that this should be removed from the adapter (I have view models available), but it works and I am wondering: Should I move logic to the fragment, to the view model, do I need to move it at all (is the code below bad practice and if so why)? Any help/input would be greatly appreciated.
Here is the adapter code:
class ViewRecipesAdapter(val context: Context, private val recipes: List<Recipe>, private val parentFragment: Fragment) :
RecyclerView.Adapter<ViewRecipesAdapter.RecipeViewHolder>()
{
private var listToUse: List<Recipe> = recipes
private lateinit var recipesViewModel: RecipesViewModel
private var isView = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecipeViewHolder
{
val layoutInflater = LayoutInflater.from(parent.context)
val binding: ViewRecipesItemBinding =
DataBindingUtil.inflate(layoutInflater, R.layout.view_recipes_item, parent, false)
return RecipeViewHolder(binding, context)
}
override fun getItemCount() = listToUse.size
override fun onBindViewHolder(holder: RecipeViewHolder, position: Int)
{
val recipe = listToUse[position]
// to delete and edit items
val dao = RecipesDatabase.getInstance(context).recipeDao()
val repository = RecipeRepository(dao)
recipesViewModel = RecipesViewModel(repository)
//display data on list item
holder.bind(recipe)
Glide.with(context).load(recipe.imageOne)
.into(holder.binding.imageViewItemImage)
//tried to handle clicks here through the viewModel but I could not get it working from fragment
//the function call after viewModel calls is what works and it seems to work well
holder.binding.imageButtonItemdelete.setOnClickListener {
recipesViewModel.setIsDelete(true)
recipesViewModel.setPositionFromAdapter(position)
startDeleteDialog(position)
}
holder.binding.imageButtonItemedit.setOnClickListener {
recipesViewModel.setIsView(false)
recipesViewModel.setPositionFromAdapter(position)
isView = false
startEditOrViewDialog(position)
}
holder.binding.imageButtonItemview.setOnClickListener {
recipesViewModel.setIsView(true)
recipesViewModel.setPositionFromAdapter(position)
isView = true
startEditOrViewDialog(position)
}
}
fun setList(newList: List<Recipe>)
{
listToUse = newList
}
//dialog functions for the edit, delete, and view buttons on each item
private fun startDeleteDialog(position: Int)
{
AlertDialog.Builder(context)
.setTitle("Delete recipe?")
.setPositiveButton("Yes") { _, _ ->
recipesViewModel.deleteRecipe(recipes[position])
notifyItemRemoved(position)
}
.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()
}.show()
}
private fun startEditOrViewDialog(position: Int)
{
when (isView)
{
true ->
{
AlertDialog.Builder(context).setTitle("View recipe?")
.setPositiveButton("Yes") { _, _ ->
//get relevant data from current recipe
val recipe = recipes[position]
//create a dialog that shows this data in an inflated layout
val viewDialog = AlertDialog.Builder(context)
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.fragment_edit_or_view, null)
view.editText_editrecipe_directions.setText(recipe.directions)
view.editText_editrecipe_ingredients.setText(recipe.ingredients)
view.editText_editrecipe_notes.setText(recipe.notes)
view.editText_editrecipe_title.setText(recipe.title)
view.textView_date_edit.text = recipe.date
view.editText_editrecipe_title.keyListener = null
view.editText_editrecipe_directions.keyListener = null
view.editText_editrecipe_ingredients.keyListener = null
view.editText_editrecipe_notes.keyListener = null
if (recipe.rating != null)
{
view.ratingBar_edit.rating = recipe.rating
}
Glide.with(context)
.load(recipe.imageOne)
.into(view.imageView_addphoto_edit)
viewDialog.setView(view).show()
}
.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()
}.show()
}
false ->
{
AlertDialog.Builder(context).setTitle("Edit recipe?")
.setPositiveButton("Yes") { _, _ ->
//get relevant data from current recipe
val recipe = recipes[position]
val idString = recipe.id.toString()
recipesViewModel.setId(idString)
recipesViewModel.getRecipeById2(idString)
notifyDataSetChanged()
val controller = parentFragment.findNavController()
controller.navigate(
ViewRecipesFragmentDirections.actionNavViewrecipesToNavAddrecipe(
recipe.id.toString()
)
)
}
.setNegativeButton("No") { dialog, _ ->
dialog.dismiss()
}.show()
}
}
}
override fun getItemId(position: Int): Long
{
return position.toLong()
}
override fun getItemViewType(position: Int): Int
{
return position
}
class RecipeViewHolder(val binding: ViewRecipesItemBinding, val context: Context) :
RecyclerView.ViewHolder(binding.root)
{
fun bind(recipe: Recipe)
{
if (recipe.isLeftover == true)
{
binding.tvIsLeftovers.visibility = View.VISIBLE
}
binding.textViewItemTitle.text = recipe.title
if (recipe.date != null)
{
binding.textViewItemDate.text = recipe.date
}
if (recipe.rating != null)
{
binding.ratingBar2.rating = recipe.rating
}
binding.root.animation = AlphaAnimation(0.0f, 1.0f).apply {
duration = 1000
}
}
}
}
This is the view model, with live data variables set up that I could not get working in the fragment that this RecyclerView is in:
class RecipesViewModel(private val repository: RecipeRepository) : ViewModel()
{
val recipesList = repository.getAllRecipes()
private val _isView = MutableLiveData<Boolean>()
val isView: MutableLiveData<Boolean> = _isView
private val _isEdit = MutableLiveData<Boolean>()
val isEdit: MutableLiveData<Boolean> = _isEdit
private val _positionFromAdapter = MutableLiveData<Int>()
val positionFromAdapter: MutableLiveData<Int> = _positionFromAdapter
private val _isDelete = MutableLiveData<Boolean>()
val isDelete: MutableLiveData<Boolean> = _isDelete
private val _recipesListFromSearch = MutableLiveData<List<Recipe>>()
val recipesListFromSearch: LiveData<List<Recipe>> = _recipesListFromSearch
private val _recipe = MutableLiveData<Recipe>()
val recipe: LiveData<Recipe> = _recipe
lateinit var searchString: String
val savedId = MutableLiveData<String>()
fun setPositionFromAdapter(position: Int)
{
_positionFromAdapter.value = position
}
fun setIsView(isView: Boolean)
{
_isView.value = isView
}
fun setIsDelete(isDelete: Boolean)
{
_isView.value = isDelete
}
fun setIsEdit(isEdit: Boolean)
{
_isEdit.value = isEdit
}
fun setId(id: String)
{
savedId.value = id
}
fun insertRecipe(recipe: Recipe)
{
CoroutineScope(Dispatchers.IO).launch {
repository.insertRecipe(recipe)
}
}
fun getRecipesFromQuery(query: String)
{
CoroutineScope(Dispatchers.IO).launch {
val list = repository.getRecipesSearch(query)
MainScope().launch { _recipesListFromSearch.value = list }
}
}
fun saveUserRecipeToDb(
title: String?,
ingredients: String?,
directions: String?,
notes: String?,
uriToSave: String?,
rating: Float?,
date: String?,
isLeftover: Boolean,
loadedId: String
): Boolean
{
val recipeToSave = Recipe(
title,
ingredients,
directions,
notes,
uriToSave,
null,
null,
rating,
date,
isLeftover
)
if (loadedId != "666")
{
recipeToSave.id = loadedId.toInt()
}
insertRecipe(recipeToSave)
return false
}
fun getRecipeById2(id: String) = repository.getRecipeByIdLive(id)
fun deleteRecipe(recipe: Recipe)
{
CoroutineScope(Dispatchers.IO).launch {
repository.deleteRecipe(recipe)
}
}
}
How to implement onClick in the RecyclerView. Let's assume that in Your Recycler every view is a visualization of some item and when You click on it You want to do something with that item:
Create class: ClickListener:
class ClickListener(
val clickListener: (itemId: Int) -> Unit,
)
{
fun onClick(item: ItemClass) = clickListener(item.id)
}
Now in Your RecylerViewAdapter pass as an argument this Listener:
class RecylerViewAdapter(
private val clickListener: ClickListener
)
In onBindViewHolder pass this Listenner as argument
override fun onBindViewHolder(holder: ViewHolder, position: Int)
{
holder.bind(getItem(position)!!, clickListener)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder
{
return ViewHolder.from(
parent
)
}
In Your ViewHolder class:
class ViewHolder private constructor(private val binding: ItemRecyclerBinding) :
RecyclerView.ViewHolder(binding.root)
{
companion object
{
fun from(parent: ViewGroup): ViewHolder
{
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ItemRecyclerBinding.inflate(layoutInflater, parent, false)
return ViewHolder(
binding
)
}
}
fun bind(
item : Item,
clickListener: ClickListener
)
{
binding.item = item
binding.clickListener = clickListener
binding.executePendingBindings()
}
}
In Your item layout (which has to be converted to data binding layout) add this:
<data>
<variable
name="item"
type="com.example.sth.database.Item" /> // path to `Item`
<variable
name="clickListener"
type="com.example.sth.ui.adapter.ClickListener" /> // Path to `ClickListener`
</data>
Now You can add onClick method to Button:
android:onClick="#{() -> clickListener.onClick(item)}"
When You create Adapter in fragment or Activity You have to pass clickListenner as a parameter. In this way You can handle everything from fragment and RecyclerView doesn't care about what You do in this function.
val clickListenner = ClickListenner(
{ id -> viewModel.clickItemWithid(id) }, // click. This function from ViewModel will be executed when You click on item in recycler View
)
val adapter = RecylerViewAdapter (
clickListenner
)
This method is based on Google developers codelabs on Udacity.
Here You can check whole codelabs. It is free.
And here is just one video with implementing click listenner
These are the changes that are working for me now:
class ClickListener(val clickListener: (itemId: Int, itemPosition: Int, dialogInt: Int) -> Unit) {
fun onClickDelete(recipe: Recipe, position: Int, dialogInt: Int) = clickListener(recipe.id, position, dialogInt)
fun onClickEdit(recipe: Recipe, position: Int, dialogInt: Int) = clickListener(recipe.id, position, dialogInt)
fun onClickView(recipe: Recipe, position: Int, dialogInt: Int) = clickListener(recipe.id, position, dialogInt)
}
In the adapter:
class RecipeViewHolder private constructor(val binding: ViewRecipesItemBinding) :
RecyclerView.ViewHolder(binding.root) {
companion object {
fun from(parent: ViewGroup): RecipeViewHolder{
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ViewRecipesItemBinding.inflate(layoutInflater, parent, false)
return RecipeViewHolder(binding)
}
}
fun bind(recipe: Recipe, clickListener: ClickListener) {
binding.recipe = recipe
binding.imageButtonItemdelete.setOnClickListener {
clickListener.onClickDelete(recipe, adapterPosition, 1)
}
binding.imageButtonItemedit.setOnClickListener {
clickListener.onClickEdit(recipe, adapterPosition,2)
}
binding.imageButtonItemview.setOnClickListener {
clickListener.onClickView(recipe, adapterPosition,3)
}
binding.executePendingBindings()
binding.root.animation = AlphaAnimation(0.0f, 1.0f).apply {
duration = 1000
}
}
In the fragment holding the RecyclerView :
private fun initRecyclerView() {
recipesViewModel.recipesList.observe(viewLifecycleOwner, Observer {
//update recyclerview
val list = it
listForFragment = it
clickListener = ClickListener { id, position, dialogInt ->
recipesViewModel.apply {
setPositionFromAdapter(position)
setDialogRecipe(id)
}
when (dialogInt) {
1 -> startDeleteDialog(position)
2 -> startEditDialog(position)
3 -> startViewDialog(position)
}
}
rv_viewrecipes.adapter = ViewRecipesAdapter(requireContext(), list, this, clickListener)
rv_viewrecipes.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
})
}
I couldn't figure out how to pass the position and int for the when statement through the xml onClick, but for now at least the adapter class has the view logic removed.