I wonder what's the idiomatic way to pass datas from fragments back to its container activity?
Consider this sign up activity:
class SignUpActivity : AppCompatActivity() {
lateinit var uiBinding: ActivitySignUpBinding
lateinit var btnNext: MaterialButton
lateinit var currentFragment: Fragment
var fragmentIdx: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
uiBinding = ActivitySignUpBinding.inflate(layoutInflater)
currentFragment = InputBasicDataFragment()
supportFragmentManager.beginTransaction().replace(R.id.fragment_signup_container,
currentFragment).commit()
btnNext = uiBinding.btnSignUpNext
val pref = SharedPrefUtil()
btnNext.setOnClickListener {
when (fragmentIdx){
0 -> {
fragmentIdx
// read all the data on InputBasicData fragment, then save them on shared pref
pref.write('FRAGMENT1_NAME', '....')
pref.write('FRAGMENT1_MAIL', '....')
currentFragment = InputAdditionalDataFragment()
}
1-> {
fragmentIdx
// read all the data on InputAdditionalData fragment, then save them on shared pref as well
pref.write('FRAGMENT2_HOME_ADDRESS', '....')
pref.write('FRAGMENT2_JOB', '....')
pref.write('FRAGMENT2_WORK_ADDRESS', '....')
currentFragment = PreviewDataFragment()
}
2 -> {
// done. ready to upload data that are stored in shared pref
// let's hit the API ...
intent iii = Intent(this@SignUpActivity, NextActivity::class.java)
startActivity(iii)
}
}
supportFragmentManager.beginTransaction().replace(R.id.fragment_signup_container,
currentFragment).commit()
}
setContentView(uiBinding.root)
}
}
There's no button on those 3 fragments, basically only TextView and EditText. The Button is located on the container activity. First the fragment container loads InputBasicDataFragment
. When the button is clicked, how to access all the inputs so can be saved on shared pref?
CodePudding user response:
The typical pattern I’ve seen in the Jetpack libraries and at least once in the documentation about Fragments is for the Fragment to supply a callback interface that the Activity can optionally implement. When the trigger action occurs in the Fragment, check if the attached activity implements the interface, and call it if it does.
class MyFragment(): Fragment(R.layout.my_layout) {
interface MyActionListener {
onSomethingReady(someData: Something)
}
// In some callback …
(activity as? MyActionListener)?.onSomethingReady(something)
In your use case though, your activity’s functionality is closely tied to the fragments and just wants to get the current values of their UI controls. I think in this case it makes more sense for the Activity to get the fragment instances from the FragmentManager and read from them. Otherwise you will have to put TextWatchers on all the EditTexts, and pass the updated text every time a character is typed for the Activity to cache in a property. It’s kind of a high price for separation of concerns in this case.
CodePudding user response:
I suggest using a ViewModel
.
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.
see https://developer.android.com/topic/libraries/architecture/viewmodel.
Then the extension to a standard view model is that of an shared view model where a single instance can be shared between multiple Fragments and their Activity.
https://developer.android.com/topic/libraries/architecture/viewmodel#sharing
This would allow for the fragments to update some value inside the view model, and the activity could react to changes to those variables.
There are many other well documented benefits of using viewModels.