I launch a BottomSheetDialogFragment from the main fragment with the show function (navigation component can't start DialogFragments).
Now in the BottomSheetDialogFragment I have a button to move to a detail activity.
I have the BottomSheetDialogFragment defined in the graph (isolated) and it points to the detail activity.
But when I try to navigate, it can't find the navController. Is it possible to pass the navController to this isolated fragment?
MainFragment to Detail is working
DialogFragment to Detail is not working.
I tried:
- findNavController: navigation is not set
- activity.findNavController(...)
But when I try to navigate, it can't find the navController. That's how navigation-component work.
Is it possible to pass the navController to this isolated fragment? I hope you will not going with that, replacing navController is not the perfect solution for your case.
What to do?
You can have new nav.xml with new parent activity and the (isolated) fragment as child, and navigate from the BottomSheetDialogFragment to (isolated) fragment-activity.
Otherwise, I don't see any problems that prevent you from adding related fragments in one nav.xml.
Also you may need to obey for navigation-component contract, don't use show() function while using navigation-component, you may miss some advantages here!
You really don't need show function see:-
Android Activity as a dialog
Explanation:-
You can have a parent activity and set it's theme as Dialog, so all fragments (inside the nav.xml) will be dialogs..
I used this trick in one of my apps before.
Related
I have an old Xamarin Android project, which targeted MVVMCross 6.
When the app launches, a welcome activity is loaded by MVVMcross.
Then the user selects the signin button, which loads the signin activity or signup activity.
Then after signin, the user is redirected to the MainActivity, which contains a home fragment.
Each of the activities mentioned above has its viewmodel. The Home fragment too has its viewmodel.
I use viewmodel first navigation, and before navigating from the signin/signup to the main activity's page, I close the current page first.
The issue is that, when I navigate from the signin/signup page to the main activity which contains a home fragment, There is an overlapp animation that occures in the app, and MVVMcross instantiates the MainActivity several times in a row.
When I look at the output window of Visualstudio, I see this:
(MvvmCross.Logging.MvxLog) PresentationAttribute not found for
MainActivity. Assuming Activity presentation (MvxAndroid) Activity
host with ViewModelType MyApp.Mobile.ViewModels.MainViewModel is not
CurrentTopActivity. Showing Activity before showing Fragment for
MyApp.Mobile.ViewModels.HomeViewModel Activity host with
ViewModelType MyApp.Mobile.ViewModels.MainViewModel is not
CurrentTopActivity. Showing Activity before showing Fragment for
MyApp.Mobile.ViewModels.HomeViewModel (MvvmCross.Logging.MvxLog)
PresentationAttribute not found for MainActivity. Assuming Activity
presentation (MvvmCross.Logging.MvxLog) PresentationAttribute not
found for MainActivity. Assuming Activity presentation
From the error message, I can understand that the main activity and its fragment are fighting the top of the navigation stack.
To navigate, I use a "IMvxNavigationService"
Here is how I navigate to the MainActivity that contains the fragments of the app.
await this.navigationService.Navigate<MainViewModel>();
Here is how I attach my presenter to each fragment which is to be displayed in the main viewmodel:
[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.content_frame, true)]
public class HomeFragment : BaseTabFragment<HomeViewModel>
{
}
and BaseTabFragment has the following declaration:
public abstract class BaseTabFragment<TViewModel> : BaseFragment<TViewModel> where TViewModel : class, IMvxViewModel
And Base fragment inherits from: "MvxFragment"
I tried creating a custom MVVMcross fragment presenter, where I add the "ActivityFlags.NewTask | ActivityFlags.ClearTop | ActivityFlags.ClearTask" Flags to the top activities, but this doesn't work. Can someone please help ?
From my perspective if you really switch from signing-activity to main-activity you also have to create the View as activity. So the MainViewModel should map to the MainView as activity and should be a kind of "parent".
Fragments are part of activities.
Take a look at this example, hope it helps.
https://github.com/MvvmCross/MvvmCross/tree/master/Projects/Playground/Playground.Droid
Special to these:
https://github.com/MvvmCross/MvvmCross/blob/master/Projects/Playground/Playground.Droid/Activities/RootView.cs
https://github.com/MvvmCross/MvvmCross/blob/master/Projects/Playground/Playground.Droid/Activities/TabsRootView.cs
how to make button to open another fragment. being within a fragment. kotlin
I'm starting in kotlin and I'm having a hard time trying to open a fragment with a button, how do I?
You need to use FragmentManager and FragmentTransaction to add your fragment on the fly. you can call a function similar to this in your button's onClick method. But it is recommended for the parent activity to handle each fragment's lifecycle and the fragments
are not supposed to interact each other. The following is taken from the developer docs, that can be found here.
"Often you will want one Fragment to communicate with another, for example to change the content based on a user event. All Fragment-to-Fragment communication is done either through a shared ViewModel or through the associated Activity. Two Fragments should never communicate directly."
fun createFragmentonTheFly(){
var mFragmentTransaction: FragmentTransaction = getSupportFragmentManager().beginTransaction()
mFragmentTransaction.add(R.id.fr_container,new ProductListFragment())
mFragmentTransaction.commit()
}
The best way to do it would be to add an interface let say onFragmentDetachedLisetner and add one method replaceFragment() or something and make your Activity implement this interaface and had it replace as soon as the fragment is detached and make your fragment that contains your button finish itself when user clicks the button, then your activity will replace it with the one you wanted to start. And also consider reusing fragments, as that is the main purpose of fragments at the first place.
scenario :
MainActivity.kt : activity_main.xml contains my_nav_host_fragment
in mobile_navigation.xml first_frag_dest is the startDestination and there is second_frag_dest also, with respective classes FirstFragment.kt and SecondFragment.kt.
I have setup bottom navigation for switch between two fragments using the Android Jetpack Navigation components.
When the Second Fragment is opened for the first time if FirbaseUser is null, A Welcome Activity is launched which is configured for GoogleSignInusing Firebase.
In WelcomeActivity.java I have a function updateUI(user: FirebaseUser) to update the UI.
Now, what code should I write in updateUI function such that I am redirected back to the Second Fragment.
Please tell how to do it using JetPack Navigation components.
Note: Principles of navigation say that only one activity must exist but here I am compelled to use an Activity for GoogleSignIn due to Firebase restrictions.
If you want to close your WelcomeActivity and return to the activity below it, have your updateUI method call finish():
Call this when your activity is done and should be closed
While Navigation component of JetPack looks pretty promising I got to a place where I could not find a way to implement something I wanted.
Let's take a look at a sample app screen:
The app has one main activity, a top toolbar, a bottom toolbar with fab attached.
There are 2 challenges that I am facing and I want to make them the right way.
1. I need to implement fragment transactions in order to allow replacing the fragment on the screen, based on the user interaction.
There are three ways I can think of and have this implemented:
the callbacks way. Having a interface onFragmentAction callback in fragment and have activity implement it. So basically when user presses a button in FragmentA I can call onFragmentAction with params so the activity will trigger and start for example transaction to replace it with FragmentB
implement Navigation component from JetPack. While I've tried it and seems pretty straightforward, I had a problem by not being able to retrieve the current fragment.
Use a shared ViewModel between fragment and activity, update it from the fragment and observe it in the activity. This would be a "replacement" of the callbacks
2. Since the FAB is in the parent activity, when pressed, I need to be able to interact with the current visible fragment and do an action. For instance, add a new item in a recyclerview inside the fragment. So basically a way to communicate between the activity and fragment
There are two ways I can think of how to make this
If not using Navigation then I can use findFragmentById and retrieve the current fragment and run a public method to trigger the action.
Using a shared 'ViewMode' between fragment and activity, update it from activity and observe it in the fragment.
So, as you can see, the recommended way to do navigation would be to use the new 'Navigation' architecture component, however, at the moment it lacks a way to retrieve the current fragment instance so I don't know how to communicate between the activity and fragment.
This could be achieved with shared ViewModel but here I have a missing piece: I understand that fragment to fragment communication can be made with a shared ViewModel. I think that this makes sense when the fragments have something in common for this, like a Master/Detail scenarion and sharing the same viewmodel is very useful.
But, then talking between activity and ALL fragments, how could a shared ViewModel be used? Each fragment needs its own complex ViewModel. Could it be a GeneralViewModel which gets instantiated in the activity and in all fragments, together with the regular fragment viewmodel, so have 2 viewmodels in each fragment.
Being able to talk between fragments and activity with a viewmodel will make the finding of active fragment unneeded as the viewmodel will provide the needed mechanism and also would allow to use Navigation component.
Any information is gladly received.
Later edit. Here is some sample code based on the comment bellow. Is this a solution for my question? Can this handle both changes between fragments and parent activity and it's on the recommended side.
private class GlobalViewModel ():ViewModel(){
var eventFromActivity:MutableLiveData<Event>
var eventFromFragment:MutableLiveData<Event>
fun setEventFromActivity(event:Event){
eventFromActivity.value = event
}
fun setEventFromFragment(event:Event){
eventFromFragment.value = event
}
}
Then in my activity
class HomeActivity: AppCompatActivity(){
onCreate{
viewModel = ViewModelProviders.of(this, factory)
.get(GlobalViewModel::class.java)
viewModel.eventsFromFragment.observe(){
//based on the Event values, could update toolbar title, could start
// new fragment, could show a dialog or snackbar
....
}
//when need to update the fragment do
viewModel.setEventFromActivity(event)
}
}
Then in all fragments have something like this
class FragmentA:Fragment(){
onViewCreated(){
viewModel = ViewModelProviders.of(this, factory)
.get(GlobalViewModel::class.java)
viewModel.eventsFromActivity.observe(){
// based on Event value, trigger a fun from the fragment
....
}
viewModelFragment = ViewModelProviders.of(this, factory)
.get(FragmentAViewModel::class.java)
viewModelFragment.some.observe(){
....
}
//when need to update the activity do
viewModel.setEventFromFragment(event)
}
}
I'm looking for some advice on the best way to handle fragments which launch other fragments.
I'm converting an app which I started writing using a more Activity based approach and have since begun moving it over to using Fragments. I have some Fragments which used to launch a new Activity and I want to move them over to launching other Fragments in the same view that the current Fragment is residing.
For example - I have an Activity which has a WebView which uses a WebViewClient to handle internal js->java interactions. My WebViewClient can launch other Activities, which I used to do with :
i = new Intent(context, GoogleMapActivity.class);
startActivity(i);
This webview activity can either be fullscreen or in a view with a menu on the side, but I want the webview to respect the layout - so if the menu is present, it should stay present when launching new Fragments - I just don't know the best approach to writing the code which launches the Fragments.
So...is there a way, within a Fragment, of essentially telling a new Fragment to load in to the same space as the current Fragment or does there need to be some interaction with the Activity?
** EDIT **
Given that there are a few different layouts which could be used, I don't always know which id I should be targeting to put the fragment in - hence I need to know if there's a way to do this without knowing the id (as in the replace method for example).
getFragmentManager().beginTransaction()
.replace(((ViewGroup) getView().getParent()).getId(), fragment)
.addToBackStack(null)
.commit();
This should replace parent container with desired fragment.
That should be doable via FragementManager.replace(). Have a look at the documentation for Fragment and especially the longer example in the "Layout" section there.
If you want to add Fragment rather replace it, use:
getFragmentManager().beginTransaction().add(R.id.fragment_container, new Fragment()).commit();