onBackStackChanged() called twice when new fragment is added in backstack
Asked Answered
C

2

10

So, i have notice interesting problem. When i add new fragment programmatically, method onBackStackChanged from OnBackStackChangedListener is called twice, but it must be call only one time. Here is my code of activity:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements FragmentManager.OnBackStackChangedListener{

    private FragmentManager fragmentManager;
    private Button button1;
    private Button button2;
    private Button button3;

    private Fragment defaultFragment;
    private Fragment previousFragment;
    private Fragment currentFragment;
    private String currentFragmentTag;

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

        button1 = (Button) findViewById(R.id.button);
        button2 = (Button) findViewById(R.id.button2);
        button3 = (Button) findViewById(R.id.button3);

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showFragment(new FragmentOne(), FragmentOne.TAG);
            }
        });

        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showFragment(new FragmentTwo(), FragmentTwo.TAG);
            }
        });

        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showFragment(new FragmentThree(), FragmentThree.TAG);
            }
        });

        setupFragmentManager();

    }

    private void setupFragmentManager() {
        fragmentManager = getSupportFragmentManager();
        fragmentManager.addOnBackStackChangedListener(this);


        FragmentOne fragmentOne = new FragmentOne();
        defaultFragment = fragmentOne;  // fragment for default

        currentFragmentTag = FragmentOne.TAG;
        currentFragment = fragmentOne;

        fragmentManager.beginTransaction()
                .add(R.id.fragments_container, currentFragment, currentFragmentTag)
                .addToBackStack(currentFragmentTag)
                .commit();
    }

    public void showFragment(Fragment fragment, String fragmentTag) {
        previousFragment = currentFragment;
        currentFragment = fragment;
        fragmentManager.beginTransaction()
                .hide(previousFragment)
                .add(R.id.fragments_container, fragment, fragmentTag)
                .addToBackStack(fragmentTag)
                .commit();
    }

    @Override
    public void onBackStackChanged() {
        Log.d("MY_TAG" , "onBackStackChangedListener - " + fragmentManager.getBackStackEntryCount() );

        if (fragmentManager.getBackStackEntryCount() > 0) {    
            String fragmentTag = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount() - 1).getName();
            currentFragment = fragmentManager.findFragmentByTag(fragmentTag);
        } else {
            currentFragment = defaultFragment;
        }

// some code...
    }

    @Override
    public void onBackPressed() {
        if (fragmentManager.getBackStackEntryCount() == 1) { 
            finish();
        } else {
            super.onBackPressed();
        }
    }
}

And fragment class:

public class FragmentOne extends Fragment {

    public static final String TAG = FragmentOne.class.getSimpleName();

    private Context context;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {


        context = getActivity();
        View topLevelView = LayoutInflater.from(context).inflate(R.layout.fragment_layout, container, false);

        TextView textView = (TextView) topLevelView.findViewById(R.id.fragment_Text);

        textView.setText("This is FRAGMENT ONE");

        return topLevelView;
    }

}

FragmentTwo and FragmentThree are identical to FragmentOne.

And when i start my app, in log i see:

onBackStackChangedListener - 1
onBackStackChangedListener - 1

after pressed of button (show next fragment) i see:

onBackStackChangedListener - 2
onBackStackChangedListener - 2

and another one:

onBackStackChangedListener - 3
onBackStackChangedListener - 3

And changing

.add(R.id.fragments_container, currentFragment, currentFragmentTag)

to

.replace(R.id.fragments_container, currentFragment, currentFragmentTag)

doesnt solve the problem.

But it is very strange. Why my OnBackStackChangedListener calls more than one time own method after one addiing the fragment?

Cosher answered 21/3, 2017 at 20:56 Comment(1)
Nothing do do with your issue really, but you can implement the View.OnClickListener interface directly to the Activity, and for each of your buttons, button.setOnClickListener(this) to each, and then you only need one onClick method in your Activity, and use a switch statement to find the view clicked on and run your logic from one single implementation rather than defining it each time you have a view that needs click behavior.Mew
M
11

Couldn't believe it can happen. Created a similar project and dived into the debugger. Turns out it's a bug in new support library. There's is a bug opened in the tracker.

Switching back to 25.0.0 will work as expected.

compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:support-v4:25.0.0'

Though not mentioned in release notes, but it is mentioned in the issue tracker, that the issue is fixed in 25.4.0, which is good 😊.

Mackler answered 21/3, 2017 at 21:5 Comment(1)
no, when i removed line: .hide(previousFragment) - it doesnt solve the problem. And there is no hide-method in adding the first(default) fragment. So, this problem is not depends on hide-method.Cosher
T
2

Just an update. Google finally released version 25.4.0 with that issue fixed.

Only, now the support stuff is meant to be fetched from the Google's Maven repository:

maven { url 'https://maven.google.com' }

So you can start using the new versions of everything and uninstall the local support repository from the SDK Manager.

Trichloroethylene answered 26/7, 2017 at 12:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.