Atualizar o ViewPager dinamicamente?

316

Não consigo atualizar o conteúdo no ViewPager.

Qual é o uso correto dos métodos instantiateItem () e getItem () na classe FragmentPagerAdapter?

Eu estava usando apenas getItem () para instanciar e retornar meus fragmentos:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

Isso funcionou bem. Exceto que não posso alterar o conteúdo.

Então encontrei o seguinte: ViewPager PagerAdapter não atualizando o View

"Minha abordagem é usar o método setTag () para qualquer exibição instanciada no método instantiateItem ()"

Agora eu quero implementar o instantiateItem () para fazer isso. Mas não sei o que tenho que retornar (o tipo é Object) e qual é a relação com getItem (int position)?

Eu li a referência :

  • resumo público Fragmento getItem (int position)

    Retorne o fragmento associado a uma posição especificada.

  • public Object instantiateItem (container ViewGroup, int position)

    Crie a página para a posição especificada. O adaptador é responsável por incluir a visualização no contêiner fornecido aqui, embora apenas seja necessário garantir que isso seja feito quando retornar de finishUpdate (ViewGroup). Parâmetros

    container A vista que contém a página em que a página será exibida. position A posição da página a ser instanciada.

    Devoluções

    Retorna um objeto que representa a nova página. Isso não precisa ser uma Visualização, mas pode ser outro contêiner da página.

mas ainda não entendi.

Aqui está o meu código. Estou usando o pacote de suporte v4.

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

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

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

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

MyFragmentAdapter

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

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

    public void setData(String[] data) {
        this.data = data;
    }

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

MyFragment

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

Aqui também está alguém com um problema semelhante, sem respostas http://www.mail-archive.com/[email protected]/msg200477.html

Ixx
fonte
Para entender melhor os componentes envolvidos, pode ser útil ler meu tutorial sobre um ViewPager com guias com histórico de navegação traseira separado. Ele vem com um exemplo de trabalho no GitHub e recursos adicionais: medium.com/@nilan/…
marktani
problema encontrado stackoverflow.com/questions/40149039/…
albert #
Você pode verificar este exemplo no GitHub . Espero que este exemplo ajude você.
Cabezas
Boa solução simples: stackoverflow.com/a/35450575/2162226
Gene Bo
O Google lançou recentemente o ViewPager2, que simplifica as atualizações dinâmicas de fragmento / exibição. Você pode ver exemplos em github.com/googlesamples/android-viewpager2 ou esta resposta em stackoverflow.com/a/57393414/1333448
Yves Delerm

Respostas:

605

Ao usar o FragmentPagerAdapter ou o FragmentStatePagerAdapter, é melhor lidar exclusivamente getItem()e não tocar instantiateItem()em nada. A interface instantiateItem()- destroyItem()- isViewFromObject()no PagerAdapter é uma interface de nível inferior que o FragmentPagerAdapter usa para implementar o muito mais simplesgetItem() interface .

Antes de entrar nisso, devo esclarecer que

se você quiser trocar os fragmentos reais que estão sendo exibidos, evite o FragmentPagerAdapter e use o FragmentStatePagerAdapter.

Uma versão anterior desta resposta cometeu o erro de usar o FragmentPagerAdapter como exemplo - isso não funcionará porque o FragmentPagerAdapter nunca destrói um fragmento depois que ele é exibido pela primeira vez.

Eu não recomendo o setTag()e findViewWithTag()solução fornecida no post é ligada. Como você descobriu, usar setTag()efindViewWithTag() não funciona com fragmentos, portanto, não é uma boa combinação.

A solução certa é substituir getItemPosition(). Quando notifyDataSetChanged()é chamado, o ViewPager chamagetItemPosition() todos os itens em seu adaptador para ver se eles precisam ser movidos para uma posição diferente ou removidos.

Por padrão, getItemPosition()retorna POSITION_UNCHANGED, o que significa: "Este objeto está bem onde está, não o destrua ou remova". RetornandoPOSITION_NONE corrige o problema dizendo: "Este objeto não é mais um item que estou exibindo, remova-o". Portanto, ele tem o efeito de remover e recriar todos os itens do seu adaptador.

Esta é uma correção completamente legítima! Essa correção faz o notifyDataSetChanged se comportar como um adaptador comum sem exibir a reciclagem. Se você implementar essa correção e o desempenho for satisfatório, estará pronto para a corrida. Tarefa concluída.

Se você precisar de melhor desempenho, poderá usar uma getItemPosition()implementação mais sofisticada . Aqui está um exemplo para um pager criando fragmentos de uma lista de strings:

ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();

FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
    public int getCount() {
        return titles.size();
    }

    public Fragment getItem(int position) {
        MyFragment fragment = new MyFragment();
        fragment.setTitle(titles.get(position));
        return fragment;
    }

    public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
});

Com esta implementação, apenas fragmentos que exibem novos títulos serão exibidos. Quaisquer fragmentos que exibam títulos que ainda estão na lista serão movidos para sua nova posição na lista, e fragmentos com títulos que não estão mais na lista serão destruídos.

E se o fragmento não tiver sido recriado, mas precisar ser atualizado de qualquer maneira? Atualizações para um fragmento vivo são melhor tratadas pelo próprio fragmento. Essa é a vantagem de ter um fragmento, afinal - é seu próprio controlador. Um fragmento pode adicionar um ouvinte ou um observador a outro objeto onCreate()e removê-lo onDestroy(), gerenciando as próprias atualizações. Você não precisa getItem()inserir todo o código de atualização como em um adaptador para um ListView ou outros tipos de AdapterView.

Uma última coisa - apenas porque o FragmentPagerAdapter não destrói um fragmento, não significa que getItemPosition seja completamente inútil em um FragmentPagerAdapter. Você ainda pode usar esse retorno de chamada para reordenar seus fragmentos no ViewPager. Porém, nunca os removerá completamente do FragmentManager.

Bill Phillips
fonte
4
Não funciona, ainda não está atualizando nada ... postei meu código.
Ixx
64
Ok, resolvi o problema - você precisa usar o FragmentStatePagerAdapter em vez do FragmentPagerAdapter. O FragmentPagerAdapter nunca remove fragmentos do FragmentManager, apenas os desanexa e os anexa. Verifique os documentos para obter mais informações - em geral, você só deseja usar o FragmentPagerAdapter para fragmentos permanentes. Eu editei meu exemplo com a correção.
Bill Phillips
1
Analisarei isso em algum momento mais tarde ... obrigado pela sua resposta. E então eu aceito sua resposta, se funcionar. Antes do seu último comentário, implementei para remover e adicionar um novo ViewPager a cada vez e isso funciona. Solução não muito boa, mas não tenho tempo agora para alterá-la.
Ixx
12
Você está certo, alterar a classe do adaptador para FragmentStatePagerAdapter corrigiu todos os meus problemas. Obrigado!
Ixx
2
Mesmo quando eu retorno POSITION_NONE, enquanto estiver usando FragmentStatePagerAdapter, posso ver que ele remove meu fragmento e cria a nova instância. Mas a nova instância recebe um BundleInstanceState salvo com o estado de um fragmento antigo. Como posso destruir completamente o fragmento para que o Bundle seja nulo quando criado novamente?
Cantou
142

Em vez de retornar POSITION_NONEa partir getItemPosition()e causando completo de recreação, faça o seguinte:

//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
    this.updateData = xyzData;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(updateData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

Seus fragmentos devem implementar a UpdateableFragmentinterface:

public class SomeFragment extends Fragment implements
    UpdateableFragment{

    @Override
    public void update(UpdateData xyzData) {
         // this method will be called for every fragment in viewpager
         // so check if update is for this fragment
         if(forMe(xyzData)) {
           // do whatever you want to update your UI
         }
    }
}

e a interface:

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}

Sua classe de dados:

public class UpdateData {
    //whatever you want here
}
M-WaJeEh
fonte
1
Isso significa que sua MainActivity também precisa implementar a interface ??? Por favor me ajude aqui. Na minha implementação atual, estou usando minha classe MainActivity como um comunicador entre os fragmentos, então fiquei confuso com esta solução.
Rakeeb Rajbhandari
1
Ok eu ter respondido em outro lugar com a plena aplicação onde eu estou atualizando Fragmentdinamicamente, dê uma olhada: stackoverflow.com/questions/19891828/...
M-Wajeeh
@ M-WaJeEh talvez algum exemplo, por favor !!! Eu tenho uma lista com uma lista de cidades e depois de selecionar a cidade abre o viewpager (3 páginas) com informações sobre esta cidade. funciona ok mas depois de selecionar outro visor da cidade, apenas a d page on refreshes the 1-st page only after swiping pages? thw 2página em 3 d estará sempre em branco. graças
SERG
4
entre todas as coisas que vi nos últimos 2,5 dias da minha vida profissional .. esse é o único que funcionou para mim .. todos os tipos de arigato, obrigado, spaciba, sukran .. n.
Orkun Ozen
2
Mãe dos Dragões !, isso funciona como um encanto, a única solução funcionou para mim ... thx muito !!
Sebastián Guerrero
24

para aqueles que ainda enfrentam o mesmo problema que eu enfrentei antes quando tenho um ViewPagercom 7 fragmentos. o padrão para esses fragmentos para carregar o conteúdo em inglês do APIserviço, mas o problema aqui que eu quero alterar o idioma da atividade de configurações e após concluir a atividade de configurações, quero ViewPagerna atividade principal atualizar os fragmentos para corresponder à seleção de idioma do usuário e carregar o conteúdo em árabe, se o usuário escolher árabe aqui, o que eu fiz para trabalhar desde a primeira vez

1- Você deve usar o FragmentStatePagerAdapter conforme mencionado acima.

2- on mainActivity, substitui o onResume e fiz o seguinte

if (!(mPagerAdapter == null)) {
    mPagerAdapter.notifyDataSetChanged();
}

3-i substituiu o getItemPosition()no mPagerAdapter e o fez retornar POSITION_NONE.

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

funciona como charme

Ziad Gholmish
fonte
1
Funciona, exceto em uma situação. Quando você exclui o último item no viewPager. Então não remove da lista.
Vlado Pandžić
Eu estava adicionando fragmentos ao lado esquerdo e direito do fragmento atual no viewpager dinamicamente. Este funcionou! Obrigado.
H8pathak
15

Encontrei esse problema e finalmente o resolvi hoje, então escrevo o que aprendi e espero que seja útil para alguém que seja novo no Android ViewPagere atualize como eu. Estou usando FragmentStatePagerAdapterno nível 17 da API e atualmente tenho apenas 2 fragmentos. Eu acho que deve haver algo errado, por favor me corrija, obrigado. insira a descrição da imagem aqui

  1. Dados serializados devem ser carregados na memória. Isso pode ser feito usando um CursorLoader/ AsyncTask/ Thread. O carregamento automático depende do seu código. Se você estiver usando um CursorLoader, ele será carregado automaticamente, pois há um observador de dados registrado.

  2. Depois de ligar viewpager.setAdapter(pageradapter), o adaptador getCount()é constantemente chamado para criar fragmentos. Portanto, se os dados estiverem sendo carregados, é getCount()possível retornar 0, portanto, você não precisa criar fragmentos fictícios para nenhum dado mostrado.

  3. Depois que os dados são carregados, o adaptador não cria fragmentos automaticamente, pois getCount()ainda é 0, para que possamos definir o número de dados realmente carregado a ser retornado getCount()e depois chamar o adaptador notifyDataSetChanged(). ViewPagercomece a criar fragmentos (apenas os 2 primeiros) por dados na memória. É feito antes de notifyDataSetChanged()ser retornado. Em seguida, ViewPagertem os fragmentos certos que você precisa.

  4. Se os dados no banco de dados e a memória forem atualizados (gravação), ou apenas os dados na memória forem atualizados (gravação novamente), ou apenas os dados no banco de dados serão atualizados. Nos últimos dois casos, se os dados não forem carregados automaticamente do banco de dados para a memória (conforme mencionado acima). O ViewPageradaptador e pager apenas lida com dados na memória.

  5. Portanto, quando os dados na memória são atualizados, precisamos chamar os adaptadores notifyDataSetChanged(). Como o fragmento já foi criado, os adaptadores onItemPosition()serão chamados antes do notifyDataSetChanged()retorno. Nada precisa ser feito getItemPosition(). Em seguida, os dados são atualizados.

oscarthecat
fonte
Eu descobri que nada precisa fazer depois de atualizar os dados (na memória), basta ligar notifyDataSetChanged()e apenas atualizar automaticamente, porque o fragmento (com chartview) que eu uso se atualizará. se não é, como um fragmento estático, eu vou ter que chamar onItemPositon()e retorna POSITION_NONE sinto muito por isso
oscarthecat
Alguém tem uma idéia de qual ferramenta foi usada para fazer esse diagrama?
sumo de
12

Experimente o destroyDrawingCache()ViewPager depois notifyDataSetChanged()no seu código.

czaku
fonte
3
O mesmo aqui. Funciona muito bem, obrigado. Eu tive um momento especialmente difícil, porque não estou usando Fragmentsmeu pager de exibição. Então obrigado!
Seth
11

Depois de horas de frustração ao tentar todas as soluções acima para superar esse problema e também tentar muitas soluções em outras questões semelhantes, como este , este e este que todos falharam comigo para resolver este problema e para fazer a ViewPagerdestruir o velho Fragmente encher o pagercom o novo Fragments. Resolvi o problema da seguinte maneira:

1) Faça a ViewPageraula estender da FragmentPagerAdapterseguinte maneira:

 public class myPagerAdapter extends FragmentPagerAdapter {

2) Crie um item para ViewPageresse armazenamento titlee o fragmentseguinte:

public class PagerItem {
private String mTitle;
private Fragment mFragment;


public PagerItem(String mTitle, Fragment mFragment) {
    this.mTitle = mTitle;
    this.mFragment = mFragment;
}
public String getTitle() {
    return mTitle;
}
public Fragment getFragment() {
    return mFragment;
}
public void setTitle(String mTitle) {
    this.mTitle = mTitle;
}

public void setFragment(Fragment mFragment) {
    this.mFragment = mFragment;
}

}

3) Faça o construtor do ViewPagertake minha FragmentManagerinstância para armazená-lo no meu da classseguinte maneira:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mPagerItems = pagerItems;
}

4) Crie um método para redefinir oadapter dados com os novos dados, eliminando todos os anteriores fragmentda fragmentManagerprópria diretamente para fazer a adapterpara definir o novo fragmenta partir da nova lista novamente da seguinte forma:

public void setPagerItems(ArrayList<PagerItem> pagerItems) {
    if (mPagerItems != null)
        for (int i = 0; i < mPagerItems.size(); i++) {
            mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        }
    mPagerItems = pagerItems;
}

5) No contêiner Activityou Fragmentnão reinicialize o adaptador com os novos dados. Defina os novos dados através do método setPagerItemscom os novos dados da seguinte maneira:

ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
    pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
    pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

    mPagerAdapter.setPagerItems(pagerItems);
    mPagerAdapter.notifyDataSetChanged();

Espero que ajude.

Sami Eltamawy
fonte
você pode me ajudar no meu problema stackoverflow.com/questions/40149039/...
albert
você pode me ajudar com esse problema stackoverflow.com/questions/41547995/…
viper
10

Por alguma razão, nenhuma das respostas funcionou para mim, então tive que substituir o método restoreState sem chamar super no meu fragmentStatePagerAdapter. Código:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {}

}
hordurh
fonte
1
Depois de tentar tudo na resposta do SO, esse foi o que funcionou para mim. Estou terminando () uma atividade que está retornando à Atividade que hospeda o PagerAdapter em questão. Quero que todos os meus fragmentos sejam recriados nesse caso, porque a Atividade que acabei de concluir pode ter alterado o estado usado para desenhar os fragmentos.
JohnnyLambada
OMG .. funciona .. funciona .. parabéns: D No meu caso, preciso remover o objeto de item atual do viewpager. mas o fragmento atual não está sendo atualizado depois de tentar todos os métodos / etapas acima.
Rahul Khurana
3

Modifiquei levemente a solução fornecida por Bill Phillips para atender às minhas necessidades

private class PagerAdapter extends FragmentStatePagerAdapter{
    Bundle oBundle;
    FragmentManager oFragmentManager;
    ArrayList<Fragment> oPooledFragments;

public PagerAdapter(FragmentManager fm) {
    super(fm);
    oFragmentManager=fm;

    }
@Override
public int getItemPosition(Object object) {

    Fragment oFragment=(Fragment)object;
    oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
    if(oPooledFragments.contains(oFragment))
        return POSITION_NONE;
    else
        return POSITION_UNCHANGED;
    } 
}

para que os getItemPosition()retornos POSITION_NONEapenas dos fragmentos que estão atualmente no FragmentManagerquando getItemPositionsejam chamados. (Observe que isso FragmentStatePagere o ViewPagerassociado a ele estão contidos em um fragmento que não está em uma atividade)

aguardar
fonte
2

Eu tive um problema semelhante, mas não quero confiar nas soluções existentes (nomes de códigos codificados etc.) e não consegui fazer a solução da M-WaJeEh funcionar para mim. Aqui está a minha solução:

Eu mantenho referências aos fragmentos criados no getItem em uma matriz. Isso funciona bem, desde que a atividade não seja destruída devido a configurationChange ou falta de memória ou qualquer outra coisa (-> ao retornar à atividade, os fragmentos retornam ao seu último estado sem que 'getItem' seja chamado novamente e, portanto, sem atualizar a matriz )

Para evitar esse problema, implementei o instantiateItem (ViewGroup, int) e atualizei minha matriz, assim:

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

Então, por um lado, estou feliz por ter encontrado uma solução que funciona para mim e queria compartilhá-la com você, mas também queria perguntar se outra pessoa tentou algo semelhante e se há alguma razão para que eu não deva. faz assim? Até agora, funciona muito bem para mim ...

jpm
fonte
Eu fiz algo relacionado em stackoverflow.com/a/23427215/954643 , embora você provavelmente deve remover o item myFragmentsem public void destroyItem(ViewGroup container, int position, Object object)tão bem como você não pegar uma referência fragement obsoleto.
Qix 8/14
1

Eu uso a biblioteca EventBus para atualizar o Fragmentconteúdo ViewPager. A lógica é simples, assim como o documento do EventBus, como fazer . Não é necessário controlarFragmentPagerAdapter instância. O código está aqui:

1: Definir eventos

Defina qual mensagem é necessária para atualizar.

public class UpdateCountEvent {
    public final int count;

    public UpdateCountEvent(int count) {
        this.count = count;
    }
}

2.Prepare assinantes

Escreva o código abaixo na atualização do fragmento necessário.

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);  
    super.onStop();
}

public void onEvent(UpdateCountEvent event) {//get update message
    Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}

3.Postar eventos

Escreva o código abaixo em outro Activityou em outro Fragmentque precise atualizar o parâmetro

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));
Fantasy Fang
fonte
1

Se você deseja usar o FragmentStatePagerAdapter, consulte https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Owner%20Summary% 20Estrelas & groupby = & sort = & id = 37990 . Há problemas com o FragmentStatePagerAdapter que podem ou não causar problemas ao seu caso de uso.

Além disso, o link também tem poucas soluções ... poucos podem atender às suas necessidades.

cgr
fonte
A partir desse URL, encontrei a solução. Eu usei o FragmentStatePagerAdapter modificado a partir desta lista gist.github.com/ypresto/8c13cb88a0973d071a64 no meu código e ele começou a funcionar como deveria.
Rafael
Legal. Sim. O link também tem poucas soluções.
cgr
1

Vivi o mesmo problema e procurei muitas vezes. Qualquer resposta dada no stackoverflow ou via google não foi a solução para o meu problema. Meu problema foi fácil. Eu tenho uma lista, mostro essa lista com o viewpager. Quando adiciono um novo elemento ao cabeçalho da lista e atualizo o nada do viewpager alterado. Minha solução final foi muito fácil de usar. Quando um novo elemento é adicionado à lista e deseja atualizar a lista. Primeiramente, defina o adaptador do viewpager como nulo, em seguida, recrie o adaptador e defina i como viewpager.

myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);

Verifique se o seu adaptador deve estender o FragmentStatePagerAdapter

nebyan
fonte
1

Aqui está minha implementação que incorpora as informações do @Bill Phillips One, que recebe o cache de fragmentos na maioria das vezes, exceto quando os dados foram alterados. Simples e parece funcionar bem.

MyFragmentStatePagerAdapter.java

private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
        this.mIsUpdating = mIsUpdating;
    }


@Override
public int getItemPosition(@NonNull Object object) {

    if (mIsUpdating) {
        return POSITION_NONE;
    }
    else {
        return super.getItemPosition(object);
    }
}

MyActivity.java

mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);
voam
fonte
0

Eu estava tentando tantas abordagens diferentes, nenhuma realmente afetou meu problema. Abaixo está como eu resolvo isso com uma mistura de soluções fornecidas por todos vocês. Obrigado a todos.

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

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

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

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

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

Eu só quero atualizar a página 0 após onResume ().

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

@Override
protected void onResume() {
    super.onResume();

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

No meu FragmentsMain, há uma "página" pública inteira, que pode me dizer se é a página que eu quero atualizar.

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;
Ted Hsieh
fonte
0

Eu sei que estou atrasado para o partido. Corrigi o problema ligando TabLayout#setupWithViewPager(myViewPager);logo apósFragmentPagerAdapter#notifyDataSetChanged();

theapache64
fonte
0

Essa solução não funcionará para todos, mas no meu caso, cada fragmento no meu ViewPager é de classe diferente e apenas um deles existe por vez.

Com essa restrição, esta solução é segura e deve ser segura para uso na produção.

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

Se você tiver várias versões do mesmo fragmento, poderá usar essa mesma estratégia para chamar métodos nesses fragmentos para determinar se é o fragmento que deseja atualizar.

Kevin Grant
fonte
0

Isso pode ser útil para alguém - no meu caso, ao inserir uma nova página, o pager de exibição estava solicitando a posição de um fragmento existente duas vezes, mas não solicitando a posição do novo item, causando comportamento incorreto e dados não sendo exibidos.

  1. Copie a fonte para o FragmentStatePagerAdapter (parece não ter sido atualizado há muito tempo).

  2. Substituir notifyDataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. Adicione uma verificação de integridade para destroyItem () para evitar falhas:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }
Homem malvado
fonte
0

Eu passei por todas as respostas acima e várias outras postagens, mas ainda não consegui encontrar algo que funcionasse para mim (com diferentes tipos de fragmentos, além de adicionar e remover abas dinamicamente). A abordagem a seguir da FWIW é o que funcionou para mim (caso alguém tenha os mesmos problemas).

public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
    private static final String TAB1_TITLE = "Tab 1";
    private static final String TAB2_TITLE = "Tab 2";
    private static final String TAB3_TITLE = "Tab 3";

    private ArrayList<String> titles = new ArrayList<>();
    private Map<Fragment, Integer> fragmentPositions = new HashMap<>();


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


    public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
        titles.clear();

        if (showTab1) {
            titles.add(TAB1_TITLE);
        }
        if (showTab2) {
            titles.add(TAB2_TITLE);
        }
        if (showTab3) {
            titles.add(TAB3_TITLE);
        }
        notifyDataSetChanged();
    }


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

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;

        String tabName = titles.get(position);
        if (tabName.equals(TAB1_TITLE)) { 
            fragment =  Tab1Fragment.newInstance();
        } else if (tabName.equals(TAB2_TITLE)) {
            fragment =  Tab2Fragment.newInstance();
        } else if (tabName.equals(TAB3_TITLE)) {
            fragment =  Tab3Fragmen.newInstance();
        } 

        ((BaseFragment)fragment).setTitle(tabName);
        fragmentPositions.put(fragment, position);
        return fragment;
    }

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

    @Override
    public int getItemPosition(Object item) {
        BaseFragment fragment = (BaseFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        Integer fragmentPosition = fragmentPositions.get(item);
        if (fragmentPosition != null && position == fragmentPosition) {
            return POSITION_UNCHANGED;
        } else {
            return POSITION_NONE;
        }
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        fragmentPositions.remove(object);
    }
}
John O'Reilly
fonte
0

Use FragmentStatePagerAdapter em vez de FragmentPagerAdapter se desejar recriar ou recarregar fragmentos com base no índice. Por exemplo, se você deseja recarregar fragmentos diferentes de FirstFragment, verifique a instância e retorne a posição como esta

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }
HemangNirmal
fonte
0

Você precisa alterar o elemento mFragments de instantiateItem, getItemPosition.

    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);

        if (f != null) {
            int newPosition = getItemPosition(f);
            if (newPosition == POSITION_UNCHANGED) {
                return f;
            } else if (newPosition == POSITION_NONE) {
                mFragments.set(position, null);
            } else {
                mFragments.set(newPosition, f);
            }
        }
    }

Baseado no AndroidX FragmentStatePagerAdapter.java, porque mFragments a posição dos elementos não muda ao chamar notifyDataSetChanged ().

Fonte: https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

Exemplo: https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

Você pode executar este projeto para confirmar como trabalhar. https://github.com/cuichanghao/infivt

changhao cui
fonte