Remover Fragment Page do ViewPager no Android

138

Estou tentando adicionar e remover dinamicamente fragmentos de um ViewPager, adicionando obras sem problemas, mas a remoção não funciona conforme o esperado.

Sempre que eu quero remover o item atual, o último é removido.

Também tentei usar um FragmentStatePagerAdapter ou retornar POSITION_NONE no método getItemPosition do adaptador.

O que estou fazendo de errado?

Aqui está um exemplo básico:

MainActivity.java

public class MainActivity extends FragmentActivity implements TextProvider {

    private Button mAdd;
    private Button mRemove;
    private ViewPager mPager;

    private MyPagerAdapter mAdapter;

    private ArrayList<String> mEntries = new ArrayList<String>();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mEntries.add("pos 1");
        mEntries.add("pos 2");
        mEntries.add("pos 3");
        mEntries.add("pos 4");
        mEntries.add("pos 5");

        mAdd = (Button) findViewById(R.id.add);
        mRemove = (Button) findViewById(R.id.remove);
        mPager = (ViewPager) findViewById(R.id.pager);

        mAdd.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                addNewItem();
            }
        });

        mRemove.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                removeCurrentItem();
            }
        });

        mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);

        mPager.setAdapter(mAdapter);

    }

    private void addNewItem() {
        mEntries.add("new item");
        mAdapter.notifyDataSetChanged();
    }

    private void removeCurrentItem() {
        int position = mPager.getCurrentItem();
        mEntries.remove(position);
        mAdapter.notifyDataSetChanged();
    }

    @Override
    public String getTextForPosition(int position) {
        return mEntries.get(position);
    }
    @Override
    public int getCount() {
        return mEntries.size();
    }


    private class MyPagerAdapter extends FragmentPagerAdapter {

        private TextProvider mProvider;

        public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
            super(fm);
            this.mProvider = provider;
        }

        @Override
        public Fragment getItem(int position) {
            return MyFragment.newInstance(mProvider.getTextForPosition(position));
        }

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

    }

}

TextProvider.java

public interface TextProvider {
    public String getTextForPosition(int position);
    public int getCount();
}

MyFragment.java

public class MyFragment extends Fragment {

    private String mText;

    public static MyFragment newInstance(String text) {
        MyFragment f = new MyFragment(text);
        return f;
    }

    public MyFragment() {
    }

    public MyFragment(String text) {
        this.mText = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View root = inflater.inflate(R.layout.fragment, container, false);

        ((TextView) root.findViewById(R.id.position)).setText(mText);

        return root;
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="add new item" />

    <Button
        android:id="@+id/remove"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="remove current item" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1" />

</LinearLayout>

fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/position"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:textSize="35sp" />

</LinearLayout>
astuetz
fonte
2
+1 para o código-fonte
wizurd
é correto usar construtor não padrão em MyFragment.java??
codeKiller

Respostas:

95

A solução de Louth não foi suficiente para fazer as coisas funcionarem para mim, pois os fragmentos existentes não estavam sendo destruídos. Motivado por esta resposta , descobri que a solução é substituir o getItemId(int position)método de FragmentPagerAdapterfornecer um novo ID exclusivo sempre que houver uma alteração na posição esperada de um fragmento.

Código fonte:

private class MyPagerAdapter extends FragmentPagerAdapter {

    private TextProvider mProvider;
    private long baseId = 0;

    public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
        super(fm);
        this.mProvider = provider;
    }

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(mProvider.getTextForPosition(position));
    }

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


    //this is called when notifyDataSetChanged() is called
    @Override
    public int getItemPosition(Object object) {
        // refresh all fragments when data set changed
        return PagerAdapter.POSITION_NONE;
    }


    @Override
    public long getItemId(int position) {
        // give an ID different from position when position has been changed
        return baseId + position;
    }

    /**
     * Notify that the position of a fragment has been changed.
     * Create a new ID for each position to force recreation of the fragment
     * @param n number of items which have been changed
     */
    public void notifyChangeInPosition(int n) {
        // shift the ID returned by getItemId outside the range of all previous fragments
        baseId += getCount() + n;
    }
}

Agora, por exemplo, se você excluir uma única guia ou alterar o pedido, ligue notifyChangeInPosition(1)antes de ligarnotifyDataSetChanged() , o que garantirá que todos os Fragmentos serão recriados.

Por que esta solução funciona

Substituindo getItemPosition ():

Quando notifyDataSetChanged()é chamado, o adaptador chama o notifyChanged()método ao ViewPagerqual está conectado. Em ViewPagerseguida, verifica o valor retornado pelos adaptadores getItemPosition()para cada item, removendo os itens que retornam POSITION_NONE(consulte o código-fonte ) e repovoando.

Substituindo getItemId ():

Isso é necessário para impedir que o adaptador recarregue o fragmento antigo quando ele ViewPagerestiver sendo preenchido novamente. Você pode entender facilmente por que isso funciona olhando o código-fonte para instantiateItem () em FragmentPagerAdapter.

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

Como você pode ver, o getItem()método é chamado apenas se o gerenciador de fragmentos não encontrar fragmentos existentes com o mesmo ID. Para mim, parece um bug que os fragmentos antigos ainda estão anexados mesmo depois de notifyDataSetChanged()serem chamados, mas a documentação para ViewPagerafirma claramente que:

Observe que esta classe está atualmente em design e desenvolvimento iniciais. A API provavelmente será alterada nas atualizações posteriores da biblioteca de compatibilidade, exigindo alterações no código-fonte dos aplicativos quando eles forem compilados na versão mais recente.

Portanto, esperamos que a solução alternativa fornecida aqui não seja necessária em uma versão futura da biblioteca de suporte.

Tim Rae
fonte
1
Muito obrigado por esta resposta! Foi difícil tentar resolver esse problema sem usar o FragmentStatePagerAdapter. Ótima explicação também. Você encontrou uma maneira de obter o getItem para acionar ao retornar ao fragmento? Eu tentei usar o seu inteligente notifyDataSetChanged () em vários lugares (ou seja, onStop) sem sucesso
u2tall
Eu realmente não entendo sua pergunta ... Por favor, poste uma nova pergunta, incluindo o código-fonte, se você não conseguir descobrir.
Tim Rae
1
2016, essa solução alternativa ainda é necessária para que notifyDataSetChanged()funcione.
Subin Sebastian
Uau! Explicação surpreendente. Eu tinha um viewpager bastante complexo, onde removia e insira itens dinamicamente e não o fazia funcionar sem entender tudo isso.
rompe
Ainda precisa dessa solução alternativa para substituir o fragmento antigo. Que droga.
precisa saber é o seguinte
291

O ViewPager não remove seus fragmentos com o código acima, porque carrega várias visualizações (ou fragmentos no seu caso) na memória. Além da vista visível, também carrega a vista em ambos os lados da vista visível. Isso fornece a rolagem suave de uma visualização para outra, o que torna o ViewPager tão legal.

Para alcançar o efeito desejado, você precisa fazer algumas coisas.

  1. Altere o FragmentPagerAdapter para um FragmentStatePagerAdapter. A razão para isso é que o FragmentPagerAdapter manterá todas as visualizações carregadas na memória para sempre. Onde o FragmentStatePagerAdapter descarta as visualizações que ficam fora das visualizações atuais e passíveis de travessia.

  2. Substitua o método do adaptador getItemPosition (mostrado abaixo). Quando ligamos, mAdapter.notifyDataSetChanged();o ViewPager interroga o adaptador para determinar o que mudou em termos de posicionamento. Usamos esse método para dizer que tudo mudou e reprocessar todo o seu posicionamento de exibição.

E aqui está o código ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

}
Louth
fonte
1
Ok, esta parece ser a solução mais fácil para esse problema. Embora algumas outras soluções alternativas sejam discutidas nos relatórios de erros aqui: code.google.com/p/android/issues/detail?id=19110 e aqui: code.google.com/p/android/issues/detail?id= 19001
Subin Sebastian
@Louth: estou usando o ViewPager com o TabsAdapter, conforme estendido do FragmentStatePagerAdapter (por exemplo, na página de referência do Google: developer.android.com/reference/android/support/v4/view/… ). Minha pergunta de acompanhamento é: como você exclui uma guia em primeiro lugar? Obrigado.
Gcl1
4
@Louth Quando chamamos notifyDataSetChanged (), o viewpager cria novos fragmentos, mas os mais antigos ainda estão na memória. E meu problema é; eles recebem uma chamada para seu evento onOptionsItemSelected. Como posso me livrar dos fragmentos antigos?
syloc
1
Obrigado, a substituição do FragmentPagerAdapter por um FragmentStatePagerAdapter resolveu o meu problema. Depois disso, basta remover AllViews () no ViewPager e recarregar fragmentos novamente dinamicamente.
Michal
17
Por que você está sempre retornando POSITION_NONE em vez de realmente procurar se uma determinada exibição / fragmento / objeto ainda existe e é válida? Este método deve responder à pergunta "ei, posso reutilizar este?" - e sempre dizendo "Não!" significa apenas recreação de tudo! Isso é desnecessário em muitos casos.
Zordid
14

minha solução de trabalho para remover a página de fragmento do modo de exibição pager

public class MyFragmentAdapter extends FragmentStatePagerAdapter {

    private ArrayList<ItemFragment> pages;

    public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
        super(fragmentManager);
        this.pages = pages;
    }

    @Override
    public Fragment getItem(int index) {
        return pages.get(index);
    }

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

    @Override
    public int getItemPosition(Object object) {
        int index = pages.indexOf (object);

        if (index == -1)
            return POSITION_NONE;
        else
            return index;
    }
}

E quando eu preciso remover alguma página por índice, faço isso

pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter

Aqui está a inicialização do meu adaptador

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
Vasil Valchev
fonte
Você poderia compartilhar código para adição de fragmento?
VVB 20/03/19
Mas ao adicioná-lo reflete lento após furto duas vezes / três vezes
VVB 21/03
se você não estiver removendo itens, ele retornará apenas itens, nada mais ... você está testando em vm ou em dispositivo real?
Vasil Valchev
Tente outras soluções, respostas com mais votos. Ver pager tem sistema de descontar muito complexo para um melhor desempenho em SWIP, por isso este hacks não são manipulados por originais pager vista Whit propósito ...
Vasil Valchev
11

O fragmento já deve ser removido, mas o problema foi o estado de salvamento do viewpager

Experimentar

myViewPager.setSaveFromParentEnabled(false);

Nada funcionou, mas isso resolveu o problema!

Felicidades !

Kathan Shah
fonte
2

Eu tive a ideia de simplesmente copiar o código-fonte android.support.v4.app.FragmentPagerAdpaterpara uma classe personalizada chamada CustumFragmentPagerAdapter. Isso me deu a chance de modificar o instantiateItem(...)modo que, toda vez que é chamado, ele remove / destrói o fragmento atualmente anexado antes de adicionar o novo fragmento recebido do getItem()método.

Simplesmente modifique instantiateItem(...)da seguinte maneira:

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

    // remove / destroy current fragment
    if (fragment != null) {
        mCurTransaction.remove(fragment);
    }

    // get new fragment and add it
    fragment = getItem(position);
    mCurTransaction.add(container.getId(), fragment,    makeFragmentName(container.getId(), itemId));

    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }

    return fragment;
}
Sven
fonte
1

Você pode combinar os dois para melhor:

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){

      if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
 return PagerAdapter.POSITION_NONE;
    }
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.

}
MSaudi
fonte
Isso só funciona se você estiver excluindo a guia da extrema direita, para excluir uma guia interna ou a guia esquerda, é necessário reordenar as guias e retornar a nova posição conforme indicado na documentação developer.android.com/reference/android/support/v4 / view /…
BitByteDog
1

Para futuros leitores!

Agora você pode usar o ViewPager2 para adicionar dinamicamente, remover fragmentos do viewpager.

Referência da API do formulário de cotação

O ViewPager2 substitui o ViewPager, abordando a maioria dos pontos problemáticos de seu antecessor, incluindo suporte de layout da direita para a esquerda, orientação vertical, coleções de fragmentos modificáveis ​​etc.

Veja MutableCollectionFragmentActivity.ktem googlesample / android-viewpager2 um exemplo de adição e remoção dinâmica de fragmentos do viewpager.


Para a sua informação:

Artigos:

Referência da API

Notas de versão

Repo de amostras: https://github.com/googlesamples/android-viewpager2

user158
fonte
Eu tenho 2 fragmentos mainfragment e otherfragment. Estou adicionando outro fragmento após cada 5ª posição e ele não está no arraylist, então como remover esse outro fragmento se não estiver na lista? Estou usando o viewpager2
b devloper 14/07
0

A resposta de Louth funciona bem. Mas acho que nem sempre retornar POSITION_NONE é uma boa ideia. Porque POSITION_NONE significa que o fragmento deve ser destruído e um novo fragmento será criado. Você pode verificar isso na função dataSetChanged no código fonte do ViewPager.

        if (newPos == PagerAdapter.POSITION_NONE) {
            mItems.remove(i);
            i--;
            ... not related code
            mAdapter.destroyItem(this, ii.position, ii.object);

Então, acho melhor você usar uma lista de array de fracaReference para salvar todos os fragmentos que você criou. E quando você adiciona ou remove alguma página, pode obter a posição correta em sua própria lista de matrizes.

 public int getItemPosition(Object object) {
    for (int i = 0; i < mFragmentsReferences.size(); i ++) {
        WeakReference<Fragment> reference = mFragmentsReferences.get(i);
        if (reference != null && reference.get() != null) {
            Fragment fragment = reference.get();
            if (fragment == object) {
                return i;
            }
        }
    }
    return POSITION_NONE;
}

De acordo com os comentários, getItemPosition é chamado quando a visualização do host está tentando determinar se a posição de um item foi alterada. E o valor de retorno significa sua nova posição.

Mas isso não é suficiente. Ainda temos um passo importante a dar. No código-fonte do FragmentStatePagerAdapter, há uma matriz chamada "mFragments" que armazena em cache os fragmentos que não são destruídos. E na função instantiateItem.

if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }

Ele retornou o fragmento em cache diretamente quando descobriu que o fragmento em cache não é nulo. Portanto, há um problema. Por exemplo, vamos excluir uma página na posição 2. Primeiramente, removemos esse fragmento de nossa própria lista de matriz de referência. portanto, em getItemPosition, ele retornará POSITION_NONE para esse fragmento e, em seguida, esse fragmento será destruído e removido de "mFragments".

 mFragments.set(position, null);

Agora o fragmento na posição 3 estará na posição 2. E o item instantiatedItem com a posição 3 do param será chamado. No momento, o terceiro item em "mFramgents" não é nulo; portanto, ele retornará diretamente. Mas, na verdade, o que retornou é o fragmento na posição 2. Então, quando entrarmos na página 3, encontraremos uma página vazia lá.

Para contornar esse problema. Meu conselho é que você pode copiar o código-fonte do FragmentStatePagerAdapter em seu próprio projeto e, ao adicionar ou remover operações, adicione e remova elementos na lista de matrizes "mFragments".

As coisas serão mais simples se você apenas usar o PagerAdapter em vez do FragmentStatePagerAdapter. Boa sorte.

passerbywhu
fonte
0

adicione ou remova fragmentos no viewpager dinamicamente.
Chame setupViewPager (viewPager) no início da atividade. Para carregar fragmentos diferentes, chame setupViewPagerCustom (viewPager). por exemplo, no botão, clique em ligar: setupViewPagerCustom (viewPager);

    private void setupViewPager(ViewPager viewPager)
{
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(new fragmnet1(), "HOME");
    adapter.addFrag(new fragmnet2(), "SERVICES");

    viewPager.setAdapter(adapter);
}

private void setupViewPagerCustom(ViewPager viewPager)
{

    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());

    adapter.addFrag(new fragmnet3(), "Contact us");
    adapter.addFrag(new fragmnet4(), "ABOUT US");


    viewPager.setAdapter(adapter);
}

//Viewpageradapter, handles the views

static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager){
        super(manager);
    }

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

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

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

    public void addFrag(Fragment fragment, String title){
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position){
        return mFragmentTitleList.get(position);
    }
}
Riad Hasan
fonte
0

Eu tive alguns problemas com FragmentStatePagerAdapter. Depois de remover um item:

  • havia outro item usado para uma posição (um item que não pertencia à posição, mas a uma posição ao lado)
  • ou algum fragmento não foi carregado (havia apenas um plano de fundo em branco visível nessa página)

Depois de muitas experiências, eu vim com a seguinte solução.

public class SomeAdapter extends FragmentStatePagerAdapter {

    private List<Item> items = new ArrayList<Item>();

    private boolean removing;

    @Override
    public Fragment getItem(int position) {
        ItemFragment fragment = new ItemFragment();
        Bundle args = new Bundle();
        // use items.get(position) to configure fragment
        fragment.setArguments(args);
        return fragment;
    }

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

    @Override
    public int getItemPosition(Object object) {
        if (removing) {
            return PagerAdapter.POSITION_NONE;
        }

        Item item = getItemOfFragment(object);

        int index = items.indexOf(item);
        if (index == -1) {
            return POSITION_NONE;
        } else {
            return index;
        }
    }

   public void addItem(Item item) {
        items.add(item);
        notifyDataSetChanged();
    }

    public void removeItem(int position) {
        items.remove(position);

        removing = true;
        notifyDataSetChanged();
        removing = false;
    }
}

Esta solução usa apenas um hack no caso de remover um item. Caso contrário (por exemplo, ao adicionar um item), ele mantém a limpeza e o desempenho de um código original.

Obviamente, do lado de fora do adaptador, você chama apenas addItem / removeItem, não é necessário chamar notifyDataSetChanged ().

Milan Laslop
fonte
0

Experimente esta solução. Eu usei a ligação de dados para a visualização de ligação. Você pode usar a função "findViewById ()" comum.

public class ActCPExpense extends BaseActivity implements View.OnClickListener,  {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;

private ViewPagerAdapter adapter;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        setContentView(R.layout.act_cp_loss);
        mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
        mContext = ActCPExpense.this;
        initViewsAct();


    } catch (Exception e) {
        LogUtils.LOGE(TAG, e);
    }

}


private void initViewsAct() {

    adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
    mBinding.viewpager.setAdapter(adapter);
    mBinding.tab.setViewPager(mBinding.viewpager);



}



@Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
    int i = itemActUtility.getItemId();
    if (i == android.R.id.home) {
        onBackPressed();

    } 

    return super.onOptionsItemSelected(itemActUtility);
}

@Override
public void onClick(View view) {
    super.onClick(view);
    int id = view.getId();
    if (id == R.id.btnAdd) {
        addFragment();

    } else if (id == R.id.btnDelete) {
        removeFragment();

    }


}
    private void addFragment(){
    adapter.addFragment(FragmentCPPayee.newInstance("Title");
    adapter.notifyDataSetChanged();
    mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
    adapter.removeItem(mBinding.viewpager.getCurrentItem());
    mBinding.tab.setViewPager(mBinding.viewpager);
}


class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();


    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

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


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

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


    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);

    }

    public void removeItem(int pos) {

        destroyItem(null, pos, mFragmentList.get(pos));
        mFragmentList.remove(pos);
        mFragmentTitleList.remove(pos);
        adapter.notifyDataSetChanged();
        mBinding.viewpager.setCurrentItem(pos - 1, false);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "Title " + String.valueOf(position + 1);
    }
}


}
Patel Jaimin
fonte
0

Eu adicionei uma função "clearFragments" e usei essa função para limpar o adaptador antes de definir os novos fragmentos. Isso chama as ações apropriadas de remoção de fragmentos. Minha classe pagerAdapter:

private class ChartPagerAdapter extends FragmentPagerAdapter{
    private ArrayList<Fragment> fragmentList;

    ChartPagerAdapter(FragmentManager fm){
        super(fm);
        fragmentList = new ArrayList<>();
    }

    void setFragments(ArrayList<? extends Fragment> fragments){
        fragmentList.addAll(fragments);
    }

    void clearFragments(){
        for(Fragment fragment:fragmentList)
            getChildFragmentManager().beginTransaction().remove(fragment).commit();
        fragmentList.clear();
    }

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

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

}
Azmi Rutkay BIYIK
fonte
0

eu resolvi esse problema com essas etapas

1- use FragmentPagerAdapter

2- em cada fragmento, crie um ID aleatório

fragment.id = new Random().nextInt();

3- substituir getItemPosition no adaptador

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

GetItemId de 4 substituições no adaptador

 @Override
public long getItemId(int position) {
    return mDatasetFragments.get(position).id;
}

5- agora o código de exclusão é

 adapter.mDatasetFragments.remove(< item to delete position >);
 adapter.notifyDataSetChanged();

isso funcionou para mim, espero ajudar

Ali Parsa
fonte
0

Meu código de versão final, corrigiu TODOS os erros. Levei 3 dias

Atualizado em 2020/07/18: mudei muito o código-fonte e consertei muitos bugs, mas não prometo que ainda funcione hoje.

https://github.com/lin1987www/FragmentBuilder/blob/master/commonLibrary/src/main/java/android/support/v4/app/FragmentStatePagerAdapterFix.java

public class FragmentStatePagerAdapterFix extends PagerAdapter {
    private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
    private static final boolean DEBUG = false;

    private WeakReference<FragmentActivity> wrFragmentActivity;
    private WeakReference<Fragment> wrParentFragment;
    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;

    protected ArrayList<Fragment> mFragments = new ArrayList<>();
    protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
    protected ArrayList<String> mFragmentTags = new ArrayList<>();
    protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
    protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();

    private Fragment mCurrentPrimaryItem = null;
    private boolean[] mTempPositionChange;

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

    public FragmentActivity getFragmentActivity() {
        return wrFragmentActivity.get();
    }

    public Fragment getParentFragment() {
        return wrParentFragment.get();
    }

    public FragmentStatePagerAdapterFix(FragmentActivity activity) {
        mFragmentManager = activity.getSupportFragmentManager();
        wrFragmentActivity = new WeakReference<>(activity);
        wrParentFragment = new WeakReference<>(null);
    }

    public FragmentStatePagerAdapterFix(Fragment fragment) {
        mFragmentManager = fragment.getChildFragmentManager();
        wrFragmentActivity = new WeakReference<>(fragment.getActivity());
        wrParentFragment = new WeakReference<>(fragment);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
        add(fragClass, null, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
        add(fragClass, args, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
        add(fragClass, null, tag);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
        add(fragClass, args, tag, getCount());
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
        mFragments.add(position, null);
        mFragmentStates.add(position, null);
        mFragmentTags.add(position, tag);
        mFragmentClassNames.add(position, fragClass.getName());
        mFragmentArgs.add(position, args);
        mTempPositionChange = new boolean[getCount()];
    }

    public void remove(int position) {
        if (position < getCount()) {
            mTempPositionChange = new boolean[getCount()];
            for (int i = position; i < mTempPositionChange.length; i++) {
                mTempPositionChange[i] = true;
            }
            mFragments.remove(position);
            mFragmentStates.remove(position);
            mFragmentTags.remove(position);
            mFragmentClassNames.remove(position);
            mFragmentArgs.remove(position);
        }
    }

    public void clear(){
        mFragments.clear();
        mFragmentStates.clear();
        mFragmentTags.clear();
        mFragmentClassNames.clear();
        mFragmentArgs.clear();
    }

    @Override
    public void startUpdate(ViewGroup container) {
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment;
        // 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.
        fragment = mFragments.get(position);
        if (fragment != null) {
            return fragment;
        }
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        FragmentState fs = mFragmentStates.get(position);
        if (fs != null) {
            fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
            // Fix bug
            // http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
            if (fragment.mSavedFragmentState != null) {
                fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
            }
        }
        if (fragment == null) {
            fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
        }
        if (DEBUG) {
            Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mFragmentStates.set(position, null);
        mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) {
            Log.v(TAG, "Removing item #" + position + ": f=" + object
                    + " v=" + ((Fragment) object).getView());
        }
        if (position < getCount()) {
            FragmentState fragmentState = new FragmentState(fragment);
            Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
            if (savedState != null) {
                fragmentState.mSavedFragmentState = savedState.mState;
            }
            mFragmentStates.set(position, fragmentState);
            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);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
            // Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
            for (Fragment fragment : mFragments) {
                if (fragment != null) {
                    fixActiveFragment(mFragmentManager, fragment);
                }
            }
        }
    }

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

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        // 目前顯示的 Fragments
        for (int i = 0; i < mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        if (mFragmentStates.size() > 0) {
            if (state == null) {
                state = new Bundle();
            }
            FragmentState[] fs = new FragmentState[mFragmentStates.size()];
            mFragmentStates.toArray(fs);
            state.putParcelableArray("states_fragment", fs);
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle) state;
            bundle.setClassLoader(loader);
            Parcelable[] fs = bundle.getParcelableArray("states_fragment");
            mFragments.clear();
            mFragmentStates.clear();
            mFragmentTags.clear();
            mFragmentClassNames.clear();
            mFragmentArgs.clear();
            if (fs != null) {
                for (int i = 0; i < fs.length; i++) {
                    FragmentState fragmentState = (FragmentState) fs[i];
                    mFragmentStates.add(fragmentState);
                    if (fragmentState != null) {
                        mFragmentArgs.add(fragmentState.mArguments);
                        mFragmentTags.add(fragmentState.mTag);
                        mFragmentClassNames.add(fragmentState.mClassName);
                    } else {
                        mFragmentArgs.add(null);
                        mFragmentTags.add(null);
                        mFragmentClassNames.add(null);
                    }
                    mFragments.add(null);
                }
            }
            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) {
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                        mFragmentArgs.set(index, f.mArguments);
                        mFragmentTags.set(index, f.mTag);
                        mFragmentClassNames.set(index, f.getClass().getName());
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
            // If restore will change
            notifyDataSetChanged();
        }
    }

    public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
        FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
        if (fm.mActive != null) {
            int index = fragment.mIndex;
            Fragment origin = fm.mActive.get(index);
            if (origin != null) {
                if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
                    Log.e(TAG,
                            String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
                                    origin.getClass().getName(), origin.mIndex,
                                    fragment.getClass().getName(), fragment.mIndex
                            ));
                }
            }
            fm.mActive.set(index, fragment);
        }
    }

    // Fix
    // http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
    @Override
    public int getItemPosition(Object object) {
        int index = mFragments.indexOf(object);
        if (index < 0) {
            return PagerAdapter.POSITION_NONE;
        }
        boolean isPositionChange = mTempPositionChange[index];
        int result = PagerAdapter.POSITION_UNCHANGED;
        if (isPositionChange) {
            result = PagerAdapter.POSITION_NONE;
        }
        return result;
    }
}
林奕 忠
fonte
Eu tenho 2 fragmentos mainfragment e otherfragment. Estou adicionando outro fragmento após cada 5ª posição e ele não está no arraylist, então como remover esse outro fragmento se não estiver na lista? Estou usando o viewpager2
b devloper 14/07
link do github não funciona
b devloper 14/07
-1

Você pode simplesmente substituir o destroyItemmétodo

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
yausername
fonte
-9

Espero que isso possa ajudar o que você deseja.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_UNCHANGED;
    }
}
Yin Khaing
fonte
9
este é exatamente o oposto da resposta aceita - como isso pode funcionar?
Zordid