You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MVVM架构中ViewModel如何控制Adapter?避免重复数据列表方案

Handling ViewModel-Adapter Communication & Avoiding Duplicate Data in MVVM

Great question! This is a super common hurdle when moving to MVVM, so let's walk through how to handle both the ViewModel-to-Adapter control and the duplicate data problem cleanly—all while keeping your architecture decoupled and maintainable.

How ViewModel Controls the Adapter (Indirectly!)

First off: your ViewModel should never directly reference or manipulate your Adapter. That would break MVVM's separation of concerns. Instead, the ViewModel's job is to manage the data state, and the UI layer (Activity/Fragment) acts as the middleman to update the Adapter when the data changes.

The magic here is using observable data holders like LiveData (for Android) or Flow (Kotlin coroutines). The ViewModel exposes these observables, and the UI observes them. When the data updates, the UI tells the Adapter to refresh its content.

Eliminating Duplicate Data Lists

The root cause of duplicate lists is when both the ViewModel and Adapter hold their own copies of the data. The fix is simple: only the ViewModel (or underlying Repository) should own the data source. The Adapter is just a "dumb" presentation layer—it receives data to display, but doesn't store or modify it.

Here's how to implement this properly:

Step 1: ViewModel Manages the Data State

Your ViewModel will hold the single source of truth for your data list, wrapped in an observable. All data operations (loading, adding, deleting, updating) happen here.

class ItemViewModel : ViewModel() {
    // MutableLiveData is internal to the ViewModel—expose immutable LiveData to the UI
    private val _itemList = MutableLiveData<List<Item>>(emptyList())
    val itemList: LiveData<List<Item>> = _itemList

    private val itemRepository = ItemRepository()

    // Example: Load data from a repository (API/Database)
    fun loadItems() {
        itemRepository.getItems().observeForever { fetchedItems ->
            _itemList.value = fetchedItems
        }
    }

    // Example: Add a new item to the list
    fun addNewItem(item: Item) {
        val currentItems = _itemList.value.orEmpty().toMutableList()
        currentItems.add(item)
        // Updating the LiveData triggers the UI observer
        _itemList.value = currentItems
    }
}

Step 2: UI Observes & Updates the Adapter

Your Activity/Fragment will observe the ViewModel's observable data, and pass the updated list to the Adapter. Use ListAdapter (for RecyclerView) if you want efficient, diff-based updates instead of full notifyDataSetChanged().

class ItemsActivity : AppCompatActivity() {
    private lateinit var viewModel: ItemViewModel
    private lateinit var itemAdapter: ItemAdapter

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

        // Initialize Adapter (no initial data needed!)
        itemAdapter = ItemAdapter()
        recyclerView.adapter = itemAdapter

        // Get ViewModel instance
        viewModel = ViewModelProvider(this)[ItemViewModel::class.java]

        // Observe the data list from ViewModel
        viewModel.itemList.observe(this) { updatedItems ->
            // Pass the new list to the Adapter
            itemAdapter.submitList(updatedItems)
        }

        // Trigger data load
        viewModel.loadItems()

        // Example: Add item on button click
        addItemBtn.setOnClickListener {
            viewModel.addNewItem(Item(id = UUID.randomUUID().toString(), name = "New Item"))
        }
    }
}

Step 3: Adapter Only Displays Data

The Adapter doesn't hold its own list—instead, it uses the data passed to it via submitList() (for ListAdapter) or a custom update method. It only handles binding data to UI elements.

Using ListAdapter (Recommended for Efficiency):

class ItemAdapter : ListAdapter<Item, ItemAdapter.ItemViewHolder>(ItemDiffCallback()) {

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

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val item = getItem(position)
        holder.itemName.text = item.name
    }

    // DiffUtil handles efficient partial updates
    class ItemDiffCallback : DiffUtil.ItemCallback<Item>() {
        override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
            // Use unique ID to check if items are the same
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
            // Compare all content fields to check if data changed
            return oldItem.name == newItem.name
        }
    }

    class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val itemName: TextView = itemView.findViewById(R.id.item_name)
    }
}

If Using a Regular RecyclerView.Adapter:

class ItemAdapter : RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {
    private var items: List<Item> = emptyList()

    // Custom method to update the list
    fun updateItems(newItems: List<Item>) {
        items = newItems
        // For better performance, use DiffUtil here instead of notifyDataSetChanged()
        notifyDataSetChanged()
    }

    // ... onCreateViewHolder and onBindViewHolder use the 'items' list ...
}

Key Takeaways

  • ViewModel owns the data: It's the single source of truth—all data changes happen here.
  • Adapter is presentation-only: No data storage, no business logic—just takes data and displays it.
  • Observable data bridges the gap: LiveData/Flow lets the ViewModel notify the UI when data changes, which then updates the Adapter.
  • Avoid direct dependencies: ViewModel never references the Adapter, and the Adapter never references the ViewModel—this keeps your code decoupled and testable.

内容的提问来源于stack exchange,提问作者MaaAn13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:22:41