当前位置: 首页 > 知识库问答 >
问题:

LiveData不更新UI(使用ViewModel和Room数据库)

申光临
2023-03-14

因此,我使用LiveData和ViewModel设置获取和插入数据的功能,并使用Room数据库保存数据。

将数据插入数据库后,我的RecyclerView不会更新数据。

RecyclerAdapterTransaksi.kt

class RecyclerAdapterTransaksi(var context: Context, private val listener: (Transaksi) -> Unit) :
RecyclerView.Adapter<RecyclerAdapterTransaksi.TransaksiViewHolder>() {

private var listTransaksi = arrayListOf<Transaksi>()

fun setListTransaksi(listTransaksi: ArrayList<Transaksi>) {
    this.listTransaksi = listTransaksi
    notifyDataSetChanged()
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransaksiViewHolder {
    context = parent.context

    val binding = ListItemBinding.inflate(LayoutInflater.from(context), parent, false)

    return TransaksiViewHolder(binding)
}

override fun onBindViewHolder(holder: TransaksiViewHolder, position: Int) {
    holder.bindItem(listTransaksi[position], listener)
}

override fun getItemCount(): Int = listTransaksi.size

class TransaksiViewHolder(private val binding: ListItemBinding) :
    RecyclerView.ViewHolder(binding.root) {
    @SuppressLint("SimpleDateFormat")
    fun bindItem(transaksi: Transaksi, listener: (Transaksi) -> Unit) {
        binding.textViewNamaTransaksi.text = transaksi.namaTransaksi
        binding.textViewJmlTransaksi.text = transaksi.total.toString()
        binding.textViewTglTransaksi.text = SimpleDateFormat("dd MMM yyyy").format(transaksi.tglTransaksi!!)
        itemView.setOnClickListener {
            listener(transaksi)
        }

    }
}
}

使用RecyclerView显示数据库数据的片段

Pemasukkanframent。kt

class PemasukkanFragment : Fragment() {

    private var _binding: FragmentPemasukkanBinding? = null
    private val binding get() = _binding!!

    private val viewModel: TransaksiViewModel by lazy {
        val dataSource = UwangkuDatabase.getInstance(requireContext()).transactionDao
        val factory = TransaksiViewModelFactory(dataSource)
        ViewModelProvider(this, factory).get(TransaksiViewModel::class.java)
    }


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        _binding = FragmentPemasukkanBinding.inflate(inflater, container, false)

        binding.fabPemasukkan.setOnClickListener {
            val intent = Intent(requireContext(), TambahDataActivity::class.java)
            intent.putExtra(Constant.TIPE_TRANSAKSI, Constant.PEMASUKKAN)
            intent.putExtra(Constant.TIPE_AKSI, Constant.AKSI_TAMBAH)
            startActivity(intent)
        }
        getDataPemasukkan()
        return binding.root
    }


    private fun getDataPemasukkan() {
        binding.recyclerViewPemasukan.setHasFixedSize(true)
        binding.recyclerViewPemasukan.layoutManager = LinearLayoutManager(requireContext())

        val adapter = RecyclerAdapterTransaksi(requireContext()) {
            //action when item clicked
           showKonfirmasiEdit(it)
        }
        binding.recyclerViewPemasukan.adapter = adapter

        viewModel.getData(Date(), Constant.PEMASUKKAN)
        viewModel.dataTransaksi.observe(viewLifecycleOwner, Observer {
            adapter.setListTransaksi(it as ArrayList<Transaksi>)
        })
    }

    private fun showKonfirmasiEdit(transaksi: Transaksi){
        val intent = Intent(requireContext(),TambahDataActivity::class.java)

        val builder = AlertDialog.Builder(requireContext())
            .setMessage(R.string.edit_message)
            .setPositiveButton(R.string.edit){_,_ ->
                intent.putExtra(Constant.DATA_PEMASUKKAN, transaksi)
                intent.putExtra(Constant.TIPE_TRANSAKSI, Constant.PEMASUKKAN)
                intent.putExtra(Constant.TIPE_AKSI, Constant.AKSI_EDIT)
                startActivity(intent)
            }
            .setNegativeButton(R.string.batal){dialog, _ ->
                dialog.cancel()
            }
        builder.show()
    }

}

使用LiveData的ViewModel类

TransaksiViewModel.kt

class TransaksiViewModel(private val dao: TransaksiDao) : ViewModel() {

    private var _dataTransaksi = MutableLiveData<List<Transaksi>>()
    val dataTransaksi: LiveData<List<Transaksi>>
        get() = _dataTransaksi

    private suspend fun getDataTransaksi(tgl : Date, tipe : String) = withContext(Dispatchers.IO) {
        dao.getData(tgl, tipe)
    }

    fun getData(tgl : Date, tipe : String) {
        viewModelScope.launch {
            _dataTransaksi.postValue(getDataTransaksi(tgl, tipe))
        }
    }

    fun inserDataTransaksi(transaksi: Transaksi) {
        viewModelScope.launch {
            insertTransaksi(transaksi)
        }
    }

    private suspend fun insertTransaksi(transaksi: Transaksi){
        withContext(Dispatchers.IO) {
            dao.insertDataTransaksi(transaksi)
        }
    }

    fun updateDataTransaksi(transaksi: Transaksi){
        viewModelScope.launch {
            updateTransaksi(transaksi)
        }
    }

    private suspend fun updateTransaksi(transaksi: Transaksi){
        withContext(Dispatchers.IO){
            dao.updateDataTransaksi(transaksi)
        }
    }
}

DAO

TransakiDao.kt

@Dao
interface TransaksiDao {
    @Insert
    fun insertDataTransaksi(transaksi: Transaksi)

    @Query("SELECT * FROM transaksi WHERE datetime(tanggal/1000,'unixepoch','start of month') = datetime(:tgl/1000,'unixepoch','start of month') AND jenis = :tipe ORDER BY id DESC")
    fun getData(tgl : Date, tipe : String): List<Transaksi>

    @Update
    fun updateDataTransaksi(transaksi: Transaksi)
}

将数据插入数据库的活动

TambahDataActivity.kt

class TambahDataActivity : AppCompatActivity() {

    private lateinit var binding: ActivityTambahDataBinding

    private val viewModel: TransaksiViewModel by lazy {
        val dataSource = UwangkuDatabase.getInstance(this).transactionDao
        val factory = TransaksiViewModelFactory(dataSource)
        ViewModelProvider(this, factory).get(TransaksiViewModel::class.java)
    }

    @SuppressLint("SimpleDateFormat")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityTambahDataBinding.inflate(layoutInflater)
        setContentView(binding.root)
        supportActionBar!!.setDisplayHomeAsUpEnabled(true)

        val getAksi = intent.getStringExtra(Constant.TIPE_AKSI)
        val getJenisTransaksi = intent.getStringExtra(Constant.TIPE_TRANSAKSI)
        if(getJenisTransaksi == Constant.PEMASUKKAN){
            val dataPemasukan = intent.getParcelableExtra<Transaksi>(Constant.DATA_PEMASUKKAN)
            if(dataPemasukan != null){
                getDataFromIntent(dataPemasukan)
            }
        } else if(getJenisTransaksi == Constant.PENGELUARAN){
            val dataPengeluaran = intent.getParcelableExtra<Transaksi>(Constant.DATA_PENGELUARAN)
            if(dataPengeluaran != null){
                getDataFromIntent(dataPengeluaran)
            }
        }
        if(getAksi == Constant.AKSI_EDIT){
            binding.buttonTambah.text = getString(R.string.edit)
            title = getString(R.string.edit_data)
        }
        binding.textViewTanggal.setOnClickListener { showDatePickerDialog() }
        binding.buttonTambah.setOnClickListener {
            if(getAksi == Constant.AKSI_TAMBAH){
                insertData()
            }else if(getAksi == Constant.AKSI_EDIT){
                updateData()
            }
        }
    }

    @SuppressLint("SimpleDateFormat")
    private fun insertData(){
        val namaTransaksi = binding.editTextKeterangan.text
        val jumlahTransaki = binding.editTextJumlah.text
        val getJenisTransaksi = intent.getStringExtra(Constant.TIPE_TRANSAKSI)
        val jenisTransaksi = if (getJenisTransaksi == Constant.PEMASUKKAN) {
            Constant.PEMASUKKAN
        } else {
            Constant.PENGELUARAN
        }
        val tanggalTransaksi =
            SimpleDateFormat("dd-MM-yyyy").parse(binding.textViewTanggal.text.toString())
        val transaksi = Transaksi(
            0,
            namaTransaksi.toString(),
            jenisTransaksi,
            jumlahTransaki.toString().toInt(),
            tanggalTransaksi
        )
        viewModel.inserDataTransaksi(transaksi).apply { finish() }
    }

    @SuppressLint("SimpleDateFormat")
    private fun updateData(){
        val namaTransaksi = binding.editTextKeterangan.text
        val jumlahTransaki = binding.editTextJumlah.text
        val getJenisTransaksi = intent.getStringExtra(Constant.TIPE_TRANSAKSI)
        val jenisTransaksi = if (getJenisTransaksi == Constant.PEMASUKKAN) {
            Constant.PEMASUKKAN
        } else {
            Constant.PENGELUARAN
        }
        val tanggalTransaksi =
            SimpleDateFormat("dd-MM-yyyy").parse(binding.textViewTanggal.text.toString())
        if(getJenisTransaksi == Constant.PEMASUKKAN){
            val dataPemasukan = intent.getParcelableExtra<Transaksi>(Constant.DATA_PEMASUKKAN)!!
            val transaksi = Transaksi(dataPemasukan.id,
                namaTransaksi.toString(),
                jenisTransaksi,
                jumlahTransaki.toString().toInt(),
                tanggalTransaksi
            )
            viewModel.updateDataTransaksi(transaksi)
        } else if(getJenisTransaksi == Constant.PENGELUARAN){
            val dataPengeluaran = intent.getParcelableExtra<Transaksi>(Constant.DATA_PENGELUARAN)!!
            val transaksi = Transaksi(dataPengeluaran.id,
                namaTransaksi.toString(),
                jenisTransaksi,
                jumlahTransaki.toString().toInt(),
                tanggalTransaksi
            )
            viewModel.updateDataTransaksi(transaksi)
        }
    }

    @SuppressLint("SimpleDateFormat")
    private fun getDataFromIntent(data : Transaksi?){
        if(data != null){
            binding.editTextKeterangan.setText(data.namaTransaksi)
            binding.editTextJumlah.setText(data.total.toString())
            binding.textViewTanggal.text = SimpleDateFormat("dd-MM-yyyy").format(data.tglTransaksi!!)
        }

    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> finish()
        }
        return true
    }

    @SuppressLint("SetTextI18n")
    private fun showDatePickerDialog() {
        val calendar = Calendar.getInstance()
        val tahun = calendar.get(Calendar.YEAR)
        val bulan = calendar.get(Calendar.MONTH)
        val hari = calendar.get(Calendar.DAY_OF_MONTH)
        val datePicker = DatePickerDialog(
            this,
            DatePickerDialog.OnDateSetListener { view, _year, monthOfYear, dayOfMonth ->
                binding.textViewTanggal.text = "$dayOfMonth-${monthOfYear + 1}-$_year "
            },
            tahun,
            bulan,
            hari
        )
        datePicker.show()
    }

我不知道这有什么问题,我被困了两天。

提前谢谢。

共有1个答案

井洲
2023-03-14

如果使用单例实现,则需要检查数据库实例。我面临着同样的问题,然后我发现了错误,在我的代码和我的解决方案是改变单从下面的代码

companion object {
    private var INSTANCE: PokemonDatabase? = null

    fun getDatabase(context: Context): PokemonDatabase {
        return INSTANCE ?: Room.databaseBuilder(
            context.applicationContext,
            PokemonDatabase::class.java,
            "pokemon_db"
        ).build()
    }

`

到下面的代码

companion object {
    @Volatile
    private var INSTANCE: PokemonDatabase? = null
    fun getDatabase(context: Context): PokemonDatabase {
        return INSTANCE ?: synchronized(this) {
            val instance = Room.databaseBuilder(
                context.applicationContext,
                PokemonDatabase::class.java,
                "pokemon_db"
            ).build()
            INSTANCE = instance
            instance//this return instance
        }
    }
}
 类似资料:
  • 我试图解决一个问题,但没有成功。每当数据库(数据库室)中特定模型的记录发生变化时,我想更新我的回收器视图。我使用ViewModel来处理模型数据,记录列表存储在LiveData中。 数据库 模型 刀 视图模型 碎片 现在,我的问题是为什么观察者只被调用一次(在片段的开始),然后没有被再次调用?我如何让观察者不断地倾听数据库中的变化(插入,更新,删除),以便我的回收器视图可以立即更新?非常感谢任何建

  • 活动可以按以下方式访问此列表: 我的问题是,我要这样做: > 在函数中,我异步获取数据,首先检查数据库(房间)中的数据

  • 我试图在下面的代码中找出,为什么当我用新数据填充数据库时,Room的LiveData observable没有给我新的班次。 这是放在我的activity的onCreate方法上的: 这是populateAdapter方法: 我还有以下填充数据库的代码(我使用RxJava在IO线程上完成这项工作,因为Room需要在主线程之外调用它的代码): 当我开始观察ShiftsViewModel之后调用per

  • 我有一个ViewModel,我正在使用LiveData,所以我有一个返回LiveData>的DAO,我可以让它工作,但实际上我希望它首先显示来自Room数据库的数据(如果有的话),然后当webservice返回新数据(如果有的话)时,将其写入数据库,然后用数据库中的最新数据更新ViewModel。我首先从数据库返回数据,并在后台将新数据写入数据库,但如何让ViewModel再次使用数据库中的新数据

  • UserViewModel: 用户存储库:

  • 我的应用程序使用MVVM架构。我有一个活动及其子片段共享的ViewModel。ViewModel包含一个简单的字符串,我想从活动中更新它,并在片段中观察它。 我的问题很简单:LiveData更新后,我的片段中永远不会到达observe回调。为了进行测试,我尝试在中观察数据,但效果很好。此外,在我在其他ViewModels中声明的片段中观察LiveData变量也可以很好地工作。奇怪的是,只有这个Vi