I have a HorizontalScrollView
containing a (horizontal) LinearLayout
which I use as the container for adding multiple fragments. Upon some changes, I need to remove all fragments from that container and add new ones. However, there seems to be a problem with ordering when I'm removing the old fragments.
Here are the scenarios:
A1
,B1
,C1
,D1
in this orderA2
,B2
,C2
(as a single transaction), it will show A1
,B1
,C1
,D1
,A2
,B2
,C2
A2
,B2
,C2
, it will show C2
,B2
,A2
For now I found a workaround, where I'm adding the new fragments first then removing the old ones (still as part of the same transaction) and that is working properly.
EDIT: The workaround doesn't work all the time.
I'm using android.support.v4.app.Fragment
.
Any ideas on what's happening?
I enabled debugging on the FragmentManager and I found the problem.
Here's an excerpt from the logs, notice how the fragment index is allocated in reverse order:
V/FragmentManager? Freeing fragment index TimeTracesChartFragment{42ac4910 #7 id=0x7f080044}
V/FragmentManager? add: RealTimeValuesFragment{42a567b0 id=0x7f080044}
V/FragmentManager? Allocated fragment index RealTimeValuesFragment{42a567b0 #7 id=0x7f080044}
V/FragmentManager? add: TimeTracesChartFragment{42d35c38 id=0x7f080044}
V/FragmentManager? Allocated fragment index TimeTracesChartFragment{42d35c38 #6 id=0x7f080044}
V/FragmentManager? add: TimeTracesChartFragment{42d35e98 id=0x7f080044}
V/FragmentManager? Allocated fragment index TimeTracesChartFragment{42d35e98 #5 id=0x7f080044}
V/FragmentManager? add: TimeTracesChartFragment{42d36220 id=0x7f080044}
V/FragmentManager? Allocated fragment index TimeTracesChartFragment{42d36220 #4 id=0x7f080044}
V/FragmentManager? add: TimeTracesChartFragment{42d39d18 id=0x7f080044}
V/FragmentManager? Allocated fragment index TimeTracesChartFragment{42d39d18 #3 id=0x7f080044}
V/FragmentManager? add: TimeTracesChartFragment{42d3a170 id=0x7f080044}
V/FragmentManager? Allocated fragment index TimeTracesChartFragment{42d3a170 #2 id=0x7f080044}
V/FragmentManager? add: TimeTracesChartFragment{42d3a528 id=0x7f080044}
V/FragmentManager? Allocated fragment index TimeTracesChartFragment{42d3a528 #1 id=0x7f080044}
V/FragmentManager? moveto CREATED: TimeTracesChartFragment{42d3a528 #1 id=0x7f080044}
And here is the culprit code:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/support/v4/app/FragmentManager.java#FragmentManagerImpl.makeActive%28android.support.v4.app.Fragment%29
void makeActive(Fragment f) {
if (f.mIndex >= 0) {
return;
}
if (mAvailIndices == null || mAvailIndices.size() <= 0) {
if (mActive == null) {
mActive = new ArrayList<Fragment>();
}
f.setIndex(mActive.size(), mParent);
mActive.add(f);
} else {
f.setIndex(mAvailIndices.remove(mAvailIndices.size()-1), mParent);
mActive.set(f.mIndex, f);
}
if (DEBUG) Log.v(TAG, "Allocated fragment index " + f);
}
Notice how the available indices are taken from the back of the list. It should probably choose the lowest index available so that it would preserve ordering.
Now to think of a workaround...
EDIT:
Here's a workaround: Create two separate transactions, one for the removals and then one for the additions, then do this:
removalTxn.commit();
getSupportFragmentManager().executePendingTransactions();
FragmentTransactionBugFixHack.reorderIndices(getSupportFragmentManager());
//create additionTxn
additionTxn.commit();
Where FragmentTransactionBugFixHack
looks like this:
package android.support.v4.app;
import java.util.Collections;
public class FragmentTransactionBugFixHack {
public static void reorderIndices(FragmentManager fragmentManager) {
if (!(fragmentManager instanceof FragmentManagerImpl))
return;
FragmentManagerImpl fragmentManagerImpl = (FragmentManagerImpl) fragmentManager;
if (fragmentManagerImpl.mAvailIndices != null)
Collections.sort(fragmentManagerImpl.mAvailIndices, Collections.reverseOrder());
}
}
It's not ideal, because of the two separate transaction it will flicker to white (or whatever your container's background is), but at least it will order them properly.
other way to fix this issue:
replace ArrayList mAvailIndices by ReverseOrderArrayList
ReverseOrderArrayList.java
public class ReverseOrderArrayList<T extends Comparable> extends ArrayList<T> {
@Override
public boolean add(T object) {
boolean value = super.add(object);
Collections.sort(this, Collections.reverseOrder());
return value;
}
@Override
public void add(int index, T object) {
super.add(index, object);
Collections.sort(this, Collections.reverseOrder());
}
@Override
public boolean addAll(Collection<? extends T> collection) {
boolean value = super.addAll(collection);
Collections.sort(this, Collections.reverseOrder());
return value;
}
@Override
public boolean addAll(int index, Collection<? extends T> collection) {
boolean value = super.addAll(index, collection);
Collections.sort(this, Collections.reverseOrder());
return value;
}
@Override
protected void removeRange(int fromIndex, int toIndex) {
super.removeRange(fromIndex, toIndex);
Collections.sort(this, Collections.reverseOrder());
}
@Override
public boolean remove(Object object) {
boolean value = super.remove(object);
Collections.sort(this, Collections.reverseOrder());
return value;
}
@Override
public boolean removeAll(Collection<?> collection) {
boolean value = super.removeAll(collection);
Collections.sort(this, Collections.reverseOrder());
return value;
}
@Override
public T remove(int index) {
T value = super.remove(index);
Collections.sort(this, Collections.reverseOrder());
return value;
}
}
Hack
public class FragmentTransactionBugFixHack {
private static final String TAG = "FragmentTransactionBugFixHack";
public static void injectFragmentTransactionAvailIndicesAutoReverseOrder(FragmentManager fragmentManager) {
try {
Log.d(TAG, "injection injectFragmentTransactionAvailIndicesAutoReverseOrder");
if (fragmentManager==null || !(fragmentManager instanceof FragmentManagerImpl)) return;
FragmentManagerImpl fragmentManagerImpl = (FragmentManagerImpl) fragmentManager;
if (fragmentManagerImpl.mAvailIndices!=null && fragmentManagerImpl.mAvailIndices instanceof ReverseOrderArrayList) return;
ArrayList<Integer> backupList = fragmentManagerImpl.mAvailIndices;
fragmentManagerImpl.mAvailIndices = new ReverseOrderArrayList<>();
if (backupList!=null) {
fragmentManagerImpl.mAvailIndices.addAll(backupList);
}
Log.d(TAG, "injection ok");
} catch (Exception e) {
Log.e(TAG, e);
}
}}
Using: call FragmentTransactionBugFixHack.injectFragmentTransactionAvailIndicesAutoReverseOrder in activity-onCreate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With