it's my first post in stack overflow, sorry in advance if my message has charter faults.
I've problem using DiffUtil in my project, i hope you can help me. I explain:
I build an app who manage different students class. I have fragment who display a students list in a recyclerView. I recove this list from a local bdd. My aim is when i scroll the recyclerView to display the items at the bottom (at the base don't displayed) and i click on interactive element on item (student), the fragment refresh the list and go to the top of the list. I've decided to use DiffUtil to fix this problem because i want to modify items many time anywhere in the list. I've solve it but an other problem was came. When i switch student class, list A to list B, (and switch student list) i want display other student list and Diffutil compare the newList (list B) with the old list (list A) and no old list and new list based from the list B. I've tried many solutions but it was inconclusive...
Here my Fragment :
class ClassManagementFragment : Fragment(), StatsUpdater {
private lateinit var binding: FragmentClassManagementBinding
lateinit var adapter: StudentsListAdapter
private lateinit var recyclerView: RecyclerView
private val databaseCallsVM: DatabaseCallsViewModel by viewModels {
MathWorldViewModelFactory((requireActivity().application as MathWorldApplication).repository)
}
private lateinit var mainVM: MainViewModel
private val uiConfigure: UiConfigure = UiConfigureImpl()
private var classID: Int? = null
private var currentClass: StudentsClass? = null
private var experienceGiven: Int = 1
private var mActionMode: ActionMode? = null
private var myStudentsList: List<Student> = emptyList()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentClassManagementBinding.inflate(inflater, container, false)
mainVM = ViewModelProvider(requireActivity())[MainViewModel::class.java]
setHasOptionsMenu(true)
recyclerView = binding.studentsListRecyclerView
mainVM.classNumber.observe(requireActivity()) { id ->
classID = id
configureRecyclerView(classID!!)
databaseCallsVM.getClassInformation(classID!!)
?.observe(requireActivity()) { cClass ->
currentClass = cClass
}
}
updateExperienceGiven(experienceGiven)
return binding.root
}
override fun onResume() {
super.onResume()
if (currentClass != null) activity?.title = currentClass!!.name
}
private fun configureRecyclerView(classDisplayed: Int) {
//val recyclerView = binding.studentsListRecyclerView
adapter = StudentsListAdapter(this@ClassManagementFragment, uiConfigure)
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(activity)
recyclerView.addItemDecoration(
DividerItemDecoration(
requireActivity(),
DividerItemDecoration.VERTICAL
)
)
databaseCallsVM.getAllStudentsInClass(classDisplayed)
?.observe(requireActivity()) {
//val firstStudentName = it[0].students[0].firstName
if (it.isNotEmpty()) {
val students = it[0].students
students.let { studentsList ->
val studentListSorted = studentsList.sortedBy { student ->
student.lastName
}
adapter.submitList(studentListSorted.toList())
}
}
}
}
override fun updateExperience(student: Student, experience: Int, xpMax: Int) {
student.experience = experience
if (student.xpMax != xpMax) {
student.xpMax = xpMax
}
databaseCallsVM.updateStudent(student)
}
override fun updateLevel(student: Student) {
student.level += 1
val text = getString(R.string.level_up)
Toast.makeText(requireActivity(), student.firstName + " " + text, Toast.LENGTH_SHORT).show()
databaseCallsVM.updateStudent(student)
}
override fun updateLife(student: Student, life: Int) {
student.pointOfLife = life
databaseCallsVM.updateStudent(student)
}
override fun updateGroup(student: Student, group: Int) {
student.group = group
databaseCallsVM.updateStudent(student)
}
override fun openDetail(student: Student) {
val intent = Intent(requireActivity(), StudentDetailsActivity::class.java)
intent.putExtra("student", student)
intent.putExtra("classId", classID)
startActivity(intent)
}
private fun updateExperienceGiven(xpChoosed: Int) {
adapter.updateExperienceGiven(xpChoosed)
}
}
And here my adapter:
private const val STUDENT_CLASS = "student.class"
private const val STUDENT_LIFE = "student.life"
private const val STUDENT_LEVEL = "student.level"
private const val STUDENT_EXPERIENCE = "student.experience"
private const val STUDENT_MAX_EXPERIENCE = "student.maxExperience"
private const val STUDENT_GROUP = "student.group"
class StudentsListAdapter(private val upStats: StatsUpdater, private val uiConfigure: UiConfigure) :
ListAdapter<Student, StudentsListAdapter.StudentViewHolder>(
AsyncDifferConfig.Builder<Student>(
DifferCallback()
).build()
) {
private lateinit var binding: ItemStudentBinding
var giveExperience: Int = 1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StudentViewHolder {
binding = ItemStudentBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return StudentViewHolder(binding)
}
override fun onBindViewHolder(holder: StudentViewHolder, position: Int) {
super.onBindViewHolder(holder, position, emptyList())
}
override fun onBindViewHolder(holder: StudentViewHolder, position: Int, payloads: List<Any>) {
val item = getItem(position)
if (payloads.isEmpty() || payloads[0] !is Bundle) {
holder.setData(item)
} else {
val bundle = payloads[0] as Bundle
holder.update(bundle)
}
}
override fun getItemCount() = currentList.size
fun updateExperienceGiven(xpChoosed: Int) {
this.giveExperience = xpChoosed
}
inner class StudentViewHolder(private val itemBinding: ItemStudentBinding) :
RecyclerView.ViewHolder(binding.root) {
fun setData(item: Student) {
binding.apply {
// XP & LVL
val xpBar = this.studentXpBar
uiConfigure.displayExperience(
item.experience,
item.xpMax,
this.studentLevelCurrentXp
)
studentLevelResponse.text = item.level.toString()
uiConfigure.updateProgressBarXp(
item.experience,
item.xpMax,
giveExperience,
this.studentXpBar
)
xpBar.progress = item.experience
xpBar.max = item.xpMax
xpBar.setOnClickListener {
xpBar.progress += giveExperience
if (xpBar.progress >= xpBar.max) {
val currentXp = (item.experience + giveExperience) - xpBar.max
val newXpMax = xpBar.max + 5
upStats.updateExperience(item, currentXp, newXpMax)
upStats.updateLevel(item)
} else {
upStats.updateExperience(item, xpBar.progress, xpBar.max)
}
}
// LIFE
uiConfigure.displayHeartIconLife(item.pointOfLife, this.studentLifeImage)
uiConfigure.displayLifeNumber(item.pointOfLife, this.studentLifePoint)
this.studentLifeImage.setOnClickListener {
var currentLife = item.pointOfLife
if (item.pointOfLife == 0) {
currentLife = 3
} else currentLife--
upStats.updateLife(item, currentLife)
}
// NAME
this.studentFirstname.text = item.firstName
this.studentLastname.text = item.lastName
// GROUP
this.studentIlotNumber.text = item.group.toString()
var groupNumber = item.group
uiConfigure.changeGroupImageColor(groupNumber, this.studentIlotImage)
this.studentIlotImage.setOnClickListener {
if (groupNumber >= 10) groupNumber = 1
else groupNumber++
upStats.updateGroup(item, groupNumber)
}
// BELT
uiConfigure.setBelt(item.bestBelt, this.studentBelt)
// JOB
uiConfigure.displayJobImage(item.job, this.studentJobImage)
// Detail
itemView.setOnClickListener {
upStats.openDetail(item)
}
}
}
fun update(bundle: Bundle) {
if (bundle.containsKey(STUDENT_LIFE)) {
val life = bundle.getInt(STUDENT_LIFE)
uiConfigure.displayHeartIconLife(life, itemBinding.studentLifeImage)
uiConfigure.displayLifeNumber(life, itemBinding.studentLifePoint)
}
if (bundle.containsKey(STUDENT_LEVEL)) {
val level = bundle.getInt(STUDENT_LEVEL)
itemBinding.studentLevelResponse.text = level.toString()
}
if (bundle.containsKey(STUDENT_EXPERIENCE)) {
val experience = bundle.getInt(STUDENT_EXPERIENCE)
val maxExp = bundle.getInt(STUDENT_MAX_EXPERIENCE)
uiConfigure.displayExperience(
experience,
maxExp,
itemBinding.studentLevelCurrentXp
)
uiConfigure.updateProgressBarXp(
experience,
maxExp,
giveExperience,
itemBinding.studentXpBar
)
}
if (bundle.containsKey(STUDENT_GROUP)) {
val group = bundle.getInt(STUDENT_GROUP)
itemBinding.studentIlotNumber.text = group.toString()
uiConfigure.changeGroupImageColor(group, itemBinding.studentIlotImage)
}
}
}
private class DifferCallback : DiffUtil.ItemCallback<Student>() {
override fun areItemsTheSame(oldItem: Student, newItem: Student): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Student, newItem: Student): Boolean {
return oldItem.class_id == newItem.class_id && oldItem.pointOfLife == newItem.pointOfLife && oldItem.level == newItem.level &&
oldItem.experience == newItem.experience && oldItem.group == newItem.group
}
override fun getChangePayload(oldItem: Student, newItem: Student): Any? {
if (oldItem.id == newItem.id) {
return if (oldItem.class_id == newItem.class_id && oldItem.level == newItem.level && oldItem.experience == newItem.experience &&
oldItem.pointOfLife == newItem.pointOfLife && oldItem.group == newItem.group
) {
super.getChangePayload(oldItem, newItem)
} else {
val diff = Bundle()
diff.putInt(STUDENT_CLASS, newItem.class_id)
diff.putInt(STUDENT_LIFE, newItem.pointOfLife)
diff.putInt(STUDENT_LEVEL, newItem.level)
diff.putInt(STUDENT_EXPERIENCE, newItem.experience)
diff.putInt(STUDENT_MAX_EXPERIENCE, newItem.xpMax)
diff.putInt(STUDENT_GROUP, newItem.group)
diff
}
}
return super.getChangePayload(oldItem, newItem)
}
}
}
Anyone have an idea what's wrong ?
I've tried to refresh fragment, not work. Same with add if() condition by the student class id but not work. And other attempts but I forgot this.