Некорректная работа RecyclerView с LiveData
Учусь работать с LiveData, ViewModel и RecyclerView. В приложении два фрагмента. В первом список элементов, второй добавляет новый элемент. После добавления фрагмент закрывается, возвращаясь в список. Проблема в том, что onChange в LiveData вызывается два раза, в адаптер набор данных передается два раза. Один раз предыдущий набор данных и второй раз уже новый. Из-за этого в списке элементы перемешиваются.
LogCat при запуске:
2020-09-06 11:31:42.459 25304-25304/ru.albeiz.equipmover D/TAG: Get Live Data
2020-09-06 11:31:42.460 25304-25304/ru.albeiz.equipmover D/TAG: Setting itemList
После я запускаю второй фрагмент и как результат добавляю новый элемент в БД. Затем этот фрагмент закрывает и управление возвращается во второй. При этом LogCat следующий:
2020-09-06 11:32:19.669 25304-25304/ru.albeiz.equipmover D/TAG: Get Live Data
2020-09-06 11:32:19.669 25304-25304/ru.albeiz.equipmover D/TAG: Setting itemList
2020-09-06 11:32:19.838 25304-25304/ru.albeiz.equipmover D/TAG: Get Live Data
2020-09-06 11:32:19.839 25304-25304/ru.albeiz.equipmover D/TAG: Setting itemList
То есть OnChange срабатывает 2 раза и адаптер списка получает набор элементов дважды. Сначала старый набор (до нового элемента), после новый. На экране recycler показывает перемешанные элементы. Если я снова запускаю второй фрагмент и закрываю его, не вводя ничего, то список уже показывает всё как надо. То же происходит и после перезапуска приложения.
Вопрос в том как это исправить и почему так происходит?
class FirstFragment : Fragment() {
private val TAG = "FirstFragment"
private lateinit var mViewModel: EquipViewModel
private lateinit var mAdapter: EquipAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_equip_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
fab.setOnClickListener {
mNewMoveCaller.createNewMove()
}
initRecyclerView()
mViewModel = ViewModelProvider(requireActivity()).get(EquipViewModel::class.java)
mViewModel.getAllMoves().observe(viewLifecycleOwner, Observer{
Log.d("TAG", "Get Live Data")
mAdapter.setList(it)
})
}
private fun initRecyclerView() {
mAdapter = EquipAdapter()
recyclerView.apply {
layoutManager = LinearLayoutManager(activity)
setHasFixedSize(true)
adapter = mAdapter
}
}
}
class SecondFragment : Fragment() {
private lateinit var mViewModel: EquipViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_new_move, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.miSaveMove -> {
saveNewMove()
}
}
return true
}
private fun saveNewMove() {
val newEquipMove = EquipMove(
null,
etNewCode.text.toString(),
Date().time,
//"spDestination.selectedItem.toString()"
"someText"
)
mViewModel.insert(newEquipMove)
exitFragment()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_new_move, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val activity = requireActivity() as AppCompatActivity
activity.setSupportActionBar(frag_new_move_toolbar)
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
activity.supportActionBar?.title = "Add a new move"
frag_new_move_toolbar.setNavigationOnClickListener {
exitFragment()
}
mViewModel = ViewModelProvider(requireActivity()).get(EquipViewModel::class.java)
btnSaveNewMove.setOnClickListener {
saveNewMove()
}
}
private fun exitFragment() {
requireActivity().onBackPressed()
}
}
class EquipAdapter(private var itemList: List<EquipMove> = listOf()):
RecyclerView.Adapter<EquipAdapter.EquipViewHolder>() {
inner class EquipViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EquipViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recview_item, parent, false)
return EquipViewHolder(view)
}
override fun getItemCount(): Int {
return itemList.size
}
override fun onBindViewHolder(holder: EquipViewHolder, position: Int) {
holder.itemView.apply {
tvEquipmentCode.append(itemList[position].equipCode)
tvDestination.append(itemList[position].moveDestination)
tvMoveDate.append(itemList[position].moveDate.toString())
}
}
fun setList(itemList: List<EquipMove>) {
this.itemList = itemList
notifyDataSetChanged()
Log.d("TAG", "Setting itemList")
}
}
class EquipViewModel(application: Application): AndroidViewModel(application) {
private val equipRepository = EquipRepository(application)
private val allMoves = equipRepository.getAllMoves()
fun insert(equipMove: EquipMove) {
viewModelScope.launch {
equipRepository.insert(equipMove)
}
}
fun deleteAll() {
viewModelScope.launch {
equipRepository.deleteAll()
}
}
fun delete(equipMove: EquipMove) {
viewModelScope.launch {
equipRepository.delete(equipMove)
}
}
fun getAllMoves() : LiveData<List<EquipMove>> {
return allMoves
}
}