Search code examples
androidandroid-fragmentsmaterial-designandroid-appcompatback

Fragment BackStack doesn't work properly


Overview

Hi there, I'm currently trying to develop a android app with the new material design and compatipility back to API15. This time I would like to use only one activity and multiple fragments by replacing them in the activity. While doing so, I've already run in multiple problems which i've either solved or just didn't reappeared... However, I still have a problem with the BackStack in the FragmentManager. It seems as the back button and the back icon in the toolbar (not actionbar!) are not working properly. I have the feeling, that this problem is somehow caused due to the use of AppCompatActivity. BTW: Since I find it very hard to understand whats the right way to code a api22 android app with one Activity/ multiple Fragments, every tip/critique from you guys is very welcome!

Problem

Back button and back icon in toolbar not working.

When I open the settings via the toolbar, neither the back button nor the back icon in the toolbar brings me back to the previous fragment. The back icon in the toolbar just doesn't do anything and the (software) back button closes the app

Code

MainActivity

public class MainActivity extends AppCompatActivity {

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

        // Set toolbar
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Load in first fragment
        getFragmentManager().beginTransaction()
                .add(R.id.fragment_container, new MyListFragment())
                .commit();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        if (id == R.id.action_settings) {
            // Replace current fragment with settings fragment
            getFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, new SettingsFragment())
                    .addToBackStack(null)
                    .commit();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

}

MyListFragment

Just a simple ListFragment

SettingsFragment

public class SettingsFragment extends PreferenceFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Activate back icon in toolbar
        ((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        // Inflate preferences
        addPreferencesFromResource(R.xml.settings);
    }

}

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimaryDark"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

value/style.xml (same as value-v21/style.xml)

<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="@color/primary">#2196F3</item>
        <item name="@color/primaryDark">#1976D2</item>
        <item name="@color/accent">#FF4081</item>
    </style>

</resources>

Update

The (software) back button now works when overwriting onBackPressed() as following:

public void onBackPressed() {
    getFragmentManager().popBackStack();
}

However, the back icon in the toolbar still doesn't take me back.


Solution

  • The issue with the 'back' button in your Toolbar, is that it is not designed for back navigation. The backwards facing arrow in the Toolbar is for 'up' navigation (incredibly confusing at times -- and often shares the same behavior as the device 'back' button). See this link for further explanation:

    http://developer.android.com/design/patterns/navigation.html

    While there are certainly ways to override 'up' behavior, (e.g., overriding android.R.id.home in the onOptionsItemSelected method), it might make more sense to add a SettingsActivity that manages the SettingsFragment. You can then set the parent Activity of the SettingsActivity to the MainActivity in your manifest. Then both back and up should work the way you want without overriding any standard OS behavior:

    http://developer.android.com/training/implementing-navigation/ancestral.html

    Hope that helps!