kotlin - 每次应用重启时 onActivityCreated() 中的函数调用都会触发 Observer
问题描述
在我的片段中,我正在观察函数中的实时数据,并且在该观察者中,一些 sharedPreferences 已更改。然后在 onActivityCreated() 内部调用该函数。问题是,每当我重新启动我的应用程序时,都会调用 onActivityCreated() 函数,该函数又调用该函数,该函数又观察实时数据,从而更改我不想要的 sharedPreference 的值.
附上我的片段的代码。
package com.example.expensemanager.ui
import android.app.AlertDialog
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.expensemanager.R
import com.github.mikephil.charting.data.PieData
import com.github.mikephil.charting.data.PieDataSet
import com.github.mikephil.charting.data.PieEntry
import kotlinx.android.synthetic.main.fragment_transaction_list.*
import kotlinx.android.synthetic.main.set_balance_details.view.*
import org.eazegraph.lib.models.PieModel
class TransactionListFragment : Fragment() {
//declaring the view model
private lateinit var viewModel: TransactionListViewModel
var cashAmount:Float = 0F
var bankAmount:Float = 0F
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
//setHasOptionsMenu(true)
//(activity as AppCompatActivity?)!!.setSupportActionBar(addAppBar)
viewModel = ViewModelProvider(this)
.get(TransactionListViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_transaction_list, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//recycler view for showing all the transactions
with(transaction_list){
layoutManager = LinearLayoutManager(activity)
adapter = TransactionAdapter {
findNavController().navigate(
TransactionListFragmentDirections.actionTransactionListFragmentToTransactionDetailFragment(
it
)
)
}
}
//code for the floating action button in the main screen
add_transaction.setOnClickListener{
findNavController().navigate(
//here id is passed 0 because the transaction is being added the first time
TransactionListFragmentDirections.actionTransactionListFragmentToTransactionDetailFragment(
0
)
)
}
addAppBar.setOnMenuItemClickListener { menuItem ->
when(menuItem.itemId){
R.id.calendar_button -> {
findNavController().navigate(TransactionListFragmentDirections.actionTransactionListFragmentToCalanderViewFragment())
true
}
R.id.monthly_cards -> {
findNavController().navigate(TransactionListFragmentDirections.actionTransactionListFragmentToMonthlyCardsFragment())
true
}
else -> false
}
}
see_all_transactions.setOnClickListener {
findNavController().navigate(TransactionListFragmentDirections.actionTransactionListFragmentToAllTransactionsFragment())
}
//submitting the new list of upcoming Transactions after getting it from the db
viewModel.upcomingTransactions.observe(viewLifecycleOwner, Observer {
(transaction_list.adapter as TransactionAdapter).submitList(it)
})
val sharedPreferences: SharedPreferences = this.requireActivity().getSharedPreferences("OnboardingDetails", Context.MODE_PRIVATE)
val monthlyBudget = sharedPreferences.getFloat("monthlyBudget",0F)
var totalBalance = monthlyBudget*12
net_balance.text = totalBalance.toString()
Log.d("netbalance",totalBalance.toString())
//the net balance (yearly) is calculated wrt the transactions already done
viewModel.sumOfTransactions.observe(viewLifecycleOwner, Observer {
if (it != null) {
totalBalance += it
net_balance.text = totalBalance.toString()
}
})
val budgetPreferences: SharedPreferences =
this.requireActivity().getSharedPreferences("Balance_details", Context.MODE_PRIVATE)
val editor: SharedPreferences.Editor = budgetPreferences.edit()
//setting pie chart initially to 0
setPieChart(budgetPreferences,editor)
//observing the cash details and the bank details to update the text view and the pie chart
observeBalance(budgetPreferences,editor)
//GraphCardView code
//button for setting the balance details
set_balance_details.setOnClickListener {
setBalanceDetails(budgetPreferences,editor)
}
}
//dialog box for setting the balance details
private fun setBalanceDetails(budgetPreferences: SharedPreferences,editor: SharedPreferences.Editor) {
val dialog = LayoutInflater.from(requireContext()).inflate(
R.layout.set_balance_details,
null
)
//AlertDialogBuilder
val mBuilder = AlertDialog.Builder(requireContext())
.setView(dialog)
//show dialog
val mAlertDialog = mBuilder.show()
dialog.save_details.setOnClickListener {
cashAmount = dialog.cash_amount.editText?.text.toString().toFloat()
bankAmount = dialog.bank_amount.editText?.text.toString().toFloat()
//saving the cashAmount and bankAmount to shared preferences for future use
editor.putFloat("cashAmount", cashAmount).apply()
editor.putFloat("bankAmount", bankAmount).apply()
//setting the pie chart with new values
setPieChart(budgetPreferences, editor)
mAlertDialog.dismiss()
}
dialog.cancel_details.setOnClickListener { mAlertDialog.dismiss() }
mAlertDialog.show()
}
private fun observeBalance(budgetPreferences: SharedPreferences,editor: SharedPreferences.Editor) {
//getting the cashAmount and bankAmount and updating the views with live data
var cashAmount = budgetPreferences.getFloat("cashAmount", 0F)
var bankAmount = budgetPreferences.getFloat("bankAmount", 0F)
viewModel.cashAmount.observe(viewLifecycleOwner, Observer {
if (it != null) {
cashAmount += it
cash.text = "CASH : ${cashAmount}"
Log.d("observeCash",cashAmount.toString())
editor.putFloat("cashAmount",cashAmount).apply()//find solution to this
setPieChart(budgetPreferences,editor)
}
})
viewModel.bankAmount.observe(viewLifecycleOwner, Observer {
if (it != null) {
bankAmount+=it
bank.text = "BANK : ${bankAmount}"
Log.d("observeBank",bankAmount.toString())
editor.putFloat("cashAmount",cashAmount).apply()
setPieChart(budgetPreferences,editor)
}
})
setPieChart(budgetPreferences,editor)
}
//https://www.geeksforgeeks.org/how-to-add-a-pie-chart-into-an-android-application/ use this for reference
private fun setPieChart(budgetPreferences: SharedPreferences,editor: SharedPreferences.Editor) {
val cashAmount = budgetPreferences.getFloat("cashAmount", 0f)
val bankAmount = budgetPreferences.getFloat("bankAmount", 0f)
Log.d("pieCank",cashAmount.toString())
Log.d("pieBank",bankAmount.toString())
cash.text = "CASH : ${cashAmount}"
bank.text = "BANK : ${bankAmount}"
val pieEntries = arrayListOf<PieEntry>()
pieEntries.add(PieEntry(cashAmount))
pieEntries.add(PieEntry(bankAmount))
pieChart.animateXY(1000, 1000)
// setup PieChart Entries Colors
val pieDataSet = PieDataSet(pieEntries, "This is Pie Chart Label")
pieDataSet.setColors(
ContextCompat.getColor(requireActivity(), R.color.blue1),
ContextCompat.getColor(requireActivity(), R.color.blue2)
)
val pieData = PieData(pieDataSet)
// setip text in pieChart centre
//piechart.setHoleColor(R.color.teal_700)
pieChart.setHoleColor(getColorWithAlpha(Color.BLACK, 0.0f))
// hide the piechart entries tags
pieChart.legend.isEnabled = false
// now hide the description of piechart
pieChart.description.isEnabled = false
pieChart.description.text = "Expanses"
pieChart.holeRadius = 40f
// this enabled the values on each pieEntry
pieData.setDrawValues(true)
pieChart.data = pieData
}
fun getColorWithAlpha(color: Int, ratio: Float): Int {
var newColor = 0
val alpha = Math.round(Color.alpha(color) * ratio)
val r = Color.red(color)
val g = Color.green(color)
val b = Color.blue(color)
newColor = Color.argb(alpha, r, g, b)
return newColor
}
}
正如应用程序重新启动时看到的那样,viewModel.cashAmount 会被触发,给出不想要的输出。我能做些什么来避免这种情况。
解决方案
活动可以重新创建很多,例如当您旋转屏幕时,或者如果它在后台并且系统将其销毁以释放一些内存。现在,每次发生这种情况时,您的代码都不知道它是获取当前值还是全新值,但其中一个应该执行计算,另一个应该只更新显示。
问题是您的计算逻辑与 UI 状态相关联——它被告知要显示什么,并决定这是否算作新的用户操作。它无法知道这一点。您的逻辑需要类似于
things observe LiveData values -> update to display new values when they come in
user clicks a button -> do calculation with the value they've entered
calculation result returns -> LiveData gets updated with new value
LiveData value changes -> things update to show the new value
这样,计算就会专门响应用户操作,例如通过单击按钮。LiveData
观察者只反映当前状态,所以如果他们多次看到相同的值并不重要,他们只是重新绘制一个饼图或其他什么。
您可以使用 aLiveData
来查看值流,但关于 UI 组件的问题是,有时它们会在那里看到它们,有时它们不会。并且LiveData
专门用于将更新推送给活跃的观察者,而不是非活跃的观察者——并且总是向新的观察者提供最新的值,或者一个变得活跃的观察者。
因此,在这种情况下,它更像是“这是当前情况”,并且更适合显示事物,如果您重复自己也没关系。这就是为什么你不能在你的 UI 中做这种“一次处理所有事情”的事情——除非你真的在响应一个 UI 事件,比如按钮点击
推荐阅读
- sql - 查询计划突然重新编译并降低性能
- sql - 查看 SQL SP 结果集而不是输出参数
- angular - Angular 组件宿主总是有一个边框
- vuejs2 - 在 v-slot 中解构期间分配默认值
- algorithm - 如何有效地找到一个数字属于哪个范围?
- matlab - 如何反转这种色彩空间转换?
- c++ - 为什么返回 SFINAE 转换运算符不起作用?
- javascript - 根据以下使用javascript的案例在房间调度系统中搜索可用房间的最佳逻辑是什么
- c++ - 为什么每个人都按照读、写、错误的顺序处理 epoll 事件?
- date - 在 SSIS 的表达式任务中将 mmddyy 转换为 YYYY-MM-DD