簡體   English   中英

在 ViewModel 中遍歷 LiveData 列表

[英]Traversing a LiveData List in the ViewModel

我是一名菜鳥 Android 開發人員,可以使用一些關於在 ViewModel 中遍歷 LiveData 列表的指導。

我的應用程序基於 MVVM 設計,它只是掃描文件夾中的圖像,並將一些文件夾添加到我存儲在數據庫中的收藏夾列表中。 在掃描期間,我需要檢查存儲的收藏夾以查看是否有任何掃描的文件夾是收藏夾。

給我帶來麻煩的是“檢查存儲的收藏夾”部分。

以下是我的片段中的相關位:

class FoldersFragment : Fragment(), KodeinAware {

    override val kodein by kodein()
    private val factory: FoldersViewModelFactory by instance()

    private var _binding: FragmentFoldersBinding? = null
    private val binding get() = _binding!!
    private lateinit var viewModel: FoldersViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        _binding = FragmentFoldersBinding.inflate(inflater, container, false)
        val root: View = binding.root
        return root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel = ViewModelProvider(this, factory).get(FoldersViewModel::class.java)
        binding.rvFolderList.layoutManager = GridLayoutManager(context, gridColumns)
        val adapter = FolderItemAdapter(listOf(), viewModel)
        binding.rvFolderList.adapter = adapter

        viewModel.getFolderList().observe(viewLifecycleOwner, {
            adapter.folderItems = it
            binding.rvFolderList.adapter = adapter // Forces redrawing of the recyclerview
        })
        ...
    }

現在,那個觀察者工作得很好——它接受了變化,我的 RecyclerView 高興地響應; 一切都很好。

以下是我的 RecyclerView 適配器中的相關位:

class FolderItemAdapter(var folderItems: List<FolderItem>, private val viewModel: FoldersViewModel):
    RecyclerView.Adapter<FolderItemAdapter.FolderViewHolder>() {

    private lateinit var binding: FolderItemBinding

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder {
        binding = FolderItemBinding.inflate(LayoutInflater.from(parent.context))
        val view = binding.root
        return FolderViewHolder(view)
    }

    override fun onBindViewHolder(holder: FolderViewHolder, position: Int) {
        val currentItem = folderItems[position]
        ...
        if (viewModel.isFavourite(currentItem)) {
            // do stuff
        }
        ...
    }
}

有了這個,我的問題; 檢查viewModel.isFavourite(currentItem)總是返回 false。 我的 ViewModel 中的實現是:

class FoldersViewModel(private val repository: FoldersRepository) : ViewModel() {

   fun getImageFolders() = repository.getImageFolders()

   fun isFavourite(item: FolderItem): Boolean {
        var retval = false
        getImageFolders().value?.forEach {
            if (it.path == item.path) {
                retval = true
            }
        }
    }
}

`getImageFolders() function 直接來自存儲庫,同樣來自 Dao:

@Dao
interface FoldersDao {
    @Query("SELECT * FROM image_folders")
    fun getImageFolders(): LiveData<List<FolderItem>>
}

我的問題是我根本無法遍歷 ViewModel 中的收藏夾列表。 isFavourite(item: FolderItem) function 總是返回 false,因為getImageFolders().value總是 null。 當我檢查getImageFolders()它是androidx.room.RoomTrackingLiveData@d0d6d31

和難題; 觀察者正在做同樣的事情嗎? 或者不是嗎?

我懷疑我在這里不懂一些基本的東西?

你的 getImageFolder() 是一個昂貴的 function 所以

getImageFolders().value?.forEach {
        if (it.path == item.path) {
            retval = true
        }
    }

在這部分中,值仍然是 null 這就是它返回 false 的原因。 解決方案是確保該值不是 null。 不要檢查 null 里面的 isFavorite function 而是調用 isFavorite() function 只有當 getImageFolder() 完成操作。

你應該做的是這樣的

observe the liveData of imageFolders 
ondatachange check if the data is null or not
if it is not null update UI and use isFavourite() function

您的getImageFolders() function 從數據庫中異步檢索某些內容,因為您指定它返回 LiveData。 當您取回 LiveData 時,它不會立即具有可用的值。 這就是為什么你的.value?.forEach永遠不會被調用的原因。 value仍然是 null 因為您正試圖立即讀取它。 LiveData 旨在被觀察以在它到達時獲取值。

有多種方法可以讓 DAO function 返回一些東西而不阻塞當前線程。 這里是方便的表格。 )返回 LiveData 是一種方法,但如果您只想要一個值返回,那么使用它會很尷尬。 相反,您應該使用鏈接表中One-shot read行中的內容。

如果您不使用 RxJava 或 Guava 庫,那么 Kotlin 協程掛起 function 作為自然選擇。

這將使你的道看起來像:

@Dao
interface FoldersDao {
    @Query("SELECT * FROM image_folders")
    suspend fun getImageFolders(): List<FolderItem>
}

然后您的 ViewModel function 看起來像:

suspend fun isFavourite(item: FolderItem): Boolean {
    return getImageFolders().any { it.path == item.path }
}

請注意,由於它是暫停 function,因此只能從協程調用。 這是避免阻塞主線程所必需的。 如果你還沒有准備好學習協程,你可以用回調類型 function 替換這個 function ,如下所示:

fun isFavoriteAsync(item: FolderItem, callback: (Boolean)->Unit) {
    viewModelScope.launch {
        val isFavorite = getImageFolders().any { it.path == item.path }
        callback(isFavorite)
    }
}

並在呼叫站點像這樣使用它

viewModel.isFavoriteAsync(myFolderItem) { isFavorite ->
    // do something with return value when it's ready here
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM