简体   繁体   中英

How to use back stack properly on activities AND fragments

I have 2 activites: - LoginActivity: it will handle the login stuff later - MainActivity: the main app, all of the functions will be provided on fragments.

I want to properly use the back stack, but I can't.

What I want (but can't) achieve: App starts with Login screen. I login (right now press the Start button), after that the main screen appears and the first fragment loads. Now if I press the back button, the app closes, and that's how it should work, it must not go back to the login screen with a simple back button press.

But now if I click logout (and the Login screen appears), and I press the back button, the app takes me back to the main screen, what is really not ok. If I log out, from the login screen the back button should close the app.

And another problem, regarding also backstack, and fragments: I have 3 fragments: first, second and third. If I open them after each other (within the main activity of course), and I press the back button, I want it to take me back to the previously opened fragment, or if there's no previous, close the app. Now it makes it, opens the previous fragment, but at the same time it closes the complete app. (I can see that they are happening right after each other)

What's missing? What should be modified to achieve this (normal) behavior?

Thanks!

My code:

MainActivity:

public class MainActivity extends AppCompatActivity
    implements NavigationView.OnNavigationItemSelectedListener {

public static final String SP_DATA = "SP_DATA";
public static final String FB_LOGIN_STATUS = "FB_LOGIN_STATUS";
public static final String FLRT = "FLRT";

public SharedPreferences sharedPreferences;

@Override
protected void onCreate(Bundle savedInstanceState) {

    sharedPreferences = getSharedPreferences(SP_DATA, MODE_PRIVATE);
    boolean bAlreadyLoggedIn = sharedPreferences.getBoolean(FB_LOGIN_STATUS, false);

    if (bAlreadyLoggedIn) {
        Log.d(FLRT, "Already logged in");
    }
    else {
        Log.d(FLRT, "Not logged in");

        Intent intent = new Intent(MainActivity.this, LoginActivity.class);
        startActivity(intent);
    }

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    showFragment(new FirstFragment(),"FirstFragment");

    Button btnLogout = (Button) findViewById(R.id.btnLogout);

    btnLogout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            logout();

            showActivity(MainActivity.this, LoginActivity.class);
        }
    });

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.setDrawerListener(toggle);
    toggle.syncState();

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);
}

private void showActivity(Context context_fromActivity, Class<?> class_toActivty){
    Intent intent = new Intent(context_fromActivity, class_toActivty);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    startActivity(intent);
}

private void logout() {
    Log.d(FLRT, "Logging out...");

    sharedPreferences = getSharedPreferences(SP_DATA, MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putBoolean(FB_LOGIN_STATUS, false);
    editor.commit();
}

@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    } else {
        super.onBackPressed();
    }

    if (getFragmentManager().getBackStackEntryCount() > 0 ){
        getFragmentManager().popBackStack();
    } 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;
    }

    return super.onOptionsItemSelected(item);
}

@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();

    if (id == R.id.nav_first_layout) {
        showFragment(new FirstFragment(), "FirstFragment");
    }
    else if (id == R.id.nav_second_layout) {
        showFragment(new SecondFragment(), "SecondFragment");
    }
    else if (id == R.id.nav_third_layout) {
        showFragment(new ThirdFragment(), "ThirdFragment");
    }
    else if (id == R.id.nav_share) {

    }
    else if (id == R.id.nav_send) {

    }

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}

private void showFragment(Fragment fragment, String sFragmentTAG) {
    FragmentManager fragmentManager = getFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    if (fragmentManager.findFragmentByTag(sFragmentTAG) != null) {
        Log.d(FLRT, "Fragment found, using existing one: " + sFragmentTAG);
        fragment = fragmentManager.findFragmentByTag(sFragmentTAG);
    }
        fragmentTransaction.replace(R.id.fragmentContainer, fragment, sFragmentTAG);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();

}
}

LoginActivity:

public class LoginActivity extends AppCompatActivity {

public static final String SP_DATA = "SP_DATA";
public static final String FB_LOGIN_STATUS = "FB_LOGIN_STATUS";
public static final String FLRT = "FLRT";

public SharedPreferences sharedPreferences;

private Button btn_start;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);

    btn_start = (Button) findViewById(R.id.btn_start);

    btn_start.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            login();

            showActivity(LoginActivity.this, MainActivity.class);;
        }
    });
}

private void showActivity(Context context_fromActivity, Class<?> class_toActivty){
    Intent intent = new Intent(context_fromActivity, class_toActivty);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    startActivity(intent);
}

private void login() {
    Log.d(FLRT, "Logging in...");

    sharedPreferences = getSharedPreferences(SP_DATA, MODE_PRIVATE);
    SharedPreferences.Editor editor = sharedPreferences.edit();
    editor.putBoolean(FB_LOGIN_STATUS, true);
    editor.commit();
}
}

About switching between Activities .

One important thing.
In showActivity() use finish() to kill current Activity when going to another. If it doesn't help - see below.

Try to use also FLAG_ACTIVITY_NEW_TASK with your current flag, because FLAG_ACTIVITY_CLEAR_TOP will close only Activities in task above current Activity, but not below.
So, it would be this: intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);

Another option is to use: FLAG_ACTIVITY_CLEAR_TASK only - new Activity will clear the task and become root in it.

If both won't help - use android:launchMode = "singleInstance" in Manifest for each Actitivy .


About Fragments - I see a little mistake in your code: it calls super.onBackPressed(); before popping Fragment from BackStack
I would change like this:

@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    } else if (getFragmentManager().getBackStackEntryCount() > 0 ){
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
}

!! Also you should test this line: getFragmentManager().getBackStackEntryCount() > 0 with 0 and 1 in condition (because in current implementation in my project FragmentContainer is considering as first, zero value in a BackStack ). Just test 0 and 1 to see which works.

Thanks.


PS. Few more tips.

You can declare FragmentManager fragmentManager in your Activity in order to create it one time only in onCreate() . It would be better than get it each time.

Consider change this:

if (id == R.id.nav_first_layout) {
        showFragment(new FirstFragment(), "FirstFragment");
    }
    else if (id == R.id.nav_second_layout) {
        showFragment(new SecondFragment(), "SecondFragment");
    }
    else if (id == R.id.nav_third_layout) {
        showFragment(new ThirdFragment(), "ThirdFragment");
    }
    else if (id == R.id.nav_share) {

    }
    else if (id == R.id.nav_send) {

    }

To:

switch (item.getItemId()) {
    case R.id.nav_first_layout:
        showFragment(new FirstFragment(), "FirstFragment");
        break;
    case ...
}

and so on. The code would be more pretty :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM