Search code examples
androidandroid-fragmentsandroid-toolbarnavigation-drawerhamburger-menu

Add back button to Toolbar for all Fragments other than Home Fragment which opens Navigation Drawer


When my application is opened Home screen is shown first.On Home screen I have NavigationDrawer which get opened after pressing HamburgerIcon.Later i go to different fragments.When I am in Other fragments other than Home Activity I need to show back button on Toolbar to come to previous fragment.But its every time showing Hamburger icon.How to do this ?
This is code for setting Toolbar in XML

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/drawerLayout"
    tools:context="biz.fyra.myApp.ActivityTwo">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#ccc"
            android:minHeight="?attr/actionBarSize">
            <ImageView
                android:id="@+id/tooImage"
                android:src="@drawable/latest"
                android:layout_width="match_parent"
                android:layout_gravity="center_horizontal"
                android:layout_height="40dp" />
        </android.support.v7.widget.Toolbar>
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/frame">
        </FrameLayout>
    </LinearLayout>
    <android.support.design.widget.NavigationView
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/nav_header"
        android:id="@+id/navigationView"
        app:menu="@menu/actionmenu"
        android:background="@android:color/white">
    </android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>  

How to achieve this ?


Solution

  • If i understand right, you are using one activity with fragments replacing. So, looking at that you would have something like this:

    Important: Activity theme should extends Theme.AppCompat.Light.NoActionBar


    Activity:

    public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {
    private DrawerLayout drawer;
    private Toolbar toolbar;
    private ActionBarDrawerToggle toggle;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        drawer = findViewById(R.id.drawer);
        toolbar = findViewById(R.id.toolbar);
    
        setSupportActionBar(toolbar);
        toggle = new ActionBarDrawerToggle(
                this,
                drawer,
                toolbar,
                R.string.navigation_drawer_open,
                R.string.navigation_drawer_close
        );
        drawer.addDrawerListener(toggle);
        toggle.syncState();
    
        NavigationView navigationView = findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);
    
        // First creation
        if (savedInstanceState == null)
            showFragment(StartFragment.newInstance());
    }
    /**
    * Using in Base Fragment
    */
    protected ActionBarDrawerToggle getToggle() {
        return toggle;
    }
    
    @Override
    public void onBackPressed() {
        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.frame);
    
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else if (fragment instanceof OnBackPressedListener) {
            ((OnBackPressedListener) fragment).onBackPressed();
        } else {
            super.onBackPressed();
        }
    }
    
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        drawer.closeDrawer(GravityCompat.START);
        switch (item.getItemId()) {
            case R.id.start: {
                showFragment(StartFragment.newInstance());
                break;
            }
            case R.id.orders: {
                showFragment(OrdersFragment.newInstance());
                break;
            }
            case R.id.category: {
                showFragment(CategoryFragment.newInstance());
                break;
            }
            case R.id.calendar: {
                showFragment(CalendarFragment.newInstance());
                break;
            }
            case R.id.settings: {
                showFragment(SettingsFragment.newInstance());
                break;
            }
            case R.id.about: {
                showFragment(AboutFragment.newInstance());
                break;
            }
        return true;
    }
    
    private void showFragment(Fragment fragment) {
        getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment).commit();
    }
    }
    

    Interface for sending backpress events from activity to fragments:

    public interface OnBackPressedListener {
        void onBackPressed();
    }
    

    And Abstract Base Fragment which you should extends and implement methods:

    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.widget.DrawerLayout;
    import android.support.v7.app.ActionBar;
    import android.support.v7.app.ActionBarDrawerToggle;
    import android.support.v7.widget.Toolbar;
    
    import ...
    /**
     * Abstract fragment with FAB button, Toolbar and 2 interfaces: 
    OnClick, OnBackPress
     *
     */
    public abstract class BaseFragment extends Fragment implements 
    View.OnClickListener, OnBackPressedListener {
    
    protected FloatingActionButton fab;
    protected Toolbar toolbar;
    protected ActionBar actionBar;
    protected ActionBarDrawerToggle toggle;
    protected DrawerLayout drawer;
    protected boolean mToolBarNavigationListenerIsRegistered = false;
    
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        fab = ((MainActivity)getActivity()).findViewById(R.id.fab);
        toolbar = ((MainActivity) getActivity()).findViewById(R.id.toolbar);
        actionBar = ((MainActivity) getActivity()).getSupportActionBar();
        drawer = ((MainActivity) getActivity()).findViewById(R.id.drawer_layout);
        toggle = ((MainActivity) getActivity()).getToggle();
        fab.setOnClickListener(this);
    }
    
    /**
    * Simplify fragment replacing in child fragments
    */
    protected void replaceFragment(@NonNull Fragment fragment) {
        FragmentManager fm = getActivity().getSupportFragmentManager();
        fm.beginTransaction().replace(R.id.container, fragment).commit();
    }
    
    // hide FAB button
    protected void hideFab() {
        fab.hide();
    }
    
    //show FAB button
    protected void showFab() {
        fab.show();
    }
    
    /**
     * Shows Home button as Back button
     * Took from here {@link}https://stackoverflow.com/a/36677279/9381524
     * <p>
     * To keep states of ActionBar and ActionBarDrawerToggle synchronized,
     * when you enable on one, you disable on the other.
     * And as you may notice, the order for this operation is disable first, then enable - VERY VERY IMPORTANT!!!
     *
     * @param show = true to show <showHomeAsUp> or show = false to show <Hamburger> button
     */
    protected void showBackButton(boolean show) {
    
        if (show) {
            // Remove hamburger
            toggle.setDrawerIndicatorEnabled(false);
            // Show back button
            actionBar.setDisplayHomeAsUpEnabled(true);
            // when DrawerToggle is disabled i.e. setDrawerIndicatorEnabled(false), navigation icon
            // clicks are disabled i.e. the UP button will not work.
            // We need to add a listener, as in below, so DrawerToggle will forward
            // click events to this listener.
            if (!mToolBarNavigationListenerIsRegistered) {
                toggle.setToolbarNavigationClickListener(v -> onBackPressed());
                mToolBarNavigationListenerIsRegistered = true;
            }
    
        } else {
            // Remove back button
            actionBar.setDisplayHomeAsUpEnabled(false);
            // Show hamburger
            toggle.setDrawerIndicatorEnabled(true);
            // Remove the/any drawer toggle listener
            toggle.setToolbarNavigationClickListener(null);
            mToolBarNavigationListenerIsRegistered = false;
        }
        // So, one may think "Hmm why not simplify to:
        // .....
        // getSupportActionBar().setDisplayHomeAsUpEnabled(enable);
        // mDrawer.setDrawerIndicatorEnabled(!enable);
        // ......
        // To re-iterate, the order in which you enable and disable views IS important #dontSimplify.
    }
    
    /**
    * Simplify setTitle in child fragments
    */
    protected void setTitle(int resId) {
        getActivity().setTitle(getResources().getString(resId));
    }
    
    //
    @Override
    public abstract void onClick(View v);
    
    // Handles BackPress events from MainActivity
    @Override
    public abstract void onBackPressed();
    }
    

    All fragments with Back Button used in MainActivity should extends from this BaseFragment.