MVVM架构中ViewModel如何控制Adapter?避免重复数据列表方案
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

