Search code examples
androidkotlinandroid-viewmodel

How to change value of textview in activity A after clicking on button which is present in activity B in android?


I have two activities in my app. In Activity A (MainActivity.kt) I have two buttons named buttonGo and buttonCounter. If I click on buttonGo it will go to another activity B (Second.kt). What I want to achieve is, everytime I click on buttonCounter the textCounter(Textview) present in activity B should increment by 1. Currently, no increment takes place.

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var counterViewModel: CounterViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        counterViewModel = ViewModelProvider(this)[CounterViewModel::class.java]

        binding.buttonGo.setOnClickListener {
            val intent = Intent(this@MainActivity, Second::class.java)
            startActivity(intent)
        }

        binding.buttonCounter.setOnClickListener {
            counterViewModel.incrementCounter()
        }
    }
}

Second.kt

class Second:AppCompatActivity() {
    private lateinit var  counterViewModel: CounterViewModel
    private lateinit var binding: SecondBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = SecondBinding.inflate(layoutInflater)
        setContentView(binding.root)
        counterViewModel = ViewModelProvider(this)[CounterViewModel::class.java]

        binding.buttonBack.setOnClickListener {
            onBackPressed()
        }

        counterViewModel.counter.observe(this, Observer {count ->
            binding.textCounter.text = count.toString()
        })
    }
}

Below is my viewModel named CounterViewModel.kt

class CounterViewModel : ViewModel() {
    private val _counter = MutableLiveData<Int>(0)
    val counter: LiveData<Int> get() = _counter

    fun incrementCounter() {
        _counter.value = (_counter.value ?: 0) + 1
    }
}

This is the layout file of MainActivity.kt

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button_Go"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Go"
        app:layout_constraintBottom_toTopOf="@+id/button_Counter"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed" />

    <Button
        android:id="@+id/button_Counter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Counter"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button_Go" />

</androidx.constraintlayout.widget.ConstraintLayout>

This is my layout file for Second.kt

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textCounter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        android:textColor="@android:color/black"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/button_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Back"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textCounter" />

</androidx.constraintlayout.widget.ConstraintLayout>

I created a Viewmodel to observe the data changes, created a function for incrementing but the textCounter is not incrementing from its initial value of 0.


Solution

  • You can't share a ViewModel across Activities. However, there are two ways to pass data between activities:

    1.Intent : You can pass data between activities using Intent. For example, you can pass the counter value from MainActivity to Second activity via Intent.

    class MainActivity : AppCompatActivity() {
        private lateinit var binding: ActivityMainBinding
        private var counter = 0
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
    
            binding.buttonGo.setOnClickListener {
                val intent = Intent(this@MainActivity, Second::class.java)
                intent.putExtra("counter", counter)
                startActivity(intent)
            }
    
            binding.buttonCounter.setOnClickListener {
                counter++
            }
        }
    }
    
    class Second : AppCompatActivity() {
        private lateinit var binding: SecondBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = SecondBinding.inflate(layoutInflater)
            setContentView(binding.root)
    
            val counter = intent.getIntExtra("counter", 0)
            binding.textCounter.text = counter.toString()
    
            binding.buttonBack.setOnClickListener {
                onBackPressed()
            }
        }
    }
    

    2.SharedPreferences : You can use SharedPreferences to hold the counter data and retrieve it whenever the activity is recreated. This method persists data even if the app is closed.

    CounterViewModel.kt

    class CounterViewModel(application: Application) : AndroidViewModel(application) {
        private val preferences = application.getSharedPreferences("counter_prefs", Context.MODE_PRIVATE)
        private val _counter = MutableLiveData<Int>()
    
        val counter: LiveData<Int> get() = _counter
    
        init {
            _counter.value = preferences.getInt("counter", 0)
        }
    
        fun incrementCounter() {
            val newValue = (_counter.value ?: 0) + 1
            _counter.value = newValue
            preferences.edit().putInt("counter", newValue).apply()
        }
    }
    

    MainActivity.kt

    class MainActivity : AppCompatActivity() {
        private lateinit var binding: ActivityMainBinding
        private lateinit var counterViewModel: CounterViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = ActivityMainBinding.inflate(layoutInflater)
            setContentView(binding.root)
    
            counterViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application))[CounterViewModel::class.java]
    
            binding.buttonGo.setOnClickListener {
                val intent = Intent(this@MainActivity, Second::class.java)
                startActivity(intent)
            }
    
            binding.buttonCounter.setOnClickListener {
                counterViewModel.incrementCounter()
            }
        }
    }
    

    Second.kt

    class Second : AppCompatActivity() {
        private lateinit var counterViewModel: CounterViewModel
        private lateinit var binding: SecondBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = SecondBinding.inflate(layoutInflater)
            setContentView(binding.root)
    
            counterViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application))[CounterViewModel::class.java]
    
            counterViewModel.counter.observe(this, Observer { count ->
                binding.textCounter.text = count.toString()
            })
    
            binding.buttonBack.setOnClickListener {
                onBackPressed()
            }
        }
    }