Android多片段事务排序

时间:2014-05-06 21:24:42

标签: android android-fragments

我有HorizontalScrollView包含(水平)LinearLayout我用作添加多个片段的容器。在进行一些更改后,我需要从该容器中删除所有片段并添加新的片段。但是,当我删除旧片段时,排序似乎存在问题。

以下是方案:

  • app启动
    • 按此顺序正确添加片段A1B1C1D1
  • 更改内容
    • 如果不删除初始片段,但添加A2B2C2(作为单个交易),则会显示A1B1C1D1A2B2C2
    • 如果删除初始片段(作为单独或使用相同的交易),然后添加A2B2C2,则会显示C2,{{ 1}},B2

现在我找到了一个解决方法,我先添加新的碎片,然后删除旧的碎片(仍然是同一个事务的一部分)并且工作正常。

编辑:解决方法无法一直有效。

我正在使用A2

有关正在发生的事情的任何想法?

5 个答案:

答案 0 :(得分:11)

我在FragmentManager上启用了调试,但我发现了问题。

这是日志的摘录,注意片段索引是如何按相反顺序分配的:

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}

这是罪魁祸首代码:

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);
    }

注意如何从列表后面获取可用索引。它应该选择可用的最低索引,以便保留排序。

现在想一个解决方法......

修改

这是一个解决方法: 创建两个单独的事务,一个用于删除,另一个用于添加,然后执行以下操作:

removalTxn.commit();
getSupportFragmentManager().executePendingTransactions();
FragmentTransactionBugFixHack.reorderIndices(getSupportFragmentManager());
//create additionTxn
additionTxn.commit();

FragmentTransactionBugFixHack看起来像这样:

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());
    }

}

这并不理想,因为两个单独的交易它会闪烁到白色(或者你的容器的背景是什么),但至少它会正确地命令它们。

答案 1 :(得分:2)

解决此问题的其他方法:

用ReverseOrderArrayList替换ArrayList mAvailIndices

  

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;
}

}

  

哈克

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);
    }
}}
  

使用:   在activity-onCreate中调用FragmentTransactionBugFixHack.injectFragmentTransactionAvailIndicesAutoReverseOrder。

答案 2 :(得分:1)

作为替代解决方法,您可以尝试向LinearLayout添加视图,然后将每个片段添加到正确的视图中。它并不理想,但似乎你不能依赖片段创建的顺序。如下所示:

ViewGroup f1 = new ViewGroup(this);
linearLayout.addView(f1);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(f1, new A2(), "mediocreworkaround");
ft.commit();

然后,当您删除所有片段时,请确保删除相应的视图。

linearlayout.removeAllViews();

注意:代码可能在语法错误,我只是直接输入StackOverflow。这个想法很合理,虽然是一个普通的解决方案。有趣的问题 - 当我有更多的时间时,我可能会更多地研究这个问题。找出究竟发生了什么。如果我这样做,我会在这里发布更多信息。

修改

如果让系统为您处理,旋转应该很容易处理。为每个生成的视图添加唯一ID。

//Declare a counter to ensure generated ids are different
idCounter = 1;

现在使用该计数器在创建视图时设置ID:

//Set unique id on the view. If you really want, you can do a 
//findViewById(idCounter) == null check to ensure it's uniqueness. 
//Once you add this id, the system will take care of remembering 
//it's state for you across configuration chagne
f1.setId(idCounter++);

答案 3 :(得分:1)

有一个类似的问题,我最终使用的解决方案是有多个交易。在我的例子中,它只有A,B,C。我使用一个事务来添加A,一个添加B,一个添加C.

交易顺序似乎可靠。

如果您希望使用Backstack工作,可能需要更复杂的代码。但是第一笔交易上的后台标签也应允许在那里进行适当的处​​理。

答案 4 :(得分:0)

这是Radu's answer的略微修改版本,我在最后添加了递归部分。这重新排序给定片段管理器的索引及其所有片段&#39; childFragmentMangers,以及所有那些片段&#39; childFragmentManagers,等等。

这是您添加到项目中的新类(您可以将包android.support.v4.app添加到源代码的java文件夹中,并将其放入该包中,并且有效对我来说):

package android.support.v4.app;

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());
        }

        //Recursively reorder indices of all child fragments.
        List<Fragment> fragments = fragmentManager.getFragments();
        //The support library FragmentManager returns null if none.
        if(fragments != null) {
            for (Fragment fragment : fragments) {
                //For some reason, the fragments in the list of fragments might be null.
                if(fragment != null) {
                    reorderIndices(fragment.getChildFragmentManager());
                }
            }
        }
    }
}

为了解决在旋转设备时无序地重新创建片段的问题,只需将其放入管理片段的Activity类中(Credit转到Andrey Uglev在Radu's answer中的评论) :

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    FragmentTransactionBugFixHack.reorderIndices(getSupportFragmentManager());
}