Android - 使用FragmentStatePageAdapter保存/恢复状态

时间:2016-10-05 17:26:41

标签: java android fragment

我上了这堂课:

public class My_ViewPagerAdapter extends FragmentStatePagerAdapter {

    private ArrayList<My_Fragment> fragments=new ArrayList<>();
    private ArrayList<String> tabTitles=new ArrayList<>();

    public My_ViewPagerAdapter(FragmentManager fm) {

        super(fm);
    }

    @Override
    public Fragment getItem(int position) {

        return fragments.get(position);
    }

    @Override
    public int getItemPosition(Object object) {

        for (int i=0;i<fragments.size();i++){

            Class<? extends My_Fragment> clazz= fragments.get(i).getClass();

            if (clazz.isInstance(object)){

                return i;
            }
        }
        return POSITION_NONE;
    }

    @Override
    public int getCount() {

        return fragments.size();
    }

    public void addFragment(My_Fragment fragment, String tabTitle){

        fragments.add(fragment);
        tabTitles.add(tabTitle);
        notifyDataSetChanged();
    }

    public void removeFragment(int position){

        fragments.remove(position);
        tabTitles.remove(position);
        notifyDataSetChanged();
    }

    public My_Fragment getFragmentAtTab(int position){

        return fragments.get(position);
    }

    @Override
    public CharSequence getPageTitle(int position) {

        return tabTitles.get(position);
    }
}

我正在动态添加/删除片段/标签,我想通过配置更改保存其状态。

我实现了onSaveInstanceState():

int tabs=tabLayout.getTabCount();

    for (int i=0;i<tabs;i++){

        outState.putInt("TabsCount",tabs);
        outState.putString("Tab_"+i,getTabTitle(i));
        outState.putString("FragmentClassAtTab_"+i,my_viewPagerAdapter.getFragmentAtTab(i).getClass().getName());
    }

我实现了onRestoreInstanceState():

for (int i=0;i<tabs;i++){

        try {

            My_Fragment fragment= (My_Fragment) Class.forName(savedInstanceState.getString("FragmentClassAtTab_"+i)).newInstance();
            addTab(fragment ,savedInstanceState.getString("Tab_"+i));
        }
        catch (ClassNotFoundException e) {

            e.printStackTrace();
        }
        catch (InstantiationException e) {

            e.printStackTrace();
        }
        catch (IllegalAccessException e) {

            e.printStackTrace();
        }
    }

addTab方法是:

public void addTab(My_Fragment fragment, String title){

    if (fragment!=null) my_viewPagerAdapter.addFragment(fragment,title);
    tabLayout.addTab(tabLayout.newTab().setText(title));
}

问题:

我添加的每个片段都会在配置发生更改时“自动”重新创建,因此每次都会调用其构造函数。 onCreateView会在这些实例上调用,而不是在我的代码重新创建的实例上调用

我需要 onCreateView 来调用我的标签中实例化的片段,以便设置他们的列表视图。

通过我的 onRestoreInstanceState 方法“自动”替换为新的片段实例创建的片段,但在这些新实例上不会调用 onCreateView 方法。

我认为我做错了,也许有更好的方法可以在配置更改中保存标签和片段...感谢任何帮助。

2 个答案:

答案 0 :(得分:0)

我曾经创造过类似的东西,在我看来是这样的:

public class DisplayPagerAdapter extends FragmentStatePagerAdapter  {

    private static final String TAG = "DisplayPagerAdapter";

    SparseArray<DisplayFragment> registeredFragments = new SparseArray<DisplayFragment>();

    private final Context context;
    private final DisplayCoreModule display;
    private final FragmentManager fm;

    private boolean isAddOrRemoving;

    public DisplayPagerAdapter(Context context, FragmentManager fm,
            DisplayCoreModule display) {
        super(fm);
        this.context = context;
        this.display = display;
        this.fm = fm;

        Log.d(TAG, "pages " + display.getPagesCount());
    }


    public void notifySizeChangingDataSetChange() {
        isAddOrRemoving = true;
        notifyDataSetChanged();
        isAddOrRemoving = false;
    }

    @Override
    public int getCount() {
        int count = (display != null && display.getPagesCount() > 0) ? display
                .getPagesCount() : 1;
        return count;
    }

    @Override
    public int getItemPosition(Object object) {
        DisplayFragment frag = (DisplayFragment) object;
        if (!display.containsPageId(frag.getPageId())) {
            // this will update the 'no information' page with id -1
            return POSITION_NONE;
        }
        if (isAddOrRemoving) {
            // recreate all for simplicity
            return POSITION_NONE;
        }
        return POSITION_UNCHANGED;
    }

    @Override
    public Fragment getItem(int position) {
        Log.d(TAG, "getItem " + position);
        return DisplayFragment.newInstance(position);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        if (display != null && display.getPagesCount() > 0) {
            return context.getString(R.string.page) + " " + (position + 1);
        } else {
            return super.getPageTitle(position);
        }
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Log.d(TAG, "instantiateItem " + position);
        DisplayFragment fragment = (DisplayFragment) super.instantiateItem(
                container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Log.d(TAG, "destroyItem " + position);
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }

    public SparseArray<DisplayFragment> getRegisteredFragments() {
        return registeredFragments;
    }

}

我写这篇文章已经有很长时间了,因此无法详细介绍。

然而,要注意的关键(我想),是我有一个外部的簿记员&#39; (DisplayCoreModule)。每当DisplayCoreModule中的片段数量发生变化时,我只需调用notifySizeChangingDataSetChange()来使FragmentStatePagerAdapter刷新内容。

考虑到您的一些问题,FragmentStatePagerAdapter在显示之前不会调用onCreateView您的片段。通常这意味着只有三个片段:您目前看到的片段和片段两侧的片段。

或许,考虑到这一点,您的问题的根源缺少getItemPosition() POSITION_NONE。似乎回想一下,动态内容的更新是必要的。使notifyDataSetChanged具有适当的效果。

答案 1 :(得分:0)

HACKY SOLUTION:

我只是修改了 addTab 方法:

public void addTab(My_Fragment fragment, String title){

    boolean createFragment=true;

    if (getTabCount()>0){
        //all tabs must have different names
        for (int i=0;i<getTabCount();i++){

            if (getTabTitle(i).equals(title)==true){

                createFragment=false;
                break;
            }
        }
    }

    if (createFragment && fragment!=null){
        //this ensures that if a fragment of the same type already exists, that should be the one to be added, tabs are made of fragments of different classes, so there will be no problem while checking this.
        for (Fragment f: my_activity.getSupportFragmentManager().getFragments()){

            if (fragment.getClass().isInstance(f)){

                my_viewPagerAdapter.addFragment((My_Fragment)f,title);
                tabLayout.addTab(tabLayout.newTab().setText(title));
                return;
            }
        }
        my_viewPagerAdapter.addFragment(fragment,title);
        tabLayout.addTab(tabLayout.newTab().setText(title));
    }
}