evgeni2306
evgeni2306

Reputation: 3

need to use DiffUtill facilities in recyclerView Kotlin

Employee class:

 data class Employee(
        val id: Long,
        val fullName: String,
        val city: String,
        var isLiked: Boolean = false,
    ) {
        companion object {
            fun getMockEmployees() = listOf(
                Employee(
                    0,  
                    "John Johnson",
                    "London",
                ),
                Employee(
                    1,
                    "John Johnson",
                    "London",
                ),
                Employee(
                    2,
                    "John Johnson",
                    "London",
                ),
                Employee(
                    3,
                    "John Johnson",
                    "London",
                )
            )
        }

EmployeeAdapter:

class EmployeeAdapter(
    private val clickedLike: (Int) -> Unit
) :
    RecyclerView.Adapter<EmployeeAdapter.EmployeeViewHolder>() {

    private val employees = mutableListOf<Employee>()

    inner class EmployeeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val employeeCard: ConstraintLayout = itemView.findViewById(R.id.employees_list_item)
        val fullNameTextView: TextView = itemView.findViewById(R.id.full_name)
        val likeButton: ImageView = itemView.findViewById(R.id.like_button)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmployeeViewHolder {
        val view =
            LayoutInflater.from(parent.context).inflate(R.layout.employees_list_item, parent, false)
        return EmployeeViewHolder(view)
    }

    override fun onBindViewHolder(holder: EmployeeViewHolder, position: Int) {
        val employee = employees[position]
        with(holder) {
            fullNameTextView.text = employee.fullName
            likeButton.visibility = if (employee.isLiked) View.VISIBLE else View.INVISIBLE

            employeeCard.setOnClickListener {
                clickedLike(position)
                likeButton.visibility = if (employee.isLiked) View.VISIBLE else View.INVISIBLE
            }
        }
    }

    override fun getItemCount(): Int {
        return employees.size
    }

    fun reload(data: List<Employee>) {
        val diffUtil = EmployeesDiffUtilCallback(employees, data)
        val result = DiffUtil.calculateDiff(diffUtil)
        employees.clear()
        employees.addAll(data)
        result.dispatchUpdatesTo(this)
    }

EmployeesDiffUtilCallback:

class EmployeesDiffUtilCallback(
    private val oldList: List<Employee>,
    private val newList: List<Employee>
) : DiffUtil.Callback() {
    override fun getOldListSize() = oldList.size

    override fun getNewListSize() = newList.size

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].id == newList[oldItemPosition].id
    }

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        val old = oldList[oldItemPosition]
        val new = newList[oldItemPosition]
        return old.id == new.id && (old.isLiked == new.isLiked)
    }
}

MainActivity:

 private val viewModel: MainActivityViewModel by viewModels()
    private lateinit var employeeAdapter: EmployeeAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.layoutManager = GridLayoutManager(this, 2)

        val itemDecoration = DividerItemDecoration(
            recyclerView.context,
            GridLayoutManager.VERTICAL
        )

        val itemDecoration2 = DividerItemDecoration(
            recyclerView.context,
            GridLayoutManager.HORIZONTAL
        )
        recyclerView.addItemDecoration(itemDecoration)
        recyclerView.addItemDecoration(itemDecoration2)

        employeeAdapter = EmployeeAdapter(viewModel::likeEmployee)

        recyclerView.adapter = employeeAdapter

        viewModel.employees.observe(this) {
            employeeAdapter.reload(it)
        }
    }

MainActivityViewModel :

val employees = MutableLiveData(Employee.getMockEmployees())

    fun likeEmployee(position: Int) {
        employees.value?.get(position)?.isLiked = !(employees.value?.get(position)?.isLiked)!!
    }

My mentor have said that modification of flag Employee::isLiked should be in ViewModel and after it by using LiveData ViewModel it should tell UI about changes in list. I'm new in Kotlin and don't understand how to do it. Help me pls

Upvotes: 0

Views: 174

Answers (1)

Daniel Knauf
Daniel Knauf

Reputation: 589

I think you have a good basis for your use case and what your mentor wants.

You can improve and simplify your Adapter by using a ListAdapter with DiffCallback:

class EmployeeAdapter(
    private val onEmployeeLiked: (Employee) -> Unit
) : ListAdapter<Employee, EmployeeAdapter.EmployeeViewHolder>(DiffCallback()){

        inner class EmployeeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
            val employeeCard: ConstraintLayout = itemView.findViewById(R.id.employees_list_item)
            val fullNameTextView: TextView = itemView.findViewById(R.id.full_name)
            val likeButton: ImageView = itemView.findViewById(R.id.like_button)
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EmployeeViewHolder {
            val view =
                LayoutInflater.from(parent.context).inflate(R.layout.employees_list_item, parent, false)
            return EmployeeViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: EmployeeViewHolder, position: Int) {
            val employee = employees[position]
            with(holder) {
                fullNameTextView.text = employee.fullName
                likeButton.visibility = if (employee.isLiked) View.VISIBLE else View.INVISIBLE
    
                employeeCard.setOnClickListener {
                    onEmployeeLiked(employee)
                    likeButton.isVisible = employee.isLiked
                }
            }
        }

}

class DiffCallback : DiffUtil.ItemCallback<Employee>() {

    override fun areItemsTheSame(
        oldItem: Employee,
        newItem: Employee
    ): Boolean = // use id or other identifier to determine if employees are the same
       
    override fun areContentsTheSame(
        oldItem: Employee,
        newItem: Employee
    ): Boolean = // compare empty
}

With a ListAdapter, you do not have to manage updating your list by yourself. Just use adapter.submitList(list) and the adapter will update itself.

You can use isVisible on the View class to update the visibility more easily.

I would also give the Employee in the callback, then you do not have to get it again.

class MainActivityViewModel{

    val employees = MutableLiveData(Employee.getMockEmployees())

    fun onEmployeeLiked(employee : Employee){
        val currentEmployees = employee.value?.toMutableList()?: return
        val index = currentEmployees.indexOf{ e -> e.id == employee.id} //find the index of the updated employee 

        if(index == -1) return // employee not found

        employee.isLiked = !employee.isLiked

        currentEmployees.set(index, employee) // update employee in list

        employees.value = currentEmployees // set new value 
    }

}

// Activity
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.layoutManager = GridLayoutManager(this, 2)

        val itemDecoration = DividerItemDecoration(
            recyclerView.context,
            GridLayoutManager.VERTICAL
        )

        val itemDecoration2 = DividerItemDecoration(
            recyclerView.context,
            GridLayoutManager.HORIZONTAL
        )
        recyclerView.addItemDecoration(itemDecoration)
        recyclerView.addItemDecoration(itemDecoration2)

        employeeAdapter = EmployeeAdapter(viewModel::likeEmployee)

        recyclerView.adapter = employeeAdapter

        viewModel.employees.observe(this) { employees ->
            employeeAdapter.submitList(employees) // display the updated list in the adapter
        }
    }


Upvotes: 0

Related Questions