6

MainActivity

package example.antonio.activitydemo;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.support.v4.app.*;

public class MainActivity extends FragmentActivity {

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

        final FragmentManager fragmentManager = getSupportFragmentManager();

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                FirstFragment fragment = new FirstFragment();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

                fragmentTransaction.add(R.id.container, fragment);
                fragmentTransaction.commit();
            }
        });

        Button button2 = (Button) findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SecondFragment fragment = new SecondFragment();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

                fragmentTransaction.replace(R.id.container, fragment);
                fragmentTransaction.commit();
            }
        });
    }
}

FirstFragment

package example.antonio.activitydemo;

import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.View;

public class FirstFragment extends Fragment {

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInsanceState) {
        return inflater.inflate(R.layout.first_fragment, container, false);
    }
}

SecondFragment

package example.antonio.activitydemo;

import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.View;

public class SecondFragment extends Fragment {

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInsanceState) {
        return inflater.inflate(R.layout.second_fragment, container, false);
    }
}

Their XML files:

main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/container"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start1"
        android:id="@+id/button"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start2"
        android:id="@+id/button2"/>

</LinearLayout>

first_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="#000000">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="First!"
        android:textColor="#ffffff"/>


</FrameLayout>

second_fragment.xml just like the previous one, only with android:text="Second!"

Here is the strange thing, when I click for 4 times the Start1 button then I get 4 times the word First!, until here all normal. Following if I click Start2 button what I would expect is the erase of the 4 previous fragments and the add of just a new one, with appearance of Second!. This is what I understood from the docs:

Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.

But here is what I get:

After I click 4 times <code>Start1</code> button After I click <code>Start2</code> button

I tried to read the source code, maybe I found where is the "problem", I would like to know what you think and if it's me that I'm making a mistake somewhere.

Here is the snippet from BackStackRecord.java, run() method:

case OP_REPLACE: {
                    Fragment f = op.fragment;
                    int containerId = f.mContainerId;
                    if (mManager.mAdded != null) {
                        for (int i = 0; i < mManager.mAdded.size(); i++) {
                            Fragment old = mManager.mAdded.get(i);
                            if (FragmentManagerImpl.DEBUG) {
                                Log.v(TAG,
                                        "OP_REPLACE: adding=" + f + " old=" + old);
                            }
                            if (old.mContainerId == containerId) {
                                if (old == f) {
                                    op.fragment = f = null;
                                } else {
                                    if (op.removed == null) {
                                        op.removed = new ArrayList<Fragment>();
                                    }
                                    op.removed.add(old);
                                    old.mNextAnim = op.exitAnim;
                                    if (mAddToBackStack) {
                                        old.mBackStackNesting += 1;
                                        if (FragmentManagerImpl.DEBUG) {
                                            Log.v(TAG, "Bump nesting of "
                                                    + old + " to " + old.mBackStackNesting);
                                        }
                                    }
                                    mManager.removeFragment(old, mTransition, mTransitionStyle);
                                }
                            }
                        }
                    }

and here is the snippet from FragmentManager.java:

public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
        if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
        final boolean inactive = !fragment.isInBackStack();
        if (!fragment.mDetached || inactive) {
            if (false) {
                // Would be nice to catch a bad remove here, but we need
                // time to test this to make sure we aren't crashes cases
                // where it is not a problem.
                if (!mAdded.contains(fragment)) {
                    throw new IllegalStateException("Fragment not added: " + fragment);
                }
            }
            **if (mAdded != null) {
                mAdded.remove(fragment);
            }**
            if (fragment.mHasMenu && fragment.mMenuVisible) {
                mNeedMenuInvalidate = true;
            }
            fragment.mAdded = false;
            fragment.mRemoving = true;
            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
                    transition, transitionStyle, false);
        }
    }

As you can see, the removeFragment method removes a frament from mAdded but after in the run() method, the i index isn't modified, it restart from where it left, in such way it can lose some elements...

zer0uno
  • 7,521
  • 13
  • 57
  • 86
  • 1
    I think it's a bug, other people found the same issue: https://code.google.com/p/android/issues/detail?id=83178&can=1&q=fragment%20replace&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars – zer0uno Nov 12 '15 at 15:38
  • try calling the getSupportFragmentManager(); directly from the onclick body of each button rather than make it final – Zuko Nov 12 '15 at 17:24
  • already done, same problem. – zer0uno Nov 12 '15 at 22:39

1 Answers1

0

you cannot achieve this scenario with the linearlayout as a container.

If you try using the fragment as a container in linearlayout you can achieve this. please refer this link for further clarifications

Android - fragment .replace() doesn't replace content - puts it on top

Otherwise use the below code for button click actions,

Button button = (Button) findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            FirstFragment fragment = new FirstFragment();
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();

            fragmentTransaction.add(R.id.container, fragment).addToBackStack(null);
            fragmentTransaction.commit();
        }
    });

    Button button2 = (Button) findViewById(R.id.button2);
    button2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            SecondFragment fragment = new SecondFragment();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            for(int i=0; i < getSupportFragmentManager().getBackStackEntryCount(); i++)
                getSupportFragmentManager().popBackStack();
            fragmentTransaction.replace(R.id.container, fragment);
            fragmentTransaction.commit();
        }
    });

Hope this will help you.. Thanks

Community
  • 1
  • 1
Vennila
  • 240
  • 1
  • 11