Implementing proper back navigation and home button handling using Toolbar in Android

前端 未结 6 1827
粉色の甜心
粉色の甜心 2021-01-30 23:31

I am using a single activity and multiple fragments(screenshot attached) within the same activity to provide a seamless navigation. But after implementing the latest toolbar and

6条回答
  •  余生分开走
    2021-01-31 00:17

    It's much easier to illustrate with some sort of division of responsibility for your Activity and Fragment.

    Problem 1: Managing the Hamburger/Back button at left top. Toggling the icon and functionality to Menu and Back nav.

    From the illustration, the solution should be encapsulated by the Activity, which will look something like this:

    public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
    
        private ActionBarDrawerToggle mDrawerToggle;
        private DrawerLayout mDrawer;
        private ActionBar mActionBar;
    
        private boolean mToolBarNavigationListenerIsRegistered = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
            mActionBar = getSupportActionBar();
    
            mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
            mDrawerToggle = new ActionBarDrawerToggle(this, mDrawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
            mDrawer.addDrawerListener(mDrawerToggle);
            mDrawerToggle.syncState();
    
            NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
            navigationView.setNavigationItemSelectedListener(this);
    
            // On orientation change savedInstanceState will not be null.
            // Use this to show hamburger or up icon based on fragment back stack.
            if(savedInstanceState != null){
                resolveUpButtonWithFragmentStack();
            } else {
                // You probably want to add your ListFragment here.
            }
        }
    
        @Override
        public void onBackPressed() {
    
            if (mDrawer.isDrawerOpen(GravityCompat.START)) {
                mDrawer.closeDrawer(GravityCompat.START);
    
            } else {
                int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
    
                if (backStackCount >= 1) {
                    getSupportFragmentManager().popBackStack();
                    // Change to hamburger icon if at bottom of stack
                    if(backStackCount == 1){
                        showUpButton(false);
                    }
                } else {
                    super.onBackPressed();
                }
            }
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.main, menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // Handle action bar item clicks here. The action bar will
            // automatically handle clicks on the Home/Up button, so long
            // as you specify a parent activity in AndroidManifest.xml.
            int id = item.getItemId();
    
            //noinspection SimplifiableIfStatement
            if (id == R.id.action_settings) {
                return true;
    
            } else if (id == android.R.id.home) {
                // Home/Up logic handled by onBackPressed implementation
                onBackPressed();
            }
    
            return super.onOptionsItemSelected(item);
        }
    
        @SuppressWarnings("StatementWithEmptyBody")
        @Override
        public boolean onNavigationItemSelected(MenuItem item) {
            // Handle navigation view item clicks here.
            int id = item.getItemId();
    
            // Navigation drawer item selection logic goes here
    
            mDrawer.closeDrawer(GravityCompat.START);
            return true;
        }
    
        private void replaceFragment() {
            /**
            * Your fragment replacement logic goes here
            * e.g.
            * FragmentTransaction ft = getFragmentManager().beginTransaction();
            * String tag = "MyFragment";
            * ft.replace(R.id.content, MyFragment.newInstance(tag), tag).addToBackStack(null).commit();
            */
    
            // The part that changes the hamburger icon to the up icon
            showUpButton(true);
        }
    
        private void resolveUpButtonWithFragmentStack() {
            showUpButton(getSupportFragmentManager().getBackStackEntryCount() > 0);
        }
    
        private void showUpButton(boolean show) {
            // 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.
            if(show) {
                // Remove hamburger
                mDrawerToggle.setDrawerIndicatorEnabled(false);
                // Show back button
                mActionBar.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) {
                    mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            onBackPressed();
                        }
                    });
    
                    mToolBarNavigationListenerIsRegistered = true;
                }
    
            } else {
                // Remove back button
                mActionBar.setDisplayHomeAsUpEnabled(false);
                // Show hamburger
                mDrawerToggle.setDrawerIndicatorEnabled(true);
                // Remove the/any drawer toggle listener 
                mDrawerToggle.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.
        }
    }
    

    Problem 2: Page title - Changing the page titles whenever a fragment in pushed and popped.

    Essentially, this can be handled in the onStart for each Fragment i.e. your ListFragment, DetailsFragment and CommentsFragment look something like this:

    @Override
    public void onStart() {
        super.onStart();
        // where mText is the title you want on your toolbar/actionBar
        getActivity().setTitle(mText);
    }
    

    Probably worth having setRetainInstance(true) in the onCreate of your fragments as well.

提交回复
热议问题