【问题标题】:How to add Fragment to first position in FragmentPagerAdapter如何将片段添加到 FragmentPagerAdapter 的第一个位置
【发布时间】:2014-12-09 14:19:57
【问题描述】:

我正在使用 FragmentPagerAdapter 和 ViewPager 添加自定义 Fragments 编辑:从我的 MainActivity(还通过捆绑发送基于 JSON 响应的一堆额外数据)并使用滑动动作移动到列表中的下一个片段。

public class MyPagerAdapter extends FragmentPagerAdapter implements Serializable {

	public List<Fragment> fragments;
	public FragmentManager fm;

	public MyPagerAdapter(FragmentManager fm) {
		super(fm);
		this.fm = fm;
		this.fragments = new ArrayList<Fragment>();	
	}
	
	@Override
	public Fragment getItem(int position) {
		return fragments.get(position);
	}


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

}

只要我添加新的 Fragments,一切正常 使用

MyPagerAdapter pageAdapter = new MyPagerAdapter(getSupportFragmentManager());

pager = (ViewPager)findViewById(R.id.myViewPager);

pageAdapter.fragments.add(new CustomFragment());

pager.setAdapter(pageAdapter);

但我找不到将片段添加到列表开头并向后滑动的正确方法。

我都试过了

pageAdapter.fragments.add(0, new CustomFragment());

以及将 FragmentPagerAdapters 列表更改为 LinkedList 并使用

pageAdapter.fragments.addFirst(new CustomFragment());

然后使用刷新适配器

pageAdapter.notifyDataSetChanged();

我不断收到以下异常:

java.lang.IllegalStateException: Can't change tag of fragment CustomFragment{2ead9520 #10 id=0x7f0a0002 android:switcher:2131361794:10}: was android:switcher:2131361794:10 now android:switcher:2131361794:11

【问题讨论】:

  • 为每个页面创建一个新实例。
  • @Krupal:我以为我已经在创建 CustomFragment() 的新实例

标签: android android-fragments android-viewpager android-fragmentactivity


【解决方案1】:

尚未有人谈论过的关键方法是public int getItemPosition(Object),它用于在片段移动后将它们重新映射到页面,而public long getItemId(int position)必须被重新排序片段的寻呼机适配器覆盖。默认实现使用页面的位置作为id,所以重新排序会混淆FragmentPagerAdapter

(我省略了 Serializable 接口,因为它与回答问题无关 - 如何在 FragmentPagerAdapter 中重新排序片段)。

public class MyPagerAdapter extends FragmentPagerAdapter {

    public List<Fragment> fragments;
    public FragmentManager fm;

    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
        this.fm = fm;
        this.fragments = new ArrayList<Fragment>(); 
    }

    void addFragmentAtPosition(int position, Fragment f) {
        if(position == fragments.size())
            fragments.add(f);
        else
            fragments.add(position, f);
        notifyDataSetChanged();
    }

    void removeFragmentAtPosition(int position) {
        Fragment f = fragments.remove(position);
        if(f != null)
            fm.beginTransaction().remove(f).commit();
        notifyDataSetChanged();
    }

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

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

    @Override
    public int getItemPosition(Object object){
        /*
         * called when the fragments are reordered to get the
         * changes.
         */
        int idx = fragments.indexOf(object);
        return idx < 0 ? POSITION_NONE : idx;
    }

    @Override
    public long getItemId(int position) {
        /*
         * map to a position independent ID, because this
         * adapter reorders fragments
         */
        return System.identityHashCode(fragments.get(position));
    }
}

添加的关键是public int getItemPosition(Object)public long getItemId(int) 的覆盖。这些允许FragmentPagerAdapter 重新定位现有片段并正确识别FragmentManager 缓存中的现有活动片段。

【讨论】:

  • FragmentStatePagerAdapter 怎么样?没有getItemId
  • 我实现这个已经有一段时间了。 FragmentStatePagerAdapter 只加载视图中的片段以及之前和之后的片段,因此位置对这个适配器没有任何意义。例如,您可以有一个显示书页的 FragmentStatePagerAdapter,如果您正在查看第 50 页,则加载第 49,50 和 51 页。当您滑动到第 51 页时,第 52 页被实例化,第 49 页被卸载。对象的位置意义不大,在第 1 页插入不会影响 FragmentStatePagerAdapter 的当前状态。即方法必须不同。
【解决方案2】:

您不应该以这种方式创建和添加片段。相反,只需在 getItem 中实例化片段,适配器将负责使用它们。这样做:

@Override
public Fragment getItem(int position) {
    Fragment fragment = new CustomFragment();
    fragments.add(fragment)
    return fragment
}

【讨论】:

    【解决方案3】:

    我建议您不要保留对片段的引用列表,因为这不是必需的,并且您可能会造成内存泄漏。 我要做的是仅在需要时才创建片段:

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = new MyFragment();
        return fragment;
    }
    

    要解决您的问题,您应该根据需要创建片段,例如,如果您有不同类实例的片段,例如 MyFragment 的一个实例,YourFragment 的一个实例等等,只需保留一个说明哪种类型的列表片段占据那个位置。 例如:

    myListMap = new HashMap<Integer, Integer>();
    myListMap.put(position, type);
    

    然后动态创建片段:

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;
        int type = ...find fragment type in that position ....
        if(type == MYFRAGMENTTYPE) {
            fragment = new MyFragment();
        }
        return fragment;
    }
    

    【讨论】:

    • 我不确定我是否理解您的回答。 “我建议您不要保留对片段的引用列表,因为这不是必需的,并且您可能会造成内存泄漏。”我如何保留对片段的引用?
    • 我还编辑了我的问题。我将从 JSON 响应中收到的数据发送到 MainActivity 的片段,但我不确定如何按照您建议的方式执行此操作。
    【解决方案4】:

    不知道您是否还需要答案,但我正在尝试做类似的事情并找到了解决方案:)

    由于您的 getItem 函数,您收到了该异常。您将在函数接收到的位置返回片段,并且该位置始终是数组的最后一个位置,因为这将对应于“典型”用法中最后添加的片段。

    在您的情况下,您想在第一个位置添加一个新片段,因此您的 getItem 将返回两次相同的片段并引发异常。

    为避免这种情况,您需要在您的适配器中创建一个公共函数和您要添加的片段的索引,然后返回这个特定的片段。

    PS.:我现在在 Kotlin 中开发了大约 6 个月,所以它可能会出现一些拼写错误。

    public int newFragmentIndex = 0;
    public List<Fragment> fragments;
    
    ...
    
    public void addFragmentAt(int index, Fragment fragment) {
        newFragmentIndex = index;
        pageAdapter.fragments.add(index, fragment);
        notifyDataSetChanged();
    }
    
    public void addFragment(Fragment fragment) {
        newFragmentIndex = fragments.size();
        pageAdapter.fragments.add(fragment);
        notifyDataSetChanged();
    }
    
    ...
    
    @Override
    public Fragment getItem(int position) {
        return fragments.get(newFragmentIndex);
    }
    


    要从您的活动中调用它,请更改您的
    pageAdapter.fragments.add(new CustomFragment());

    pageAdapter.fragments.addFragment(new CustomFragment());

    还有
    pageAdapter.fragments.add(0, new CustomFragment());

    pageAdapter.fragments.addFragmentAt(0, new CustomFragment());


    希望这可以帮助您解决问题!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-04
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 2017-04-14
      相关资源
      最近更新 更多