FragmentPagerAdapter неправильно удаляет элементы (фрагменты)

Я реализовал FragmentPagerAdapter и использовал List<Fragment> для хранения всех фрагментов для моего ViewPager для отображения. В addItem() я просто добавляю экземпляр Fragment, а затем вызываю notifyDataSetChanged(). Я не уверен, нужно это или нет.

Моя проблема просто... начните с фрагмента 1

[Fragment 1] 

добавить новый Фрагмент 2

[Fragment 1] [Fragment 2]

удалить Фрагмент 2

[Fragment 1]

добавить новый фрагмент 3

[Fragment 1] [Fragment 2]

При добавлении новых фрагментов все выглядит отлично. Как только я удаляю фрагмент, а затем добавляю новый фрагмент, старый фрагмент все еще отображается. Когда я иду .getClass.getName(), он дает мне имя Фрагмента 3, однако я все еще вижу Фрагмент 2.

Я полагаю, что это может быть проблема с instantiateItem() или чем-то подобным, но я думал, что адаптер должен справиться с этим за нас. Любые предложения были бы замечательными.

код адаптера...

public class MyPagerAdapter extends FragmentPagerAdapter {
public final ArrayList<Fragment> screens2 = new ArrayList<Fragment>();


private Context context;

public MyPagerAdapter(FragmentManager fm, Context context) {
    super(fm);
    this.context = context;
}

public void removeType(String name){
    for(Fragment f: screens2){
        if(f.getClass().getName().contains(name)){ screens2.remove(f); return; }
    }
    this.notifyDataSetChanged();
}

public boolean addSt(String tag, Class<?> clss, Bundle args){
    if(clss==null) return false;
    if(!clss.getName().contains("St")) return false; 
    if(!args.containsKey("cId")) return false;
    boolean has = false;
    boolean hasAlready = false;
    for(Fragment tab: screens2){
        if(tab.getClass().getName().contains("St")){
            has = true;
            if(tab.getArguments().containsKey("cId"))
                if(tab.getArguments().getLong("cId") == args.getLong("cId")){
                    hasAlready = true;
                }
            if(!hasAlready){
                // exists but is different so replace
                screens2.remove(tab);
                this.addScreen(tag, clss, args, C.PAGE_ST);
                // if returned true then it notifies dataset
                return true;
            }
        }
        hasAlready = false;
    }

    if(!has){ 
        // no st yet exist in adapter
        this.addScreen(tag, clss, args, C.PAGE_ST);
        return true;
    }

    return false;
}

public boolean removeCCreate(){
    this.removeType("Create");  
    return false;
}

@Override
public int getItemPosition(Object object) {

   return POSITION_NONE; //To make notifyDataSetChanged() do something
  }

public void addCCreate(){
    this.removeCCreate();
    Log.w("addding c", " ");
    this.addScreen("Create C",  CreateCFragment.class, null, C.PAGE_CREATE_C);
}

public void addScreen(String tag, Class<?> clss, Bundle args, int type){
    if(clss!=null){
        screens2.add(Fragment.instantiate(context, clss.getName(), args));
    }
}

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


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

}

Я понимаю, что код использует некоторые средства "гетто" для определения типа фрагмента, однако я написал этот код исключительно для тестирования функциональности. Любая помощь или идеи были бы замечательны, так как кажется, что не так много людей рискнули в мир FragmentPagerAdapters.


person Maurycy    schedule 30.01.2012    source источник
comment
кто-нибудь имел дело с этим раньше?   -  person Maurycy    schedule 30.01.2012
comment
У меня была аналогичная проблема с пользовательским FragmentPagerAdapter, когда я неправильно реализовал getItemPosition(Object) (на самом деле вообще). После добавления правильной реализации этого метода все заработало.   -  person joelpet    schedule 12.02.2014


Ответы (9)


У меня возникла та же проблема, и мое решение отменяло метод «destroyItem» следующим образом.

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    FragmentManager manager = ((Fragment)object).getFragmentManager();
    FragmentTransaction trans = manager.beginTransaction();
    trans.remove((Fragment)object);
    trans.commit();
}

Это работа для меня, у кого-нибудь есть другие решения?

Обновлено:

Я обнаружил, что этот код сделал фрагмент ненужным, поэтому я добавил условие, чтобы избежать этого.

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    if (position >= getCount()) {
        FragmentManager manager = ((Fragment) object).getFragmentManager();
        FragmentTransaction trans = manager.beginTransaction();
        trans.remove((Fragment) object);
        trans.commit();
    }
}
person Tericky Shih    schedule 24.04.2012
comment
Я дам это тебе. Не плохой способ справиться с этой проблемой. В итоге я сделал то же самое, захватив ссылку на свой фрагмент через менеджер фрагментов по сравнению со списком, и я просто создаю все три фрагмента одновременно и сам управляю переключением. ГД - person Maurycy; 30.04.2012
comment
Чувак, я бы купил тебе пива, если бы мог. Большое спасибо! - person The Awnry Bear; 09.02.2013
comment
Я один не могу это решить? Я уже пробовал этот метод, но он не работал. Тем не менее, всякий раз, когда я прихожу, чтобы создать новый фрагмент, он «перезагружает» ранее уничтоженный :-( - person Elad Avron; 09.10.2013
comment
@EladAvron Не могли бы вы описать свою ситуацию более подробно? - person Tericky Shih; 06.11.2013
comment
В конце концов я использовал решение mikepenz ниже, и оно сработало! Надо было вернуться, чтобы обновить этот комментарий. - person Elad Avron; 06.11.2013
comment
Потрясающе, как раз то, что я искал. - person Soham; 05.03.2014
comment
Есть вещь, которую я не понимаю, когда вызывается метод destroyItem? Код просто удаляет элемент из ArrayList, а не из FragmentPageAdapter: S - person Ricardo; 26.06.2014
comment
Разве вы не должны вызывать супер в случае, если вы не удалите фрагмент? - person kingston; 05.12.2014
comment
В вашем коде есть проблема в if (position >= getCount()) {, она должна быть if (position < getCount()) {. потому что позиция всегда ниже, чем getCount() - person SadeghAlavizadeh; 28.02.2016
comment
Но после удаления фрагмента, если я добавляю новый фрагмент, затем снова добавляется удаленный фрагмент - person Rajesh Nasit; 28.06.2017
comment
@SadeghAlavizadeh Так это вообще не нужно, не так ли? - person android developer; 31.10.2017
comment
это лучшее решение - person Tram Nguyen; 12.06.2018

Обновил этот пост и включил мое решение (если кто-то может улучшить, дайте мне знать)

Хорошо, теперь я решил свою проблему хакерским способом, но да, это работает;). Если кто-то может улучшить мое решение, пожалуйста, дайте мне знать. Для моего нового решения я теперь использую CustomFragmentStatePagerAdapter, но он не сохраняет состояние, как должно, и сохраняет все фрагменты в списке. Это может вызвать проблемы с памятью, если у пользователя более 50 фрагментов, как это делает обычный FragmentPagerAdapter. Было бы здорово, если бы кто-то мог добавить State-thing обратно в мое решение, не удаляя мои исправления. Спасибо.

Итак, вот мой CustomFragmentStatePagerAdapter.java

package com.tundem.webLab.Adapter;

import java.util.ArrayList;

import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter {
    private static final String TAG = "FragmentStatePagerAdapter";
    private static final boolean DEBUG = false;

    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;

    public ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
    public ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
    private Fragment mCurrentPrimaryItem = null;

    public CustomFragmentStatePagerAdapter(FragmentManager fm) {
        mFragmentManager = fm;
    }

    /**
     * Return the Fragment associated with a specified position.
     */
    public abstract Fragment getItem(int position);

    @Override
    public void startUpdate(ViewGroup container) {}

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do. This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.

        // DONE Remove of the add process of the old stuff
        /* if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } } */

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Fragment fragment = getItem(position);
        if (DEBUG)
            Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                try // DONE: Try Catch
                {
                    fragment.setInitialSavedState(fss);
                } catch (Exception ex) {
                    // Schon aktiv (kA was das heißt xD)
                }
            }
        }
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment);

        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        mCurTransaction.remove(fragment);

        /*if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment)
         * object).getView()); while (mSavedState.size() <= position) { mSavedState.add(null); } mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
         * mFragments.set(position, null); mCurTransaction.remove(fragment); */
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment) object).getView() == view;
    }

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        if (mSavedState.size() > 0) {
            state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
        }
        for (int i = 0; i < mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle) state;
            bundle.setClassLoader(loader);
            Parcelable[] fss = bundle.getParcelableArray("states");
            mSavedState.clear();
            mFragments.clear();
            if (fss != null) {
                for (int i = 0; i < fss.length; i++) {
                    mSavedState.add((Fragment.SavedState) fss[i]);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key : keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        while (mFragments.size() <= index) {
                            mFragments.add(null);
                        }
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
        }
    }
}

Вот мой обычный FragmentAdapter.java

package com.tundem.webLab.Adapter;

import java.util.LinkedList;
import java.util.List;

import android.support.v4.app.FragmentManager;

import com.tundem.webLab.fragments.BaseFragment;
import com.viewpagerindicator.TitleProvider;

public class FragmentAdapter extends CustomFragmentStatePagerAdapter implements TitleProvider {
    public List<BaseFragment> fragments = new LinkedList<BaseFragment>();

    private int actPage;

    public FragmentAdapter(FragmentManager fm) {
        super(fm);
    }

    public void setActPage(int actPage) {
        this.actPage = actPage;
    }

    public void addItem(BaseFragment fragment) {
        // TODO if exists don't open / change to that tab
        fragments.add(fragment);
    }

    public BaseFragment getActFragment() {
        return getItem(getActPage());
    }

    public int getActPage() {
        return actPage;
    }

    @Override
    public BaseFragment getItem(int position) {
        if (position < getCount()) {
            return fragments.get(position);
        } else
            return null;
    }

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

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

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }
}

И так я удаляю фрагмент. (Я знаю, что это немного больше, чем просто .remove() ). Не стесняйтесь улучшать мое решение, вы также можете добавить этот код куда-нибудь в адаптер, так что да. Это зависит от пользователя, который пытается реализовать это. Я использую это в моем TabHelper.java (класс, который обрабатывает все операции с вкладками, такие как удаление, добавление и т. д.)

    int act = Cfg.mPager.getCurrentItem();
    Cfg.mPager.removeAllViews();
    Cfg.mAdapter.mFragments.remove(act);
    try {
        Cfg.mAdapter.mSavedState.remove(act);
    } catch (Exception ex) {/* Already removed */}
    try {
        Cfg.mAdapter.fragments.remove(act);
    } catch (Exception ex) {/* Already removed */}

    Cfg.mAdapter.notifyDataSetChanged();
    Cfg.mIndicator.notifyDataSetChanged();

Описание кфг. предмет. Я сохраняю ссылку на эти объекты в классе cfg, поэтому я всегда могу использовать их без необходимости в специальном Factory.java...

Да. Я надеюсь, что смог помочь. Не стесняйтесь улучшать это, но дайте мне знать, чтобы я тоже мог улучшить свой код.

Спасибо.

Если я пропустил какой-либо код, дайте мне знать.


Мой старый ответ тоже работает, но только если у вас разные фрагменты. FileFragment, WebFragment, ... Нет, если вы используете один из этих типов фрагментов дважды.

У меня это псевдо работает на данный момент. Это действительно грязное решение, и я все еще ищу лучшее. Пожалуйста помоги.

Я изменил код, где я удаляю вкладку, на эту:

   public static void deleteActTab()
        {   
            //We set this on the indicator, NOT the pager
            int act = Cfg.mPager.getCurrentItem();
            Cfg.mAdapter.removeItem(act);
            List<BaseFragment> frags = new LinkedList<BaseFragment>();
            frags = Cfg.mAdapter.fragments;

            Cfg.mPager = (ViewPager)Cfg.act.findViewById(R.id.pager);
            Cfg.mPager.setAdapter(Cfg.mAdapter);
            Cfg.mIndicator.setViewPager(Cfg.mPager);

            Cfg.mAdapter.fragments = frags;

            if(act > 0)
            {
                Cfg.mPager.setCurrentItem(act-1);
                Cfg.mIndicator.setCurrentItem(act-1);
            }

            Cfg.mIndicator.notifyDataSetChanged();
        }

Если кто-то может улучшить этот код, дайте мне знать. Если кто-то может сказать нам реальный ответ на эту проблему. Пожалуйста, добавьте его сюда. Есть много людей, которые сталкиваются с этой проблемой. Я добавил репутации 50 тому, кто ее решит. Я также могу сделать пожертвование для того, кто ее решить.

Спасибо

person mikepenz    schedule 01.02.2012
comment
Я думаю, что на самом деле извлечение исходного кода для FragmentPagerAdapter может быть общим решением. Я также изменил его на FragmentStatePagerAdapter, протестировал свой код и понял, что могу получить дескриптор фрагментов и заставить их выполнять метод onCreateView(). Кроме того, переопределение getItemPosition (объект объекта) фактически перерисовывает представление в notifyDataSetChanged(), однако иногда оно неправильно перерисовывает представления. Поэтому, как только я протестирую все это, я вернусь с более конкретным решением. - person Maurycy; 02.02.2012
comment
Да, это было бы здорово. Можете ли вы прислать код, когда вы смогли решить его таким образом? Или опубликуйте весь код, чтобы я видел, что вы сделали. Вы по-прежнему используете разные фрагменты (FragmentA, FragmentB) (поэтому они не являются одним и тем же объектом/классом), верно? Было бы очень здорово, если бы мы смогли решить эту проблему. Если вы получите решение, разместите его в моей теме, то, возможно, вы получите репутацию (не знаю, как ее отдать, но да xD) - person mikepenz; 02.02.2012
comment
Опять же, это не конкретно, однако я создал интерфейс для всех своих фрагментов с помощью метода обновления. Таким образом, по сути, мое использование позволяет мне вызывать обновление для этого фрагмента и создавать новый список или что-то еще, не без проблем. Так что пока я думаю, что это то, с чем я буду идти. Это не очень помогает, но если ваши требования подходят, это вместе с FragmentStatePagerAdapter должно устранить любые проблемы. - person Maurycy; 02.02.2012
comment
Можете ли вы поделиться своим кодом? Как вы думаете, это будет работать для разных фрагментов, таких как FragmentA, FragmentB, FragmentC, даже если один фрагмент используется несколько раз? - person mikepenz; 02.02.2012
comment
Эй, у меня есть решение (своеобразное и хакерское) для нашей проблемы, перейдите к моему сообщению об ошибке. Возможно, это помогает. Ваше здоровье. ;) - person mikepenz; 04.02.2012
comment
Спасибо друг. Я решил свою проблему, просто перестроив фрагменты с правильными данными или обновив их с помощью пользовательского метода интерфейса. - person Maurycy; 09.02.2012
comment
Да! НАКОНЕЦ! Это решило все мои проблемы! Спасибо Спасибо спасибо! - person Elad Avron; 09.10.2013
comment
Создайте свои решения и переварите коды, я использую это как базовый класс в своем проекте, спасибо, что поделились им :) - person Fuong Lee; 27.02.2015

Возможно, вам поможет этот ответ.

Используйте FragmentStatePagerAdapter вместо FragmentPagerAdapter.

Поскольку FragmentPagerAdapter не уничтожает представления. Для получения дополнительной информации прочитайте этот ответ.

person Afshin    schedule 27.04.2016

Взяв «лучшее из обоих миров» (я имею в виду ответы @Tericky Shih и @mikepenz), у нас это коротко и просто:

public class MyPagerAdapter extends FragmentPagerAdapter {

    public ArrayList<Fragment> fragments = new ArrayList<Fragment>();    

    ...

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        if (position >= getCount()) fm.beginTransaction().remove((Fragment) object).commit();
    }

    @Override
    public int getItemPosition(Object object) {
        if (fragments.contains(object)) return fragments.indexOf(object);
        else return POSITION_NONE;
    }
}

Основное отличие состоит в том, что если какой-то фрагмент не изменился, вам не нужно уничтожать его представление и не нужно возвращать для него POSITION_NONE. В то же время я столкнулся с ситуацией, когда ViewPager держал ссылку на элемент, который уже был уничтожен, поэтому проверка if (fragments.contains(object)) помогает определить, не нужен ли этот элемент больше.

person Alex Kuzmin    schedule 12.03.2015

У меня была ситуация похожая на вашу. Недавно мне нужно было добавлять и удалять фрагменты из ViewPager. В первом режиме у меня есть фрагменты 0, 1 и 2, а во втором режиме у меня есть фрагменты 0 и 3. Я хочу, чтобы фрагмент 0 был одинаковым для обоих режимов и содержал информацию.

Все, что мне нужно было сделать, это переопределить FragmentPagerAdapter.getItemId, чтобы убедиться, что я возвращаю уникальный номер для каждого отдельного фрагмента — по умолчанию возвращается «позиция». Мне также пришлось снова установить адаптер в ViewPager - новый экземпляр будет работать, но я вернул его к тому же экземпляру. Установка адаптера приводит к тому, что ViewPager удаляет все представления и пытается добавить их снова.

Однако хитрость в том, что адаптер вызывает getItem только тогда, когда он хочет создать экземпляр фрагмента, а не каждый раз, когда он его показывает. Это связано с тем, что они кэшируются и ищет их по «позиции», возвращаемой getItemId.

Представьте, что у вас есть три фрагмента (0, 1 и 2), и вы хотите удалить «1». Если вы вернете «position» для getItemId, то удаление фрагмента 1 не сработает, потому что, когда вы попытаетесь отобразить фрагмент 2 после удаления фрагмента 1, пейджер/адаптер будет думать, что он уже получил фрагмент для этой «позиции», и продолжит отображать фрагмент 1. .

К вашему сведению: я попробовал notifyDataSetChanged вместо установки адаптера, но у меня это не сработало.

Во-первых, пример переопределения getItemId и то, что я сделал для своего getItem:

public class SectionsPagerAdapter extends FragmentPagerAdapter
{
    ...

    @Override
    public long getItemId(int position)
    {
        // Mode 1 uses Fragments 0, 1 and 2. Mode 2 uses Fragments 0 and 3
        if ( mode == 2 && position == 1 )
            return 3;
        return position;
    }

    @Override
    public Fragment getItem(int position)
    {
        if ( mode == 1 )
        {
            switch (position)
            {
                case 0:
                    return <<fragment 0>>;
                case 1:
                    return <<fragment 1>>;
                case 2:
                    return <<fragment 2>>;
            }
        }
        else    // Mode 2
        {
            switch (position)
            {
                case 0:
                    return <<fragment 0>>;
                case 1:
                    return <<fragment 3>>;
            }
        }
        return null;
    }
}

Теперь смена режима:

private void modeChanged(int newMode)
{
    if ( newMode == mode )
        return;

    mode = newMode;

    // Calling mSectionsPagerAdapter.notifyDataSetChanged() is not enough here
    mViewPager.setAdapter(mSectionsPagerAdapter);
}
person RowanPD    schedule 15.03.2015

У меня не получилось. Мое решение было помещено в мой проект FragmentStatePagerAdapter.java, переименованное в FragmentStatePagerAdapter2.java. В destroyItem() я немного изменился на основе журналов ошибок. От

// mFragments.set(position, null);

to

if (position < mFragments.size())mFragments.remove(position);

Возможно, у вас нет такой же проблемы, просто проверьте журнал. Надеюсь, это кому-то поможет!

person BruceDu    schedule 28.11.2014

После долгих попыток я заставил его работать так, чтобы он правильно удалял или присоединял третий фрагмент в конечной позиции.

Object fragments[] = new Object[3];
int mItems = 2;
MyAdapter mAdapter;
ViewPager mPager;


public void addFragment(boolean bool) {

    mAdapter.startUpdate(mPager);

    if (!bool) {
        mAdapter.destroyItem(mPager, 2, fragments[2]);
        mItems = 2;
        fNach = false;
    }
    else if (bool && !fNach){
        mItems = 3;
        mAdapter.instantiateItem(mPager,2);
        fNach = true;
    }
    mAdapter.finishUpdate(mPager);
    mAdapter.notifyDataSetChanged();

}

public class MyAdapter extends FragmentPagerAdapter {
    MyAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return mItems;
    }

    @Override
    public CharSequence getPageTitle(int position) {
    ... (code for the PagerTitleStrip)
    }

    @Override
    public Fragment getItem(int position) {
        Fragment f = null;
        switch (position) {
            case 0:
                f = new Fragment1();
                break;
            case 1:
                f = new Fragment2();
                break;
            case 2:
                f = new Fragment3();
                break;
        }
        return f;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container,position);
        fragments[position] = o;
        return o;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        System.out.println("Destroy item " + position);
        if (position >= getCount()) {
                FragmentManager manager = ((Fragment) object).getFragmentManager();
                FragmentTransaction ft = manager.beginTransaction();
                ft.remove((Fragment) object);
                ft.commit();
        }

    }
}

Некоторое пояснение: чтобы получить ссылку на объект для вызова destroyItem, я сохранил объекты, возвращенные из instanceItem, в массиве. Когда вы добавляете или удаляете фрагменты, вы должны объявить об этом с помощью startUpdate, FinishUpdate и notifyDataSetChanged. Количество элементов должно быть изменено вручную, для добавления вы увеличиваете его и создаете экземпляр, затем getItem создает его. Для удаления вы вызываете destroyItem, и в этом коде важно, чтобы позиция >= mItems, потому что destroyItem также вызывается, если фрагмент выходит из кеша. Вы не хотите, чтобы удалить его тогда. Единственное, что не работает, это анимация смахивания. После удаления последней страницы анимация «невозможно провести влево» не восстанавливается правильно на новой последней странице. Если вы проводите по нему, отображается пустая страница, и она возвращается обратно.

person Stephan Brunker    schedule 22.10.2016

Настоящая проблема заключается в том, что FragmentPagerAdapter использует позицию фрагмента в вашем списке в качестве идентификатора. Поэтому, если вы добавите новый список или просто удалите элементы, элемент "instantiateItem" найдет разные фрагменты для новых элементов в списке...

@Override
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);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }

    return fragment;
}

а также

  private static String makeFragmentName(int viewId, long id) {
    return "android:switcher:" + viewId + ":" + id;
}

а также

     * Return a unique identifier for the item at the given position.
 * <p>
 * <p>The default implementation returns the given position.
 * Subclasses should override this method if the positions of items can change.</p>
 *
 * @param position Position within this adapter
 * @return Unique identifier for the item at position
 */
public long getItemId(int position) {
    return position;
}
person stefan    schedule 31.12.2016

У меня была такая же проблема, пока меня не осенило, я создавал свой PagerView из другого фрагмента, а не из основного действия.

Мое решение состояло в том, чтобы передать диспетчера дочерних фрагментов конструктору Fragment(State)PagerAdapter, а не диспетчеру фрагментов родительского фрагмента.

Используя ChildFragmentManager, все фрагменты, созданные ViewPagerAdapter, автоматически очищаются при уничтожении родительского фрагмента.

person Eurospoofer    schedule 23.08.2019