上下文:
MainActivity
启动、创建列表ItemObject
并调用ListFragment
. 当用户点击button
(在 中MainActivity
)时,它会修改列表,然后调用listFragment
来更新它。
发现问题:
在update
功能上listFragment
,newList
和oldList
都是一样的。
这是我的代码。
MainActivity
class MainActivity : AppCompatActivity() {
private val viewModel = MainViewModel()
private var button : FloatingActionButton? = null
private val listFragment = ListFragment.newInstance()
var list = ArrayList<ItemObject>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.viewModel = viewModel
button = binding.button
button?.setOnClickListener { modifyList() }
createItemsList()
}
/**
* This creates a list of 10 ItemObject, that will contains `i` as `randomInt` and "TEST $i" as `title`, then call
* setListFragment().
*/
private fun createItemsList() {
val itemsList = ArrayList<ItemObject>()
var i = 0
while (i < 10) {
itemsList.add(ItemObject().apply { this.randomInt = i ; this.title = "TEST $i" })
i++
}
list = itemsList
setListFragment()
}
/**
* Set listFragment inside content.
*/
private fun setListFragment() {
supportFragmentManager.beginTransaction().replace(R.id.content, listFragment).commit()
}
/**
* Triggered when the user clicks on the FloatingActionButton. Will modify each even item, add 2 to its `randomInt`
* and set its `title` to "MODIFIED $randomInt".
*/
private fun modifyList() {
list.forEach {
if (it.randomInt % 2 == 0) {
it.randomInt += 2
it.title = "MODIFIED ${it.randomInt}"
}
}
if (listFragment.isAdded) {
listFragment.updateList(list)
}
}
inner class MainViewModel
}
并且ListFragment
:
class ListFragment : Fragment() {
private val viewModel = ListViewModel()
private val listAdapter = ListAdapter()
private var listRv : RecyclerView? = null
private var list = ArrayList<ItemObject>()
override fun onCreateView(inflater: LayoutInflater, parent: ViewGroup?, savedInstanceState: Bundle?): View? {
val binding = DataBindingUtil.inflate<FragmentListBinding>(inflater, R.layout.fragment_list, parent, false)
binding.viewModel = viewModel
listRv = binding.listRv
list = (activity as MainActivity).list
setList()
return (binding.root)
}
/**
* Sets up the RecyclerView and set the list inside it.
*/
private fun setList() {
listRv?.layoutManager = LinearLayoutManager(context)
listRv?.adapter = listAdapter
listRv?.post { listAdapter.setData(list) }
}
/**
* Triggered by MainActivity when the user clicks on the button and the list is modified. Will call update() method
* from adapter.
*/
fun updateList(newList : ArrayList<ItemObject>) {
listAdapter.update(newList)
}
companion object {
fun newInstance() : ListFragment = ListFragment()
}
inner class ListViewModel
inner class ItemDiff : DiffUtil.Callback() {
private var old = ArrayList<ItemObject>()
private var new = ArrayList<ItemObject>()
fun setLists(old : ArrayList<ItemObject>, new : ArrayList<ItemObject>) {
this.old = old
this.new = new
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
println("ARE ITEMS ${old[oldItemPosition].title} THE SAME ? ${(old[oldItemPosition] == new[newItemPosition])}")
return (old[oldItemPosition] == new[newItemPosition])
}
override fun getOldListSize(): Int = old.size
override fun getNewListSize(): Int = new.size
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
println("ARE ITEMS ${old[oldItemPosition].title} CONTENTS THE SAME ? ${(old[oldItemPosition].title == new[newItemPosition].title
&& old[oldItemPosition].randomInt == new[newItemPosition].randomInt)}")
return (old[oldItemPosition].title == new[newItemPosition].title
&& old[oldItemPosition].randomInt == new[newItemPosition].randomInt)
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = old[oldItemPosition]
val newItem = new[newItemPosition]
val bundle = Bundle()
if (oldItem.title != newItem.title) {
println("SHOULD ADD NEW STRING ${newItem.title}")
bundle.putString("title", newItem.title)
}
if (oldItem.randomInt != newItem.randomInt) {
println("SHOULD ADD NEW INT ${newItem.randomInt}")
bundle.putInt("randomInt", newItem.randomInt)
}
return (bundle)
}
}
inner class ListAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var items = ArrayList<ItemObject>()
fun setData(list : ArrayList<ItemObject>) {
items = list
notifyDataSetChanged()
}
/**
* @param new
* Triggered when the list is modified in the parent activity. Uses DiffUtil to update the list.
*/
fun update(new : ArrayList<ItemObject>) {
println("///// IN UPDATE ; WILL PRINT OLD AND NEW LIST /////")
items.forEach { println("OLD ITEM ${it.title}") }
new.forEach { println("NEW ITEM ${it.title}") }
println("///// PRINT END /////")
val diffCallback = ItemDiff()
diffCallback.setLists(old = items, new = new)
val diffResult = DiffUtil.calculateDiff(diffCallback)
diffResult.dispatchUpdatesTo(this)
items = new
}
override fun onCreateViewHolder(parent: ViewGroup, position: Int): RecyclerView.ViewHolder {
return (ItemViewHolder(DataBindingUtil.inflate<ItemBinding>(LayoutInflater.from(parent.context),
R.layout.item, parent, false).apply {
viewModel = ItemViewModel()
}))
}
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as ItemViewHolder).setData(items[position])
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any>) {
println("IN ON BIND VIEWHOLDER ; PAYLOAD SIZE = ${payloads.size}")
if (payloads.isEmpty())
super.onBindViewHolder(holder, position, payloads)
else {
val bundle = payloads[0] as Bundle
if (bundle.size() != 0) {
val name = bundle.getString("name")
val randomInt = bundle.getInt("randomInt")
if (name != null)
(holder as ItemViewHolder).setName(name)
(holder as ItemViewHolder).setRandomInt(randomInt)
}
}
}
inner class ItemViewHolder(private val binding : ItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun setData(item : ItemObject) {
binding.viewModel?.setData(item)
}
fun setRandomInt(newInt : Int) {
binding.viewModel?.setRandomInt(newInt)
}
fun setName(newName : String) {
binding.viewModel?.setTitle(newName)
}
}
inner class ItemViewModel {
val title = ObservableField<String>("")
val randomInt = ObservableField<String>("")
fun setData(item : ItemObject) {
setRandomInt(item.randomInt)
setTitle(item.title)
}
fun setRandomInt(newInt : Int) {
randomInt.set(newInt.toString())
}
fun setTitle(newName : String) {
title.set(newName)
}
}
}
}
这是我的ItemObject
课:
class ItemObject {
var title = ""
var randomInt = 0
}
奇怪的是我从不修改里面的列表ListFragment
。那么,当列表更新时,我的调试 println 怎么可能告诉我新旧列表是一样的呢?