I have a navigation drawer based on the example in the Android documentation.
When you open the navigation drawer it shows a MainMenuFragment inside the drawer. When you select a menu item, it replaces the fragment inside the drawer with a SubMenuFragment. This works fine expect for the back button.
Pressing the back button always closes the drawer but I want to use the back button to show the MainMenuFragment in the drawer when the SubMenuFragment is showing. How can you handle the back button inside the navigation drawer to replace the fragment showing inside the drawer.
Replacing the fragment inside the drawer is not the issue here. I can not figure out how to stop the back button from closing the drawer and have it show the MainMenuFragment instead.
My main activity XML (shortened):
<android.support.v4.widget.DrawerLayout ....>
<FrameLayout android:id="#+id/container" .../>
<FrameLayout android:id="#+id/drawer" android:layout_gravity="start" .../>
</android.support.v4.widget.DrawerLayout>
and the relevant parts of my class:
public class MainActivity extends Activity {
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getFragmentManager().beginTransaction()
.replace(R.id.drawer, new MainMenuFragment()).commit();
// setup DrawerLayout and ActionBarDrawerToggle
...
}
private void showSubMenu() {
getFragmentManager().beginTransaction()
.replace(R.id.drawer, new SubMenuFragment()).commit();
}
}
What I've used before in different cases is to add fragments to Fragment BackStack.
So when changing fragment added it to the back stack first.
getFragmentManager().beginTransaction()
.replace(R.id.drawer, new MainMenuFragment()).addToBackStack(null).commit();
And when handling back do the following.
int count = getSupportFragmentManager().getBackStackEntryCount();
if(count > 1)
getSupportFragmentManager().popBackStack();
Try this and see if it works.
Also check this out : http://developer.android.com/training/implementing-navigation/temporal.html#back-fragments
Related
I have an app (Java) which loads a fragment (Fragment A). This fragment has a recyclerView which in turn loads a second fragment (Fragment B). I want Fragment B to have a back arrow that can return to Fragment A, but I don't want the back arrow in Fragment A.
I added this code to the Main Activity:
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
But this causes the back arrow to appear in every fragment.
How can I have the back arrow appear only on second level fragments (like B)?
you can hide the button on the wanted fragment like this
#Override
public void onResume() {
super.onResume();
((AppCompatActivity)getActivity()).getSupportActionBar(). setDisplayHomeAsUpEnabled(false);
}
#Override
public void onStop() {
super.onStop();
((AppCompatActivity)getActivity()).getSupportActionBar(). setDisplayHomeAsUpEnabled(true);
}
I made a NavigationView on MainActivity (AppCompatActivity).
I have multiple items on that NavigationView each one is a Fragment.
When I use back button I want to go to previous Fragment instead of put my app on background. Thus to change between Fragments I use:
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.layout, fragment).addToBackStack(null).commit();
When I press back button it goes to the previous Fragment, but the item checked of NavigationView remains. Which is the best way to update item checked of NavigationView on back button press?
i have used the following code into my project....and it worked....
I have inflated menu items into navigation drawer.....
you have to implement it in your every fragment...in onActivityCreated() method....because this method will certainly called after activity is created so we can use UI elements...
//code.....
NavigationView navigation = (NavigationView)getActivity().findViewById(R.id.navigation_view);
Menu drawer_menu = navigation.getMenu();
MenuItem menuItem;
menuItem = drawer_menu.findItem(R.id.Id_ofYourMenuItem);
if(!menuItem.isChecked())
{
menuItem.setChecked(true);
}
So, every time your fragment is called(via navigation drawer or via pressing back button) ,it will set menu item checked itself.
for future users here is another solution :
my drawer_menu.xml is like :
<item
android:id="#+id/Home"
android:title="Home"
android:icon="#drawable/ic_account_balance_black_24dp"
>
</item>
<item
android:id="#+id/courses"
android:title="courses"
android:icon="#drawable/ic_airplay_black_24dp"
>
</item>
<item
android:id="#+id/download"
android:title="Download"
android:icon="#drawable/ic_file_download_black_24dp"
>
</item>
0 :home
1 :courses
2 :download
Now in your Mainactivity : (im showing you only navigation view purpose code)
private private NavigationView navigationView;
protected void onCreate(Bundle savedInstanceState)
{
navigationView= (NavigationView) findViewById(R.id.navview);
}
Now create a manual method in mainactivity to call this method from fragment when fragment resume:
public void SetNavItemChecked(int id)
{
Menu m=navigationView.getMenu();
MenuItem mi=m.getItem(id);
mi.setChecked(true);
}
Now in All fragments in this exmple Homefragment,CoursesFragment,DownloadFragments are the fragments
in HomeFragment (YourFragment) :
override onresume method :
#Override
public void onResume() {
super.onResume();
((MainActivity)getActivity()).SetNavItemChecked(0);
// MainActivity is your activity
//SetNavItemChecked is method from activity and 0 is first item in menu for this example o is HomeFragment
}
and for all remaining fragment you can write in resume method like above
Alternatively, you can create a Stack, which stores the MenuItems of the NavigationDrawer when they are selected and then use the poll method in the onBackButtonPressed method to get and uncheck the MenuItem and the peek method to setChecked(true) the current MenuItem.
Probably not the most efficient way, but very easy to implement.
If you are using NavigationComponents, this is one of the ways
override fun onBackPressed() {
super.onBackPressed()
navController.currentBackStackEntry?.destination?.id?.let {
binding.navView.setCheckedItem(it)
}
}
Instead of using boiling code in every fragments, you can override this method in the mainActivity (fragment container).
Actually, I am using whole application in a single activity by fragments in main view container.
One fragment contains a viewPager when I clicked over a header button fragment replaced by LoginFragment and on Back we maintaining backstack so fragment containing viewPager will be on top but there we got blank view of current item in viewPager.
This is my Solution and i have searched a lot of the answer so if you got a better one please share it with me..
i switched the ViewPager to the Activity
Activity XML
<RelativeLayout ....
xmlns.
<FrameLayout
android:id="#+id/container"
android:layout_height="match_parent"
android:layout_width="match_parent"
/>
<android.support.v4.view.ViewPager
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
</...viewPager>
Activity Class
public MainActivity extends Activity .... {
private boolean mShowPager;
private FrameLayout mContainer;
private ViewPager mPager;
//Inflate them
public void showViewPager() {
mPager.setVisibility(View.Visible);
mContainer.setVisibilty(View.Gone);
}
public void showContainer() {
mContainer.setVisibility(View.Visible);
mPager.setVisibilty(View.Gone);
}
public void showViewPager(boolean show) {
mShowPager = show;
}
#override
public void onBackPressed() {
if(mShowPager) {
showViewPager();
}
super.onBackPressed();
Fragment Example
public void onAttach(Activity activity) {
((MainActivity)activity).showViewPager(true);
((MainActivity)activity).showContainer();
//True means when back pressed back show ViewPager
//Back means still show Container but do Back pressed
Info : you decide what you want to show ViewPager or the Container that contain the Fragments plus you can use the BackStack if needed.
Hope it helps its helped me.
I got an answer by my self on debugging. Problem was I was using fragmentPager inside fragment so there for adapter initialization we have to pass getChildFragmentManager() instead of getFragmentManager()
I am currently re-coding most of the back end of my android app in order to follow the design guidelines more closely. Currently I am using all activities and zero fragments. I am trying to switch to fragments in order to use the slide out navigation draw and eventually some sliding tabs.
For navigation right now I have this drop down menu which when an item is clicked launches a new activity:
The "Your Statistics" activity is kind of like the home page, where the user will enter the app too. I also want the user to be able to get back to that "page" from anywhere in the app.
My activity that I plan to run the draw from I have a draw layout called fragment_main:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<FrameLayout
android:id="#+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
<ListView
android:id="#+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#FFF"
android:choiceMode="singleChoice"/>
</android.support.v4.widget.DrawerLayout>
and my activity which loads the drawer layout is:
public class MainDraw extends FragmentActivity {
final String[] data ={"one","two","three"};
final String[] fragments ={
"com.beerportfolio.beerportfoliopro.FragmentOne",
"com.beerportfolio.beerportfoliopro.FragmentTwo",
"com.beerportfolio.beerportfoliopro.FragmentThree"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
//todo: load statistics
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActionBar().getThemedContext(), android.R.layout.simple_list_item_1, data);
final DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
final ListView navList = (ListView) findViewById(R.id.drawer);
navList.setAdapter(adapter);
navList.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> parent, View view, final int pos,long id){
drawer.setDrawerListener( new DrawerLayout.SimpleDrawerListener(){
#Override
public void onDrawerClosed(View drawerView){
super.onDrawerClosed(drawerView);
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this, fragments[pos]));
tx.commit();
}
});
drawer.closeDrawer(navList);
}
});
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main,Fragment.instantiate(MainDraw.this, fragments[0]));
tx.commit();
}
}
IN my //todo: comment I should load my first "home" fragment there which is my statistics "page" ? And then all the other fragments will be transitioned in and out based on the draw clicks?
Thanks for your help in advance, I want to make sure I am doing this right, I used to code just to get things working which is why I am now re doing a huge chunk of my code. Please share any other fragment tips to that I might need!
First of all read the well written documentation, it answers to your doubts.
I would share my personal pattern to convert existing Activity to Fragment
Create your on abstract Fragment class from which derive all drawer fragments, this can help to group common attributes
Use a method like selectItem() on docs, it helps to explicit do a call at first run (showing the "home" fragment) and then from onItemClick
move inflating XML layout from Activity.onCreate() code to Fragment.onCreateView() (ie setContentView to inflater.inflate(R.layout.my_layout, container, false), in many cases you can copy all code from onCreate() to onCreateView
move initialization code from Activity.onCreate() to Fragment.onActivityCreated(), this is very useful when both Activity (including fragment) and the direct Fragment exist, for example if your app exposes a "Share with" action you continue to have the Activity that inside the XML includes a <fragment/> and the fragment can be created from the drawer, too
if you need to communicate from Activity to Fragment and viceversa I suggest to create an interface and store it inside the 'onAttach()' (see google example)
Action bar items must be hidden when drawer is open, again take a look at example used in doc, here is very useful the interface to communicate from activity to fragment, the main activity can tell if drawer is open and the fragment can call the interface
public interface FragmentActivityStatus {
public boolean isDrawerOpen();
}
The activity
public class MainActivity extends Activity implements FragmentActivityStatus {
#Override
public boolean isDrawerOpen() {
return drawerLayout.isDrawerOpen(drawerList);
}
}
The fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
fragmentActivityStatus = (FragmentActivityStatus)activity;
}
#Override
public void onPrepareOptionsMenu(Menu menu) {
boolean isMenuVisible = !fragmentActivityStatus.isDrawerOpen();
menu.findItem(R.id.my_menu).setVisible(isMenuVisible);
super.onPrepareOptionsMenu(menu);
}
Not related to fragment, in your code you declare class names as string, consider to create a Class array if you refactor packages the code continue to work, then you can call the Class.getName() to obtain the string to pass to Fragment.instantiate()
final Class<?>[] fragments = {
FragmentOne.class,
FragmentTwo.class,
FragmentThree.class};
Then
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, Fragment.instantiate(MainDraw.this,
fragments[pos].getName()));
tx.commit();
I'm working on application that has a tab structure, and use sliding movements to move through the tabs.
But now, I want to apply Drawer Layout. The problem is that the Drawer has slide to open events. How I can delete this event? My idea was that the Drawer only could open and close with a button. Is this possible?
Thanks!
Just write
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
to prevent menu from listening to gesture
and use openDrawer and closeDrawer to change menu visibility
By default the DrawerLayout is initially hidden from the view unless you put a code to open the Drawer, by the time there is a sliding event triggered.
From the Navigation Drawer example, the contain content_frame is used to dynamically display views inside the Drawer using fragments.
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
From the Fragment's onCreateView() you can include a button somewhere that has OnClickListener where in you put this code,
//For me a better way in avoiding a `null pointer` in getting the DrawerLayout
final DrawerLayout drawer = (DrawerLayout) getActivity().findViewById(R.id.drawer_layout);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
//Opens the Drawer
drawer.openDrawer(Your View, Usually a ListView);
}
return false;
});
You Can also use* to close the drawer.
drawer.closeDrawer(Your View, Usually a ListView);
you can write this way
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
drawer.openDrawer(navigationView);
}
});
If you want to navigate between drawer item on click of button inside your fragments,
you can use this
((YourMainActivity)getActivity()).selectItem(position);