Reputation: 1943
I have just create a demo to understand the replace method of FragmentTransaction and I am not getting result as per Developer Guide.
Activity and Fragment
public class MainActivity extends AppCompatActivity {
private int count;
private Fragment prevFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button button = (Button) findViewById(R.id.activity_main_bv_add);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
count++;
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
final TestFragment fragment = new TestFragment(count);
if (count == 1) {
transaction.add(R.id.activity_main_rl_container, fragment, fragment.getClass().getSimpleName());
} else if (count == 5) {
transaction.replace(R.id.activity_main_rl_container, fragment, fragment.getClass().getSimpleName());
transaction.addToBackStack(null);
if (prevFragment != null) {
transaction.hide(prevFragment);
}
} else {
transaction.add(R.id.activity_main_rl_container, fragment, fragment.getClass().getSimpleName());
transaction.addToBackStack(null);
if (prevFragment != null) {
transaction.hide(prevFragment);
}
}
prevFragment = fragment;
transaction.commit();
}
});
}
public class TestFragment extends Fragment {
private final String TAG = this.getClass().getSimpleName();
private int number;
public TestFragment(final int number) {
this.number = number;
}
public TestFragment() {
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.e(TAG, "onCreateView : " + number);
final View view = inflater.inflate(R.layout.row_button, null);
final Button button = (Button) view.findViewById(R.id.button);
button.setText("" + number);
return view;
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.e(TAG, "onDestroyView : " + number);
}
}
}
Fragment 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:background="@color/colorPrimary"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="40dp"
android:focusable="false"
android:focusableInTouchMode="false"
/>
</LinearLayout>
Activity xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/activity_currency_select"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
>
<Button
android:id="@+id/activity_main_bv_add"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="Add More"/>
<RelativeLayout
android:id="@+id/activity_main_rl_container"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_below="@id/activity_main_bv_add"
android:layout_marginTop="20dp"
android:background="@color/colorAccent"></RelativeLayout>
</RelativeLayout>
FragmentTransaction replace method : As per Android document, 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.
Assume Case 1 : Using replace method will remove current(last and latest) fragment and add new one in container view.
Assume Case 2 : Using replace method will remove add the fragments that are added into same container view.
But as per above code non of the above case seems right.
Let's click on button as per shown code. On every button click till count == 4, new object of Fragment will be added to container.
When we click on button, for the count == 5, here replace method is used at that time new object of Fragment will be added to container and onDestroyView method is called for the 1st and 3rd added fragments
Question 1 : If replace will remove all the fragments added in same container then why onDestroyView is not called for 2nd and 4th added fragments?
Question 2 : If replace method removes last added fragment in same container then why it does not destroy 4th fragment but 1st and 3rd instead?
As per my understanding the behaviour dose not follow the document showing on Developer site.
Correct me if I'm wrong.
Upvotes: 0
Views: 256
Reputation: 2135
It seems a bug of replace
method.
Notice that this doesn't happen with the Support version of the FragmentManager
.
I will refer to this and this.
The first one is marked as Obsolete and the second one as Assigned, but both seem to be very related.
In both is highlighted that the removal of the fragments (from an ArrayList) is done inside a for
loop, and then ArrayList of fragments change its size inside this for
loop, this causes skipping of some indexes, and then the removal of some (not all) fragments.
Here is the for loop referred inside the links:
for (int i = 0; i < mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
// ...
if (f == null || old.mContainerId == f.mContainerId) {
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);
}
}
}
In your specific case, you are adding fragments 1,2,3,4. After, when you want to replace all these four fragments with the fifth one, the for loop above is executed. So you remove the 1st (corresponding to i=0 in the for loop), and the arraylist of fragments becomes 2,3,4. Then you pick the next index in the for loop (i=1), but in the meantime the arraylist changed, so take the fragment 3 (corresponding to i=1) and remove it. Finally, this arraylist becomes 2,4 and i=2, and no fragment corresponds to the index 2.
I hope I have helped you understand, even if I do not take the 50 points :)
Upvotes: 2
Reputation: 11
for (int i=0; i<mManager.mAdded.size(); i++) {
Fragment old = mManager.mAdded.get(i);
if (f ==null ||old.mContainerId == f.mContainerId) {
mManager.removeFragment(old,mTransition, mTransitionStyle);
}
}
MManager.mAdded is an ArrayList list, when called in the traversal of the mManager.removeFragment method, and the method calls the ArrayList remove method;
Public void removeFragment (Fragmentfragment, int transition, inttransitionStyle) {
MAdded.remove (fragment);
}
This means that remove is used when looping through the ArrayList list with a for loop
Upvotes: 0