LiveData Observer not triggered in Main Activity when data is changed in Fragment

57 Views Asked by At

The observer in my MainActivity is not being triggered when the string data is being changed

Here is my MainActivity:

package com.example.translator

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log

import android.widget.EditText
import android.widget.TextView
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.translator.databinding.ActivityMainBinding;
import com.example.translator.databinding.FragmentTranslateBinding
import com.example.translator.viewmodellivedata.TranslateViewModel

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewModel: TranslateViewModel


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding =ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.translateText.text = "translation.."



        viewModel = ViewModelProvider(this).get(TranslateViewModel::class.java)
        viewModel.currentWord().observe(this, Observer {
            binding.translateText.text = it.toString()
            Log.d("current", it.toString())
        })




    }



}

TranslateViewModel:

package com.example.translator.viewmodellivedata

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.LiveData

class TranslateViewModel : ViewModel() {
    private var _currentWord = MutableLiveData<String>()


    fun currentWord(): LiveData<String> {
        return _currentWord
    }

    fun setCurrentWord(word: String) {
        _currentWord.value = word
    }




}

TranslateFragment:

package com.example.translator

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.text.TextWatcher
import android.text.Editable
import android.view.View
import android.view.ViewGroup
import android.util.Log
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.translator.databinding.FragmentTranslateBinding
import com.example.translator.viewmodellivedata.TranslateViewModel


// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

/**
 * A simple [Fragment] subclass.
 * Use the [translateFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class translateFragment : Fragment() {
    // TODO: Rename and change types of parameters
    private var param1: String? = null
    private var param2: String? = null
    private var _binding: FragmentTranslateBinding? = null  // View Binding instance
    private val binding get() = _binding!!
    private lateinit var  viewModel: TranslateViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

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

        viewModel = ViewModelProvider(this).get(TranslateViewModel::class.java)


        binding.editText.hint = "Write something..."





        // Add a TextWatcher to the EditText
        binding.editText.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
                // This method is called before text is changed
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                // This method is called as text is changing
            }

            override fun afterTextChanged(s: Editable?) {
                // This method is called after text has changed
                val enteredText = s.toString()
                //binding.editText.setText(enteredText)
                //binding.translateText.text

                viewModel.setCurrentWord(enteredText)
                //viewModel.currentWord().observe(viewLifecycleOwner, Observer { word ->
                //    // This code will execute when the LiveData value changes
                ///    Log.d("current", word.toString())
               // })






                // Reattach the TextWatcher



                // You can perform translation or any other actions here with the enteredText
            }
        })


            //val view = binding.root

        //viewModel = ViewModelProvider(this).get(TranslateViewModel::class.java)
        return view
    }
    override fun onDestroyView() {
        super.onDestroyView()
        _binding = nul
l
    }

    companion object {
        /**
         * Use this factory method to create a new instance of
         * this fragment using the provided parameters.
         *
         * @param param1 Parameter 1.
         * @param param2 Parameter 2.
         * @return A new instance of fragment translateFragment.
         */
        // TODO: Rename and change types and number of parameters
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            translateFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }


I'm trying to have the TextView show what the user is typing in an fragment editview using live data observer but every time I type something in the edittext the textview does to recipreacte it and the observer is not being triggered in my MainActivity

1

There are 1 best solutions below

0
Tenfour04 On

Inside the Fragment, you need to pass requireActivity() to the ViewModelProvider constructor instead of passing this. The argument you pass is what scope the ViewModel will be loaded in, so if they don’t match, they will be distinct instances.

By the way, it is much easier to use by viewModels() and in the fragment, by activityViewModels() rather than fooling with the view model provider.