I am currently facing an issue with the radio buttons in my app. My app comprises Information Forms which include Radio buttons with other Views and there are multiple forms. When I select option 1 of the first and second radio buttons and select option 2 of the last radio button in my fragment and navigate to another fragment and come back to my previous fragment all the radio buttons have option 2 selected. No matter what I choose as an option for radio buttons, whatever the last radio option is selected all the radio buttons have that option selected. This happens when I navigate to another fragment and come back to the previous fragment. I want to show the correct options selected when I navigate back to the previous fragment.
I am using Hilt, DataBinding, ViewBinding, MVVM, HiltNavGraph
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="tvText"
type="String" />
<variable
name="option1Text"
type="String" />
<variable
name="option2Text"
type="String" />
<variable
name="selectedOption"
type="Integer" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/layout_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{tvText}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Some Text"/>
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
app:layout_constraintTop_toBottomOf="@id/layout_textview">
<RadioButton
android:id="@+id/rb_option1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:checked="@{selectedOption == @id/rb_option1 ? true : false}"
android:text="@{option1Text}"
tools:text="Option 1" />
<RadioButton
android:id="@+id/rb_option2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="@{selectedOption == @id/rb_option2 ? true : false}"
android:text="@{option2Text}"
tools:text="Option 2" />
</RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="fragment"
type="com.yousuf.demo.radio.SomeFragment" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".radio.SomeFragment"
android:gravity="center"
android:orientation="vertical"
android:padding="20dp">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Go Back"
android:onClick="@{() -> fragment.goBack()}" />
</LinearLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="viewModel"
type="com.yousuf.demo.radio.RadioViewModel" />
<variable
name="fragment"
type="com.yousuf.demo.radio.RadioFragment" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp">
<include
android:id="@+id/layout_question1"
layout="@layout/layout_textview_radiogroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:option1Text="@{`Option 1`}"
app:option2Text="@{`Option 2`}"
app:selectedOption="@={viewModel.selectedQuestion1OptionId}"
app:tvText="@{`Question 1`}" />
<include
android:id="@+id/layout_question2"
layout="@layout/layout_textview_radiogroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/layout_question1"
app:option1Text="@{`Option 1`}"
app:option2Text="@{`Option 2`}"
app:selectedOption="@={viewModel.selectedQuestion2OptionId}"
app:tvText="@{`Question 2`}" />
<include
android:id="@+id/layout_question3"
layout="@layout/layout_textview_radiogroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/layout_question2"
app:option1Text="@{`Option 1`}"
app:option2Text="@{`Option 2`}"
app:selectedOption="@={viewModel.selectedQuestion3OptionId}"
app:tvText="@{`Question 3`}" />
<Button
android:id="@+id/btn_next"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> fragment.navigateToSomeFragment()}"
android:text="Next"
app:layout_constraintTop_toBottomOf="@id/layout_question3"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/nav_graph"
app:startDestination="@id/radio_nav_graph">
<navigation android:id="@+id/radio_nav_graph"
app:startDestination="@id/radioFragment">
<fragment
android:id="@+id/radioFragment"
android:name="com.yousuf.demo.radio.RadioFragment"
android:label="RadioFragment">
<action
android:id="@+id/action_radioFragment_to_someFragment"
app:destination="@id/someFragment" />
</fragment>
<fragment
android:id="@+id/someFragment"
android:name="com.yousuf.demo.radio.SomeFragment"
android:label="SomeFragment" />
</navigation>
</navigation>
import android.os.Bundle
import android.view.*
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.MenuProvider
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.lifecycle.LifecycleOwner
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.appbar.MaterialToolbar
abstract class BaseFragment<Type : ViewDataBinding> : Fragment() {
private var _binding: Type? = null
protected val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
_binding = inflateLayout(inflater)
init()
return binding.root
}
abstract fun inflateLayout(inflater: LayoutInflater): Type
private fun init() {
binding.lifecycleOwner = viewLifecycleOwner
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
fun setUpToolbar(
toolbar: MaterialToolbar,
@DrawableRes toolbarHomeIcon: Int,
menuProviderCallback: MenuProvider,
viewLifecycleOwner: LifecycleOwner,
) {
toolbar.setupWithNavController(
findNavController(),
AppBarConfiguration(findNavController().graph)
)
(requireActivity() as AppCompatActivity).apply {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeAsUpIndicator(toolbarHomeIcon)
}
requireActivity().addMenuProvider(menuProviderCallback, viewLifecycleOwner)
}
}
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.yousuf.demo.BaseFragment
import com.yousuf.demo.R
import com.yousuf.demo.databinding.FragmentSomeBinding
class SomeFragment : BaseFragment<FragmentSomeBinding>() {
override fun inflateLayout(inflater: LayoutInflater): FragmentSomeBinding {
return FragmentSomeBinding.inflate(inflater)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.fragment = this@SomeFragment
}
fun goBack() {
findNavController().navigateUp()
}
}
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.hilt.navigation.fragment.hiltNavGraphViewModels
import androidx.navigation.fragment.findNavController
import com.yousuf.demo.BaseFragment
import com.yousuf.demo.R
import com.yousuf.demo.databinding.FragmentRadioBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class RadioFragment : BaseFragment<FragmentRadioBinding>() {
private val viewModel: RadioViewModel by hiltNavGraphViewModels(R.id.radio_nav_graph)
override fun inflateLayout(inflater: LayoutInflater): FragmentRadioBinding {
return FragmentRadioBinding.inflate(inflater)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = viewModel
binding.fragment = this@RadioFragment
setUpRadioClickListener()
}
private fun setUpRadioClickListener() {
with(viewModel) {
binding.layoutQuestion1.radioGroup.setOnCheckedChangeListener { radioGroup, _ ->
selectedQuestion1OptionId.value = radioGroup.checkedRadioButtonId
}
binding.layoutQuestion2.radioGroup.setOnCheckedChangeListener { radioGroup, _ ->
selectedQuestion2OptionId.value = radioGroup.checkedRadioButtonId
}
binding.layoutQuestion3.radioGroup.setOnCheckedChangeListener { radioGroup, _ ->
selectedQuestion3OptionId.value = radioGroup.checkedRadioButtonId
}
}
}
fun navigateToSomeFragment() {
findNavController().navigate(R.id.action_radioFragment_to_someFragment)
}
}
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class RadioViewModel @Inject constructor(): ViewModel() {
var selectedQuestion1OptionId = MutableLiveData(-1)
var selectedQuestion2OptionId = MutableLiveData(-1)
var selectedQuestion3OptionId = MutableLiveData(-1)
}
I found the answer by playing with it a bit
variable name="onCheckedChangedListener" type="android.widget.RadioGroup.OnCheckedChangeListener" />
android:onCheckedChanged="@{onCheckedChangedListener}" android:checkedButton="@{selectedOption}"
android:checked="@{selectedOption == @id/rb_option1 ? true : false}"
fun setQuestion1OptionId(id: Int) { selectedQuestion1OptionId.value = id } fun setQuestion2OptionId(id: Int) { selectedQuestion2OptionId.value = id } fun setQuestion3OptionId(id: Int) { selectedQuestion3OptionId.value = id }
app:onCheckedChangedListener="@{(radioGroup, id) -> viewModel.setQuestion1OptionId(id)}"
Here is my modified Code
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="tvText"
type="String" />
<variable
name="option1Text"
type="String" />
<variable
name="option2Text"
type="String" />
<variable
name="selectedOption"
type="Integer" />
<!--Modification-->
<variable
name="onCheckedChangedListener"
type="android.widget.RadioGroup.OnCheckedChangeListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/layout_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{tvText}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Some Text"/>
<!--Modification-->
<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="7dp"
android:onCheckedChanged="@{onCheckedChangedListener}"
android:checkedButton="@{selectedOption}"
app:layout_constraintTop_toBottomOf="@id/layout_textview">
<!--Modification-->
<RadioButton
android:id="@+id/rb_option1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:text="@{option1Text}"
tools:text="Option 1" />
<!--Modification-->
<RadioButton
android:id="@+id/rb_option2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{option2Text}"
tools:text="Option 2" />
</RadioGroup>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="viewModel"
type="com.yousuf.demo.radio.RadioViewModel" />
<variable
name="fragment"
type="com.yousuf.demo.radio.RadioFragment" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp">
<!--Modification-->
<include
android:id="@+id/layout_question1"
layout="@layout/layout_textview_radiogroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:onCheckedChangedListener="@{(radioGroup, id) -> viewModel.setQuestion1OptionId(id)}"
app:option1Text="@{`Option 1`}"
app:option2Text="@{`Option 2`}"
app:selectedOption="@{viewModel.selectedQuestion1OptionId}"
app:tvText="@{`Question 1`}" />
<!--Modification-->
<include
android:id="@+id/layout_question2"
layout="@layout/layout_textview_radiogroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/layout_question1"
app:onCheckedChangedListener="@{(radioGroup, id) -> viewModel.setQuestion2OptionId(id)}"
app:option1Text="@{`Option 1`}"
app:option2Text="@{`Option 2`}"
app:selectedOption="@{viewModel.selectedQuestion2OptionId}"
app:tvText="@{`Question 2`}" />
<!--Modification-->
<include
android:id="@+id/layout_question3"
layout="@layout/layout_textview_radiogroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
app:layout_constraintTop_toBottomOf="@id/layout_question2"
app:onCheckedChangedListener="@{(radioGroup, id) -> viewModel.setQuestion3OptionId(id)}"
app:option1Text="@{`Option 1`}"
app:option2Text="@{`Option 2`}"
app:selectedOption="@{viewModel.selectedQuestion3OptionId}"
app:tvText="@{`Question 3`}" />
<Button
android:id="@+id/btn_next"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> fragment.navigateToSomeFragment()}"
android:text="Next"
app:layout_constraintTop_toBottomOf="@id/layout_question3" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.hilt.navigation.fragment.hiltNavGraphViewModels
import androidx.navigation.fragment.findNavController
import com.yousuf.demo.BaseFragment
import com.yousuf.demo.R
import com.yousuf.demo.databinding.FragmentRadioBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class RadioFragment : BaseFragment<FragmentRadioBinding>() {
private val viewModel: RadioViewModel by hiltNavGraphViewModels(R.id.radio_nav_graph)
override fun inflateLayout(inflater: LayoutInflater): FragmentRadioBinding {
return FragmentRadioBinding.inflate(inflater)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = viewModel
binding.fragment = this@RadioFragment
}
fun navigateToSomeFragment() {
findNavController().navigate(R.id.action_radioFragment_to_someFragment)
}
}
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class RadioViewModel @Inject constructor(): ViewModel() {
var selectedQuestion1OptionId = MutableLiveData(-1)
var selectedQuestion2OptionId = MutableLiveData(-1)
var selectedQuestion3OptionId = MutableLiveData(-1)
// Modification
fun setQuestion1OptionId(id: Int) {
selectedQuestion1OptionId.value = id
}
fun setQuestion2OptionId(id: Int) {
selectedQuestion2OptionId.value = id
}
fun setQuestion3OptionId(id: Int) {
selectedQuestion3OptionId.value = id
}
}