I use navigation component for navigating between my fragments. My app is simple. It has two fragmentsm the first one is list of items and the second one shows details of that Item. When user click on an item, I call
view.findNavController()
.navigate(R.id.action_photosFragment_to_photoDetailsFragment, bundle)
but problem is when I press back button, the first fragment reloads and make a network call again.
override fun onActivityCreated(savedInstanceState: Bundle?) {
photosViewModel.getPhotos()
photosViewModel.photosLiveData.observe(viewLifecycleOwner, photosObserver)
photosViewModel.loadingLiveData.observe(viewLifecycleOwner, loadingObserver)
super.onActivityCreated(savedInstanceState)
}
this block of code calls again! How can I stop reloading?
Use onCreate() in your fragment to call the network:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
photosViewModel.getPhotos()
}
Related
I want to implement a single activity project.
After showing splash i want to navigate to HomeFragment or LoginFragment based on user's login condition. In the MainActivity :
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (userLoggedIn()) {
navigateToHomeScreen()
} else {
navigateToLoginScreen()
}
}
The question is how should i set the navigation xml? Which one of the fragments should be the startDestionation. What's the best approach for a smooth splash/login( sign-in/sign-up/forget-pass)/ home flow.
I'm trying to develop an app where I have spinner in fragment(first fragment), to update spinner options user has to open a dialog window(second fragment) and put input in there. Data is passed through interface as a bundle and then into first fragment arguments. My spinner is in first fragment view so I couldn't figure out how to call a first argument view function from second argument, instead inside first argument view I constantly check if the arguments are updated, something like this
if(!displayedText.isNullOrEmpty()) {
updateListSpinner()
arguments?.clear()
}
so in fragment view I constantly check if arguments were updated and then I clear them, everything works as it should but I just wonder what's better way of doing it.
You can use Fragment Result Listeners. More documentation: Communicating between fragments. So in your instance, whenever the user interacts with the dialog fragment, send a result to the first fragment, which will be registered to use the data and you can update your spinner.
Here is some working code that controls navigation in my app:
In second fragment:
open fun navigate(idOfDestination: Int, bundle: Bundle?) {
val tempBundle = bundle ?: Bundle()
tempBundle.putInt(NAVIGATION_DESTINATION, idOfDestination)
activity?.supportFragmentManager?.setFragmentResult(NAVIGATION_RESULT, tempBundle)
}
In target fragment (which in your case would be the first fragment), register the listener:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.supportFragmentManager?.setFragmentResultListener(NAVIGATION_RESULT, this, FragmentResultListener { _, bundle ->
val destination = bundle.getInt(NAVIGATION_DESTINATION)
bundle.remove(NAVIGATION_DESTINATION)
navigationHandler(bundle,destination)
})
}
private fun navigationHandler(bundle: Bundle?, idOfDestination: Int) {
navController = Navigation.findNavController(binding.root)
if (!FeatureControlManager.isDestinationAllowedToGo(idOfDestination)) {
navController = childFragmentManager.fragments.first().findNavController()
}
navController.validateAndNavigate(navController, idOfDestination, childFragmentManager, bundle)
}
I'm using the DrawerLayout with fragments inside and every fragment navigates to another fragment. I was able to handle the physical back button using :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
findNavController().navigate(R.id.action_user_validation_to_make_money);
}
}
But every time that I go inside another fragment, the toolbar shows a back button :
I would like to know how can I handle that back button. Thanks!
To navigate using up button, as mentioned in the official docs, override onSupportNavigateUp() in your activity class
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(drawerLayout)
}
I've done my due diligence and checked all the answers on SO around that, incorporated suggestions but for some reason none of the methods for save and restore are getting called.
An answer suggested that I should have save at the activity level in order for the fragment save state to be called, so here's my activity
class MyActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.my_layout)
supportActionBar?.title = "My App"
}
// not called
override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {
super.onSaveInstanceState(outState, outPersistentState)
}
// not called
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
}
// not called
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
}
}
My Fragment is as follows.
class MyFragment: Fragment() {
// savedInstanceState is null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
// savedInstanceState is null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
}
// not getting called.
override fun onSaveInstanceState(bundle: Bundle) {
super.onSaveInstanceState(bundle)
bundle.putSerializable("users", arrayListOf()) // user array
}
}
So not sure what am I missing here for the state to be saved and restored.
How am I testing? I press back button to home screen to push the app in the background and then bring it to the foreground.
In Android, onSaveInstanceState() will be called by the system to save the current state of the activity to make sure when users resume the app they will see the activity when they left before (by calling onRestoreInstanceState() to restore the state of activity). It will be called in the following conditions:
Users move from an activity to another activity
Users press the Home key to bring the app to the background
When configuration changed, like users rotate the phone or change language, etc.
In your case, because you press the Back key, so the system knows that you want to leave the activity, so no need to save the state of that activity, that why you never see onSaveInstanceState() and onRestoreInstanceState() get called.
I want to use CastStateListener in a fragment to check if casting devices are available or not.
Code used in Fragment is
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mCastContext = CastContext.getSharedInstance(mContext)
mCastStateListener = CastStateListener { newState ->
if (newState != CastState.NO_DEVICES_AVAILABLE) {
castDevicesAvailable = true
}
}
}
override fun onResume() {
super.onResume()
mCastContext?.addCastStateListener(mCastStateListener)
}
override fun onPause() {
super.onPause()
mCastContext?.removeCastStateListener(mCastStateListener)
}
This code does not give me a call back inside CastListner when used in Fragment but it works fine when I use it in an Activity or Fragment.
I am using custom view
<androidx.mediarouter.app.MediaRouteButton
android:id="#+id/media_route_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:actionButtonStyle="#android:style/Widget.Holo.Light.MediaRouteButton"/>
I want to hide/show the view based on cast devices available
I don't think it is a good idea to use CastStateListener in a fragment. Because an activity can host multiple fragments. When a activity is paused, all fragments in it are paused, so are resumed. In the code, if you add CastStateListener in fragment onResume and remove CastStateListener in fragment onPause. If the activity hosts multiple fragments, it is very easy to mess up the add/remove CastStateListener. So I think it is better to add/remove the CastStateListener to the activity life cycle