How to communicate from Fragment to ViewModel? - android

I currently have an interface that's implemented in the MainActivity that allows communication with the NumberFragment. The onCountClicked is the method in NumberFragment receiving the id of the item click.
Question
How can I send the onCountClicked position to the NumberFragmentViewModel every time an item is clicked?
Note: Using databinding & recycleview
Main Activity
val newFragment = NumberFragment()
val args = Bundle()
args.putInt(NumberFragment().onCountClicked(data).toString(),data.toInt())
newFragment.arguments = args
Number Fragment
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val sleepTrackerViewModel =
ViewModelProviders.of(
this, viewModelFactory).get(NumberViewModel::class.java)
setHasOptionsMenu(true)
binding.setLifecycleOwner(this)
binding.sleepTrackerViewModel = sleepTrackerViewModel
//Observes for any changes on database and updates the UI
sleepTrackerViewModel.dbCount.observe(this, Observer {dbCount ->
dbView.text = dbCount.number.toString()
})
return binding.root
}
fun onCountClicked(position: Long) {
Log.i("NumberF" , "------------->" + position)
}

You can just simply create a method in the viewModel that receives that position and updates the value of the live data. I would do it like this:
private val _idItemClicked = MutableLiveData<Int>()
val idItemClicked: LiveData<Int> = _idItemClicked
fun setIdItemClicked(id: Int) {
_idItemClicked.value = id
}
Now you call the function setIdItemClicked to set a new value.

Related

Data not visible

My Variable Class
data class QnaVariable( val questionOne: String, val answerOne : String, val questionTwo: String, val answerTwo : String)
My ViewModel Class
val item = QnaVariable("I am unable to login my account",
"You can reset password using the Reset Password in My account Page. If you are still unable to access your account, then please call our customer care.",
"Still need help?",
"Have a queries? please get in touch and we will be happy to help you")
}
My Fragment Class
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentQuestionAnswerBinding.inflate(layoutInflater, container, false)
val model = viewModel.item
//binding.questionHeading1.text = model.toString()
binding.questionHeading1.text = QNAdapter(myList).toString()
return binding.root
}
}
return FragmentQuestionAnswerBinding.inflate(layoutInflater, container, false).apply {
binding = this
//TODO: write code to update your ui
}.root
Modify binding part like this.

Adding an Observer in a Tabbed Activity

I have an app which creates "tasks" and creates a countdown for each one.
I have a view model with a list, and it's observable via LiveData
val tasksList = mutableListOf<Task>()
private val _tasksListData = MutableLiveData(tasksList)
val tasksListData : LiveData<MutableList<Task>>
get() = _tasksListData
fun addNewTask(task : Task){
tasksList.add(task)
_tasksListData.value = tasksList
}
I have already check that the items are created via a log statement. So that's working alright.
Then in the fragment I'm observing this live data and trying to add dynamically each task.
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_history,
container,
false
)
taskListContainer = binding.tasksListContainer
tasksViewModel = ViewModelProvider(requireActivity()).get(TasksViewModel::class.java)
//Checks if the list has some items, otherwise displays a message
checkTasksList()
tasksViewModel.tasksListData.observe(viewLifecycleOwner,{
for (item in it) {
val view: IndividualTaskViewBinding = DataBindingUtil.inflate(
inflater, R.layout.individual_task_view, container, false
)
view.taskTitle.text = item.name
view.taskDateCreated.text = item.dateCreated
view.taskTertiaryText.text = item.cyclesCompleted.toString()
taskListContainer.addView(view.root)
}
checkTasksList()
})
setHasOptionsMenu(true)
return binding.root
}
private fun checkTasksList(){
if(taskListContainer.childCount == 0 ){
binding.emptyListText.setVisibility(View.VISIBLE)
} else{
binding.emptyListText.setVisibility(View.GONE)
}
}
}
The problem is that onCreateView() is called just once, and then I can't find a way to Observe the LiveData again. I have checked with Log statements, but while I'm swapping tabs (meaning I'm swapping fragments), I can't get any method from the Fragment's lifecycle.
The entire project is here: https://github.com/arieldipietro/PomodoroTechnique

Android recyclerview not updating when removing an item

My recyclerview doesn't udpate with the livedata when I remove an
item.
The function jokeisclicked() opens up a dialog for the user which
can choose to edit or delete an item of the room database.
The delete completes, but only when I refresh the tab. How can I complete the delete without refreshing the tab?
class DashboardFragment : Fragment() {
private lateinit var dashboardViewModel: DashboardViewModel
lateinit var binding: FragmentDashboardBinding
lateinit var adapter: JokeRecyclerViewAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//init repo and viewmodel
val dao: JokeDao = RoomDB.getInstance(requireContext()).JokeDAO
val jokerepo = JokeRepo(dao, RetrofitBuilder.jokeservice)
val factory = DashboardViewModelFactory(jokerepo)
dashboardViewModel = ViewModelProvider(this, factory).get(DashboardViewModel::class.java)
//init binding
binding = FragmentDashboardBinding.inflate(inflater, container, false)
binding.lifecycleOwner = this
//recycler observe from livedata
dashboardViewModel.jokes.observe(viewLifecycleOwner, {
binding.recyclerview.layoutManager = LinearLayoutManager(this.requireContext())
adapter = JokeRecyclerViewAdapter(it, { selected: Joke -> jokeIsClicked(selected) })
binding.recyclerview.adapter = adapter
})
return binding.root
}
fun jokeIsClicked(joke: Joke) {
//show pop up dialog
val dialog = PopUpFragment(joke)
getFragmentManager()?.let { dialog.show(it, "popUpDialog") }
}
}
here is the popupfragment class
class PopUpFragment(private val selectedJoke : Joke) : DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
//init viewmodel
val dao = RoomDB.getInstance(this.requireContext()).JokeDAO
val jokerepo = JokeRepo(dao,RetrofitBuilder.jokeservice)
val factory = PopUpViewModelFactory(jokerepo)
val popupViewModel : PopUpFragmentViewModel = ViewModelProvider(this,factory).get(PopUpFragmentViewModel::class.java)
//init binding
val binding = FragmentPopUpBinding.inflate(inflater,container,false)
//edit clicked
binding.editjoke.setOnClickListener{
}
//delete clicked
binding.deletejoke.setOnClickListener {
//TODO
popupViewModel.deleteJoke(selectedJoke)
this.dismiss()
}
return binding.root
}
}
I think you dont update value of livedata at viewModel. Could you share viewmodles also ? If dialogViewModel removes joke from database than you have to return at #Dao's method LiveData or Flow.
Any way don't set adapert and layout manager at observe method - it's point less. For updating list at adaper you could create method and call in it notifiDataSetChanged() or (the best way of handling changes at adaper) diffUtils.
You'll need to do 2 things:
1 - Remove the data from your variable i.e if you're storying it within a an array then use array = ArrayUtils.remove(array, index);
2 - run .notifyDataSetChanged() on your RecyclerView's Adapter in order refresh the view based on the values of the new data set.

Kotlin android data transfer from fragment to dialogFragment

I want to pass some data from Fragment to a DialogFragment (when I click a view by using onClickListener), but the data has empty values in Dialog.
While debugging I found that VO data has no problem. (log comments in my code works correctly)
So, I think that I am not using Bundle correctly.
What can I do to solve this problem?
AccountFragment.class (recyclerview bindViewHolder)
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val accountVO = list[position]
val viewHolder = holder as AccountViewHolder
viewHolder.text_account_title.text = accountVO.title
viewHolder.text_account_bank.text = accountVO.bank
viewHolder.text_account_account.text = accountVO.account
viewHolder.text_account_name.text = accountVO.name
viewHolder.text_account.setOnClickListener() {
// log
// Toast.makeText(context, "${accountVO.title}, ${accountVO.content}", Toast.LENGTH_SHORT).show()
val accountFragment = AccountFragment()
val bundle = Bundle()
bundle.putString("title", accountVO.title)
bundle.putString("content", accountVO.content)
accountFragment.arguments = bundle
AccountDetailDialogFragment().show(activity?.supportFragmentManager as FragmentManager, "dialog_event")
}
}
AccountDetailDialogFragment
class AccountDetailDialogFragment : DialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.activity_account_detail_dialog_fragment, container, false)
view.text_account_detail_title.text = arguments?.getString("title")
view.text_account_detail_content.text = arguments?.getString("content")
isCancelable = false
return view
}
}
You didn't actually set the arguments on your AccountDetailDialogFragment, you set it on your accountFragment (which you didn't even use):
val bundle = Bundle()
bundle.putString("title", accountVO.title)
bundle.putString("content", accountVO.content)
val dialogFragment = AccountDetailDialogFragment()
dialogFragment.arguments = bundle
dialogFragment.show(requireActivity().supportFragmentManager, "dialog_event")
Note that you should make sure your AccountDetailDialogFragment uses the right import for its superclass (you shouldn't need to cast the supportFragmentManager).
try to get this inot onviewCreated
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.text_account_detail_title.text = arguments?.getString("title")
view.text_account_detail_content.text = arguments?.getString("content")
}
Or if its not working than you can try using instance it's a good way to transfer data
change this
AccountDetailDialogFragment().show(activity?.supportFragmentManager as FragmentManager, "dialog_event")
to
accountFragment.show(activity?.supportFragmentManager as FragmentManager, "dialog_event")
this will work :)

Pass data between fragments in kotlin android studio

I want to pass some data from PowerofMind to wishlist fragment but I encountered with some error.
This Activity from where data has to be transferred
wish?.setOnClickListener({
val name = "Power of Subconcoius Mind"
val intent = Intent(this#PowerofMind, WishlistFragment::class.java)
intent.putExtra("Book: ", name)
startActivity(intent)
Toast.makeText(this, "Added to WishList", Toast.LENGTH_SHORT).show()
})
I want to show data in this activity as
class WishlistFragment : Fragment() {
var result: TextView? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_wishlist, null)
result =view.findViewById(R.id.list1)
val name = activity?.intent?.getStringExtra("Book: ")
list1.text = "Book: $name"
return view
}
}
But there is an error on Intent. Please Help
Here is an example how to instantiate a fragment with factory method:
companion object {
private const val MY_DATA_KEY = "my_data"
private const val ANOTHER_DATA_KEY = "another_data"
fun newInstance(mySerializableData: Any, anotherData: Int) = MyFragment().apply {
//bundleOf() is an exstension method from KTX https://developer.android.com/kotlin/ktx
arguments = bundleOf(MY_DATA_KEY to mySerializableData, ANOTHER_DATA_KEY to anotherData)
}
}
Here is how you can pass data between fragment in kotlin using Parcelable class:
on Button Click:
override fun onClick(v: View?) {
firstName = editTextName!!.text.toString()
lastName = editTextLast!!.text.toString()
Toast.makeText(context, firstName, Toast.LENGTH_SHORT).show()
// val viewFragment = ViewFragment()
// val transaction = fragmentManager.beginTransaction()
// transaction.replace(R.id.fragmentContainer, viewFragment)
// transaction.commit()
var details = Details(firstName!!, lastName!!)
val viewFragment = ViewFragment()
val bundle = Bundle()
bundle.putParcelable(KEY_PARSE_DATA, details)
viewFragment.setArguments(bundle)
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.fragmentContainer, viewFragment)
transaction.commit()
}
Here is an parcel class how to handle data
#Parcelize
class Details(val firstName: String, val lastName: String) : Parcelable
on another fragment
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view: View = inflater!!.inflate(R.layout.fragment_view, container, false)
textViewName = view.findViewById(R.id.text_name_another) as TextView
textViewLastName = view.findViewById(R.id.text_surname_another) as TextView
val bundle = arguments
if (bundle != null) {
val details = bundle.getParcelable<Details>(KEY_PARSE_DATA)
textViewName!!.setText(details.firstName)
textViewLastName!!.setText(details.lastName)
}
return view
}
Currently I don't know if this required or not in kotlin in app gradle (Check this before used)
androidExtensions {
experimental = true
}
In your WishlistFragment you are creating a new Intent instead of getting the one provided by the activity.
As you are using Kotlin, you can directly use intent instead of getIntent().
You could use also the synthetic advantage of kotlin, and drop that findViewById.
And as a tip, do not use string concatenation ;)
Your fragment would look something like the following:
class WishlistFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_wishlist, null)
val name = activity?.intent?.getStringExtra("Book: ")
many.text = "Book: $name"
}

Categories

Resources