android - 使用参数查询 ROOM 数据库后无法让 ViewModel 显示数据 (Kotlin)

标签 android kotlin android-recyclerview android-room android-viewmodel

首先我不得不说我对 Kotlin 相当陌生,并且在花了 5 天(35 小时以上)试图用谷歌搜索这个问题并尝试了无数不同的选项(关于堆栈溢出的类似问题,文档和教程在谷歌,GitHub 上的其他 Kotlin 项目,甚至使用我自己的服务器和数据库想知道这个问题是否与 ROOM 有关)我不得不放弃并寻求帮助,因为这个应用程序是我应该在几周。

应用程序说明(费用跟踪器):

  • 当您打开应用程序时,您会看到 HomeFragment,其中显示了最近添加的费用。
  • 有一个添加费用 fragment/选项卡,您可以在其中添加费用:写下费用名称和金额,从微调器中选择类别并从日期选择器中选择日期(默认:今天)。
  • 有一个总计 fragment/选项卡,您可以在其中查看费用的统计数据/数据。我有一个类别微调器和时间选项微调器(今天、本月、今年、所有时间),当用户单击按钮时,我正在根据所选选项构建查询,并希望根据用户的偏好显示数据下面是我的 RecyclerView。
  • 在 RecyclerView 上方你可以看到你的支出、收入和总计(总计 = 收入 - 支出,一旦我弄清楚这部分就使用 SELECT SUM 查询来获得收入和支出),RV 应该只是一个列表查询结果,用户可以通过向左滑动来删除单笔费用(非常基本的东西,已经在 HomeFragment 上运行,其中 RV 显示静态查询就很好)。

我觉得我几乎已经尝试了所有方法——尤其是 Transformations.switchMap,因为很多结果似乎都指向那个方向,但我没有取得任何进展。我浏览了 GitHub 上的数十个应用程序以查看它们的工作方式,并尝试在我的应用程序中实现逻辑,但即使在我设法调整代码以确保没有错误之后,我的 RecyclerView 上仍然没有显示任何内容.

以下是我认为与此问题相关的类的 fragment (按照从最相关到​​有点相关的顺序,省略了部分代码,以免完全淹没这篇文章):

TotalsFragment:

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.expensetracker.R
import com.example.expensetracker.model.Category
import com.example.expensetracker.model.Expense
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_totals.*
import java.util.*
import kotlin.collections.ArrayList

class TotalsFragment : Fragment() {

    private val totals: MutableList<Expense> = ArrayList()
    private val totalAdapter = ExpenseAdapterTotals(totals)
    private lateinit var viewModel: TotalsViewModel

    // 
    // Bunch of variables omitted 
    //  


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        // Initialize the ViewModel
        viewModel = ViewModelProviders.of(activity as AppCompatActivity).get(TotalsViewModel::class.java)

        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_totals, container, false)
    }

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

        updateUI()
        initViewModel()
        initViews()
        initCategorySpinner()
        initTimeSpinner()

        // For getting data and updating the UI after the button is clicked
        btn_show.setOnClickListener {
            updateRvData()
            updateTotals()
            updateUI()
        }

    }

    private fun initViewModel(){
        viewModel = ViewModelProviders.of(this).get(TotalsViewModel::class.java)

        viewModel.totals.observe(this, Observer {
            if (totals.isNotEmpty()) {
                totals.clear()
            }
            totals.addAll(it!!)

            totalAdapter.notifyDataSetChanged()
        })

    }

    private fun initViews(){
        createItemTouchHelper().attachToRecyclerView(rv_expenses_totals)
        rv_expenses_totals.apply {
            layoutManager = LinearLayoutManager(activity)
            rv_expenses_totals.adapter = totalAdapter
            rv_expenses_totals.addItemDecoration(DividerItemDecoration(this.context, DividerItemDecoration.VERTICAL))
        }
    }
// Code omitted

向前发送查询的部分: viewModel.getTotals(queryString)

TotalsViewModel:

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import com.example.expensetracker.database.ExpenseRepository
import com.example.expensetracker.model.Expense
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class TotalsViewModel(application: Application) : AndroidViewModel(application) {

    private val ioScope = CoroutineScope(Dispatchers.IO)
    private val expenseRepository = ExpenseRepository(application.applicationContext)

    var query = MutableLiveData<String>()
    val totals: LiveData<List<Expense>> = Transformations.switchMap(query, ::temp)
    private fun temp(query: String) = expenseRepository.getTotals(query)

    fun getTotals(queryString: String) = apply { query.value = queryString }


    fun insertExpense(expense: Expense) {
        ioScope.launch {
            expenseRepository.insertExpense(expense)
        }
    }

    fun deleteExpense(expense: Expense) {
        ioScope.launch {
            expenseRepository.deleteExpense(expense)
        }
    }
}

ExpenseDao:

@Dao
interface ExpenseDao {

    // sort by order they were added, newest on top
    @Query("SELECT * FROM expense_table ORDER BY id DESC LIMIT 15")
    fun getExpensesMain(): LiveData<List<Expense>>

    // get data for totals
    @Query("SELECT * FROM expense_table WHERE :queryString")
    fun getTotals(queryString: String): LiveData<List<Expense>>

// Rest of the queries omitted

费用库:

class ExpenseRepository(context: Context) {

    private var expenseDao: ExpenseDao

    init {
        val expenseRoomDatabase = ExpenseRoomDatabase.getDatabase(context)
        expenseDao = expenseRoomDatabase!!.expenseDao()
    }

    fun getExpensesMain(): LiveData<List<Expense>> {
        return expenseDao.getExpensesMain()
    }

    fun getTotals(queryString: String): LiveData<List<Expense>> {
        return expenseDao.getTotals(queryString)
    }

// Code omitted

费用室数据库:

@Database(entities = [Expense::class], version = 1, exportSchema = false)
abstract class ExpenseRoomDatabase : RoomDatabase() {

    abstract fun expenseDao(): ExpenseDao

    companion object {
        private const val DATABASE_NAME = "EXPENSE_DATABASE"

        @Volatile
        private var expenseRoomDatabaseInstance: ExpenseRoomDatabase? = null

        fun getDatabase(context: Context): ExpenseRoomDatabase? {
            if (expenseRoomDatabaseInstance == null) {
                synchronized(ExpenseRoomDatabase::class.java) {
                    if (expenseRoomDatabaseInstance == null) {
                        expenseRoomDatabaseInstance = Room.databaseBuilder(
                            context.applicationContext,
                            ExpenseRoomDatabase::class.java, DATABASE_NAME
                        ).build()
                    }
                }
            }
            return expenseRoomDatabaseInstance
        }
    }
}

ExpenseAdapterTotals:

class ExpenseAdapterTotals(private val totals: MutableList<Expense>) : RecyclerView.Adapter<ExpenseAdapterTotals.ViewHolder>() {

    lateinit var context: Context

    override fun getItemCount(): Int {
        return totals.size
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        context = parent.context
        return ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_expense_totals, parent, false))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(totals[position])
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(totals: Expense) {
            itemView.tv_expense_totals.text = totals.expense
            itemView.tv_category_totals.text = totals.category
            itemView.tv_date_totals.text = totals.date
            itemView.tv_total_totals.text = totals.total.toString()
        }
    }
}

我的应用程序 build.gradle 中有以下依赖项:

    //Navigation
    implementation "androidx.navigation:navigation-fragment-ktx:2.0.0"
    implementation "androidx.navigation:navigation-ui-ktx:2.0.0"


    // ViewModel and LiveData
    def lifecycle_version = "2.1.0"
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"

    // Room.
    def room_version = "2.1.0-rc01"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
....

因此,此代码是我最近的尝试,但已更改多次。我没有收到任何错误消息,但也没有显示任何内容。

我的目标简而言之:当我单击按钮 (btn_show) 时,它应该创建查询字符串(它确实如此)并且该 fragment 中的 RecyclerView 应该更新以显示所需的结果(它没有)。我认为问题出在 ViewModel 和 Fragment 之间,但正如我所说,我仍然是初学者,这是我第一次真正开发自己的应用程序。

提前非常感谢您提供的任何帮助和提示,如果我遗漏了您想知道的任何内容,请随时询问。

最佳答案

我注意到的几件事: 在您的总计 fragment 中,为什么要在 onCreate 和 onViewCreated 中两次初始化 View 模型?

此外,您没有将总计值提交到适配器中。 totals.addAll(it!!) 这只是将它们添加到您在 totalFragment 中声明的列表中(您根本不需要它,因为您首先从 viewmodel 获取所有总计所有。)

关于android - 使用参数查询 ROOM 数据库后无法让 ViewModel 显示数据 (Kotlin),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59445119/

相关文章:

android - 从 ListView/RecyclerView 处理逻辑显示项目详细信息的最佳方式/UX 是什么

Android 在线 PDF 加载 - 通过 Google 文档预览不可用错误

c# - 以 xamarin 形式缩放 webview 的文本

Android密码加密/解密

java - 什么是 ViewModelFactory

date - 在ListView中倒计时(Flutter)

java - 在android中使用带有multipart的HttpUrlConnection上传文件的进度非常快

android - Kotlin 和 Dagger 属于同一类吗?

android - 为什么有数据绑定(bind)的recyclerview 有时UI 更新失败?

adapter - Android RecyclerView 不会更新内容更改