Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 235
Alex D. Avatar asked Mar 21 '17 20:03

Alex D.


2 Answers

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 😊.

like image 174
azizbekian Avatar answered Nov 09 '22 10:11

azizbekian


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.

like image 42
Doodler Avatar answered Nov 09 '22 11:11

Doodler