I need to set the start destination of a navigation graph programmatically depending on a condition. I have Fragment0, and also Fragment1, Fragment2, ... FragmentN all extending Fragment0, and being all of them (including Fragment0) able to be that wished start destination.
I already know there is the method from NavGraph called
setStartDestination(R.id.nav_fragment_X);
but it is not useful for me because, if I am not wrong, it requires to have all these fragment0...N nodes declared in the xml navigation file.
So, what I would like to, is to have just one fragment node in the xml file and be able to set, programmatically, the Class (a fragment in my case) that in normal cases you can indicate via the design/text tabs of android studio and is displayed like this:
android:name="com.android.fragments.FragmentX"
I finally ended up with the answer:
First you get your startDestination graph by, for instance, executing:
NavDestination startDestNode = navGraph.findNode(R.id.start_dest);
And, later, you update the class name by getting use of:
((FragmentNavigator.Destination) startDestNode).setClassName("Class name you wish");
In conclusion, the method setClassName(String className) of the class FragmentNavigator.Destination allows you to indicate the fragment class that will be displayed associated with the destination.
Related
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.
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.
When I navigate to an activity using ShowViewModel, it is nicely animated. But when the target is a Fragment it won't. Is there a way to add this as well?
I saw that in native android you would add it to the FragmentTransaction, but since MvvmCross Handles that for us, I assume there is another place to handle that.
The code that handles the fragment transaction is the Show method from the activity implementing IMvxFragmentHost that's responsible for handling the specific Fragment being show. In order to change the animation, you need to use the SetCustomAnimations method when displaying the fragment.
What I usually do is creating a BaseFragmentView class that has enter and leave animations exposed as properties. When displaying the fragments, I can simply use those properties like this:
var transaction = SupportFragmentManager
.BeginTransaction()
.SetCustomAnimations(fragmentView.EnterAnimation, fragmentView.ExitAnimation)
.Replace(targetId, fragmentView)
.Commit();
When using the MvxChachingFragmentView, you can simply override the OnBeforeFragmentChanging method and use the second parameter to add the custom animations you want.
You can see how to implement the IMvxFragmentHost interface by checking the MvxCachingFragmentView class and, if you don't know how to use the new Fragments from MvvmCross 4, refer to this answer
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();
On the iPhone I use a Navigation Controller to push and pop Views from. Very handy.
Is there an equivalent in Android?
This is an old question, but I believe the answer has changed. It is now possible to imitate the Nav stack in iOS in android using Fragments.
http://developer.android.com/reference/android/app/Fragment.html
Basically instead of jumping from Activity to Activity you instead stay in one Activity that controls the display, organization, and animation of Fragments which each contain their own behavior much like the NavController / UIViewController model in iOS.
It is also backwards compatible as a static library so you can implement it on pre-Honeycomb devices.
Strategies for Honeycomb & backward compatibility
Typically in android, each view is displayed in its own Activity. You can read about activities in the application fundamentals documentation. To move to a new Activity, or view, you use an intent.
If you haven't done so yet, I'd highly recommend reading through those introductary android docs. They aren't too long, and do a good job of explaning the basic program structure.
I made a Framework (github) to provide a hierarchical navigation pattern, with animations to provide sense of navigation, rather than launching new Activities every time.
Here's how to use it:
Add the framework to your project as a Module
Add a new Java class in your project ("File - New - Java Class").
Note: If you are editing the Activity.java file that provides you the template, delete all its implementations and leave it empty.
Make it extend NavigationActivity
Implement all the NavigationActivity abstract methods
(in Android Studio if you click Alt + insert and select implement - methods all the function definitions are automatically generated).
public class NavigationTest extends NavigationActivity{
#Override
public Fragment firstFragment() {
//return the first fragment that will be shown
}
#Override
public Boolean showBackButtonInFirstFragment() {
//show back button already in the first Fragment
//set to True if this activity is called by another Activity
//the back button will then pop back to the previous Activity
}
#Override
public Boolean showMasterDetailLayoutInTablets() {
//set to false if you don't want a master-detail layout in tablets
}
}
Presenting a new Fragment
You can present a new fragment (with a nice animation) by calling the pushFragment method from NavigationActivity.
public void pushFragment(Fragment newFragment, animationType animation, boolean showAsDetailFragmentIfPossible)
newFragment (Fragment): New Fragment that will be presented
animation (animationType): Animation type enum: RIGHT_TO_LEFT, BOTTOM_TO_TOP, FLIP
showAsDetailFragmentIfPossible (boolean): If set as True, the user is in a Tablet, and you are using a master-detail layout, the Fragment will be shown in the detail Fragment (the panel in the right)!
Since you can access the activity from any Fragment with the getActivity() method, you can show a new Fragment from the currently displaying Fragment.
For example you can put this code within a button click listener:
NextFragment f = new NextFragment();
NavigationActivity nav =((NavigationActivity)getActivity());
nav.pushFragment(f,NavigationActivity.animationType.RIGHT_TO_LEFT,false);
You don't have to worry about implementing the back button behaviour. This is handled automatically by the NavigationActivity class.
There are thee basic types in Android to show UI in Android:
View
Fragment
Activity
Google IO 2018 introduced Navigation component which should make life easier. It is a wrapper under a standard mechanisms.
Here you can find NavGraph which looks like storyboard and NavController which help to navigate to destination