android 我的 viewModel 在内部更新列表,但 recyclerview 不反映更改

android my viewModel update the list internally but recyclerview does't reflects the changes

我正在尝试使用回收器视图和 MVVM 查看我的文件,问题是 memoryViewModel 没有反映 memoryItemsRecyclerViewAdapter

上的更改
memoryViewModel!!.mutableLiveData?.observe(this.viewLifecycleOwner) {
            memoryItemsRecyclerViewAdapter.notifyDataSetChanged() //this should show the list items of the recycler view
            Log.d("view model result", it.size.toString()) // log message shows the list items
        }

所以,最后一个问题是为什么 mutableLiveData!!.postValue(mutableList) 不更新回收站视图? 这是我的代码示例

片段

class MemoryFragment: Fragment() {
    companion object{
        private var rootDirectory: java.io.File? = null
        var currentFolder = rootDirectory
        private var filesList: MutableList<File?>? = mutableListOf()
        private var memoryViewModel: MemoryViewModel? = null
    }
    private var sharedPreferences: SharedPreferences? = null
    private lateinit var listView: RecyclerView
    private lateinit var pathsRecyclerView: RecyclerView
    private lateinit var searchView: androidx.appcompat.widget.SearchView
    private lateinit var refreshSwipe: SwipeRefreshLayout
    private lateinit var memoryItemsRecyclerViewAdapter: MemoryItemsRecyclerViewAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sharedPreferences = androidx.preference.PreferenceManager
            .getDefaultSharedPreferences(requireContext())

        val memoryViewModelProvider = MemoryViewModelProvider()
        memoryViewModel = ViewModelProvider(this, memoryViewModelProvider).get(MemoryViewModel::class.java)
        
//        rootDirectory = Environment.getExternalStorageDirectory()
//        currentFolder = rootDirectory
//        val rootFoldersList = rootDirectory?.listFiles()?.toMutableList()
//        for (item in rootFoldersList!!)
//            if(item.isDirectory)
//                filesList?.add(File(R.drawable.ic_folders, item, item.totalSpace))
//            else
//                filesList?.add(File(R.drawable.ic_file, item, item.totalSpace))
//        memoryViewModel = MemoryViewModel(filesList!!)
//        MemoryViewModel.lazyMemoryViewModel
    }
    
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = LayoutInflater.from(requireContext()).inflate(R.layout.fragment_memory, container, false)
        listView = view.findViewById(R.id.list_view)
        pathsRecyclerView = view.findViewById(R.id.recycler_view)
        searchView = view.findViewById(R.id.search_view)
        refreshSwipe = view.findViewById(R.id.swipe_refresh)
        listView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)

        memoryItemsRecyclerViewAdapter = MemoryItemsRecyclerViewAdapter(memoryViewModel!!, requireContext(), filesList!!)

        listView.adapter = memoryItemsRecyclerViewAdapter

        memoryViewModel!!.mutableLiveData?.observe(this.viewLifecycleOwner) {
            memoryItemsRecyclerViewAdapter.notifyDataSetChanged()
            Log.d("view model result", it.size.toString())

        }
        return view
    }
}

视图模型

class MemoryViewModel(private var mutableList: MutableList<com.example.everyentertainment.models.File?>?): ViewModel() {
private val foldersPath: MutableList<String?>? = null
private val foldersName: MutableList<String?>? = null
private var rootDirectory: File? = null
var mutableLiveData: MutableLiveData<MutableList<com.example.everyentertainment.models.File?>>? = null

companion object{
    var currentFolder: File? = null
}

init {
    val job = viewModelScope.launch(Dispatchers.IO) {
        if (mutableLiveData == null) mutableLiveData = MutableLiveData()
        if(mutableList == null) mutableList = mutableListOf()
        initializeMemoryFragment()
    }
    job.start()
    job.invokeOnCompletion {
        mutableLiveData!!.postValue(mutableList)//postValue() doesn't update UI also I tried mutableLiveData.value = mutableList but it throws OnCompletionHandlerException
        Log.d("mutable list size", mutableList!!.size.toString())
        Log.d("mutable live data size", mutableLiveData!!.value.toString())
    }
}

fun initializeMemoryFragment(){
    rootDirectory = getExternalStorageDirectory()
    currentFolder = rootDirectory
    val filesList = rootDirectory!!.listFiles()
    for(position in filesList!!.indices)
        if(filesList[position].isDirectory)
            mutableList!!.add(com.example.everyentertainment.models.File(R.drawable.ic_folders,
                filesList[position], getFolderSize(filesList[position])))
        else
            mutableList!!.add(com.example.everyentertainment.models.File(R.drawable.ic_file,
                filesList[position], getFolderSize(filesList[position])))
}

 fun getFolderSize(file: File): Long{
    if(!file.exists())
        return 0
    if(!file.isDirectory)
        return file.length()
    val dirs: MutableList<File> = LinkedList()
    dirs.add(file)
    var result: Long = 0
    while (!dirs.isEmpty()) {
        val dir = dirs.removeAt(0)
        if (!dir.exists()) continue
        val listFiles = dir.listFiles()
        if (listFiles == null || listFiles.isEmpty()) continue
        for (child in listFiles) {
            result += child.length()
            if (child.isDirectory) dirs.add(child)
        }
    }
    return result
}

最后是回收器视图适配器

class MemoryItemsRecyclerViewAdapter(private val memoryViewModel: MemoryViewModel, private val context: Context, private val dataSet: MutableList<File?>?):
RecyclerView.Adapter<MemoryItemsRecyclerViewAdapter.ViewHolder>() {
/**
 * Provide a reference to the type of views that you are using
 * (custom ViewHolder).
 */
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    val fileNameTextView: TextView = view.findViewById(R.id.name_text_view)
    val sizeTextView: TextView = view.findViewById(R.id.size_text_view)
    val numberOfFilesTextView: TextView = view.findViewById(R.id.number_of_files_text_view)
    val dateTextView: TextView = view.findViewById(R.id.date_text_view)
    val imageView: ImageView = view.findViewById(R.id.image_view)

    init {
    // Define click listener for the ViewHolder's View.

    }
}

// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
    // Create a new view, which defines the UI of the list item
    val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.memory_list_view_item, viewGroup, false)
    return ViewHolder(view)
}

// Replace the contents of a view (invoked by the layout manager)
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
    // Get element from your dataset at this position and replace the
    // contents of the view with that element
    val file = dataSet!![position]
    viewHolder.fileNameTextView.text = file!!.file.name
    viewHolder.sizeTextView.text = memoryViewModel.readableFileSize(dataSet[position]!!.size)
    viewHolder.dateTextView.text = memoryViewModel.getFolderDateModified(file.file)
    viewHolder.numberOfFilesTextView.text = memoryViewModel.getSubFoldersQuantity(context, file.file)
    if(file.file.isDirectory)
        viewHolder.imageView.setImageResource(R.drawable.ic_folders)
    else
        viewHolder.imageView.setImageResource(R.drawable.ic_file)
}

// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = dataSet!!.size

}

您的适配器被硬编码为显示 dataSet 的内容(您在 onBindViewHolder 中获取它,在 getItemCount 中引用它)并且您永远不会更改这些内容,所以有从来没有什么要更新的。 RecyclerView 只会显示您第一次传入的内容。您需要使适配器的数据可更新,我建议这样做:

class MemoryItemsRecyclerViewAdapter(
    private val memoryViewModel: MemoryViewModel,
    private val context: Context,
    private var dataSet: List<File?> = emptyList() // this is a var now, and not a mutable list
) : RecyclerView.Adapter<MemoryItemsRecyclerViewAdapter.ViewHolder>() {

    // this function replaces the current data with the new set, and refreshes the UI
    fun setData(newData: List<File?>) {
        dataSet = newData
        notifyDataSetChanged()
    }

    ...
}

现在您有了更新适配器的方法,它负责更新 UI 本身 - 最好在此处包含逻辑(如调用 notifyDataSetChanged()),因为确实适配器应该决定发生了什么变化以及如何处理它。

我制作了 dataSet 一个 var 这样你就可以将旧列表换成新列表,并使它们不可变 List 因为它们不需要可变.我将 emptyList() 添加为默认值 - 它不需要可以为 null,并且您将其视为 non-null (到处都是 !! ),所以只需将其设为 non-null.如果你真的想要,你可以使它成为一个可变列表并这样做 - 在这种情况下,使用 mutableListOf() 作为空默认值而不是


所以现在你可以更新适配器了,你只需要在你的观察者函数中这样做:

memoryViewModel!!.mutableLiveData?.observe(this.viewLifecycleOwner) { newData ->
    // when a new list comes in, set it on the adapter
    memoryItemsRecyclerViewAdapter.setData(newData)
    Log.d("view model result", newData.size.toString())
}

就是这样。你的观察者只是在新值进来时做出反应(包括任何当前值,当你第一次 observeLiveData 时)所以你只需要对它们做任何事情

(此外,通过使所有内容都可为空,您确实使事情变得复杂,尤其是当您假设它们都是 non-null 而无论如何您都可以使用 !! 访问它们时)