Java Android在FragmentPagerAdapter内更改片段顺序

Java Android在FragmentPagerAdapter内更改片段顺序,java,android,android-fragments,android-viewpager,fragmentpageradapter,Java,Android,Android Fragments,Android Viewpager,Fragmentpageradapter,我在FragmentPagerAdapter中有大约20个片段,其中包含一个列表,它从中选择片段,因此不需要重新创建片段 private List<TitledFragment> fragments; public SectionsPagerAdapter(FragmentManager fm) { super(fm); this.fragments = new ArrayList<TitledFragment>(); } @Override pub

我在FragmentPagerAdapter中有大约20个片段,其中包含一个列表,它从中选择片段,因此不需要重新创建片段

private List<TitledFragment> fragments;

public SectionsPagerAdapter(FragmentManager fm) {
    super(fm);

    this.fragments = new ArrayList<TitledFragment>();
}

@Override
public Fragment getItem(int i) {
    return fragments.get(i).getFragment();
}

@Override
public int getCount() {
    return fragments.size();
}

@Override
public CharSequence getPageTitle(int position) {
   return fragments.get(position).getTitle();
}

public synchronized List<TitledFragment> getFragments() {
    return fragments;
}


public synchronized void addFragment(TitledFragment fragment) {
    fragments.add(fragment);
    notifyDataSetChanged();
}
由于某些原因,只更改了标题的顺序,即使它返回的getItem片段不同,因为顺序不同。为什么会出现这种情况?我怎样才能避开它?

您还需要覆盖。默认情况下,此函数始终返回
位置\u UNCHANGED
,因此当调用
notifyDataSetChanged()
时,适配器假定所有片段仍处于相同位置。(它不会再次调用
getItem()
,因为它认为所有必需的片段都已创建。)新的
getItemPosition()
应该在洗牌后返回每个片段的新位置


一个简单的方法是总是返回
位置\u NONE
,然后适配器将丢弃所有现有片段,并在调用
getItem()
时在正确的位置重新创建它们。当然,这是没有效率的,并且还有一个额外的缺点,当前正在查看的片段可能会改变。然而,由于在重新排列碎片时可能存在一些错误-请参阅-无位置方法可能是最安全的方法。

接受的答案是非最佳答案。从
int-getItemPosition(Object)
返回
POSITION\u NONE
只会破坏有效片段管理的任何希望,需要重新实例化所有片段。它还忽略了另一个问题。FragmentPageAdapter在FragmentManager中保留片段的缓存副本,并在实例化新片段时查找这些副本。如果它找到了它认为匹配的片段,则不会调用
公共片段getItem(int)
方法,而是使用缓存的副本

例如,假设加载了页面0和1,FragmentManager中将有标记为0和1的缓存片段。现在在索引0处插入了一个页面(不要忘记调用
notifyDataSetChanged()
),旧的索引0变为1,1变为2(这是使用方法
public int FragmentPageAdapter.getItemPosition(Object)
发出的信号)。对于项目0位置\未返回任何位置(因为它是新位置),因此为位置0调用方法
public Object instanceItem(ViewGroup,int)

public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        ...
看看会发生什么,找到位置0的缓存片段,位置1中需要的片段现在位于位置0,位置2中需要的片段现在位于位置1,位置2中会得到FragmentPageAdapter.getItem(int)返回的新片段,它是位置1的副本

如何解决这个问题?我看到了许多关于这方面的建议,包括:

  • 始终从FragmentPageAdapter.getItemPosition()返回位置\u NONE-此回答忽略效率和内存管理(如果不清除缓存的片段,最终将无法工作)
  • 跟踪片段标记,这依赖于可能更改的实现细节
  • 最糟糕的是,通过反向工程FragmentPageAdapter实现使用魔法,这太可怕了
  • 无需破坏内存管理或跟踪
    FragmentPagerAdapter
    的内部实现细节。所有答案中缺少的细节是,想要对片段重新排序的FragmentPagerAdapter必须实现方法
    public long getItemId(int position)

        @Override
        public long getItemId(int position) {
            return System.identityHashCode(fragments.get(position));
        }
    
    这提供了一个非基于位置的ID,可用于在FragmentManager缓存中查找正确的片段,即使它移动了页面

        @Override
        public long getItemId(int position) {
            return System.identityHashCode(fragments.get(position));
        }