ViewPager.setOffscreenPageLimit (0) não funciona como esperado

100

Os fragmentos que uso em minha ViewPagerinstância consomem muitos recursos, portanto, gostaria de carregar apenas um de cada vez. Quando tento o seguinte:

mViewPager.setOffscreenPageLimit(0);
mViewPager.setAdapter(mPagerAdapter);

Minha FragmentStatePagerAdapter.getItem(int position)função de substituição é chamada 3 vezes, que é o que acontece quando eu chamo mViewPager.setOffscreenPageLimit(1). Eu esperaria que ele fosse chamado apenas uma vez, porque eu especifiquei 0 páginas fora da tela.

Acredito que estou chamando tudo corretamente, pois se eu ligar mViewPager.setOffscreenPageLimit(2), FragmentStatePagerAdapter.getItem(int position)é chamado 5 vezes como eu esperava.

O ViewPager requer no mínimo 1 página fora da tela ou estou fazendo algo errado aqui?

Chris Lacy
fonte
9
Eu adicionei uma solicitação de recurso: code.google.com/p/android/issues/detail?id=56667
Marcos
Eu tinha apenas 3 guias e a configuração ViewPager.setOffscreenPageLimit(2)funcionava para mim. TenteViewPager.setOffscreenPageLimit(totTabs - 1)
sibin

Respostas:

112

O ViewPager requer no mínimo 1 página fora da tela

Sim. Se estou lendo o código-fonte corretamente, você deve receber um aviso sobre isso no LogCat, algo como:

Requested offscreen page limit 0 too small; defaulting to 1
CommonsWare
fonte
6
Isso não é uma solução! Eu quero carregar apenas 1 fragmento, não 2. Certamente isso é possível de alguma forma?
HGPB
14
@Haraldo: "Certamente isso é possível de alguma forma?" - não com ViewPager. ViewPagercria esses fragmentos para que as visualizações existam, de modo que o usuário possa deslizar entre eles, com o efeito animado mostrando a visualização antiga deslizando para fora da tela e a nova deslizando para a tela. Sinta-se à vontade para tentar escrever o seu próprio, ViewPagerque pode deslizar entre coisas que não existem. Você pode ler mais sobre isso em code.google.com/p/android/issues/detail?id=56667#c3
CommonsWare
2
Sim, eu li esta edição. Simplesmente não é lógico. Espero ser capaz setOffscreenPageLimit(0)de 0. Então, posso lidar com as consequências sozinho. Um fragmento vazio padrão pode ser um espaço reservado, por exemplo, até que o fragmento seja carregado. Tudo isso poderia ocorrer fora do thread da IU. De qualquer forma, não pensei muito nisso.
HGPB
5
@Haraldo: "Um fragmento vazio padrão pode ser um espaço reservado, por exemplo, até que o fragmento seja carregado" - você é bem-vindo para ter um espaço reservado em seu fragmento hoje, com a implementação existente de ViewPager, um que você substitui pelo conteúdo real quando disponível .
CommonsWare
2
onPageChangeListener parece ser a solução.
alicanbatur
123

A melhor maneira que encontrei foi setUserVisibleHint
adicionar isso ao seu fragmento

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}
Tyler Davis
fonte
1
+++++ para esta solução !!! obrigado, depois de adicionar este código eu obtive o NPL pela primeira vez, acabei de adicionar o bloco try catch e funciona perfeitamente para mim !!!
Bhavin Chauhan
1
Ao usar isso, ele não carrega a data no fragmento secondViewPager, no segundo fragmento seus dados de carregamento do terceiro fragmento. Alguém pode me ajudar?
Arshad de
Estou faltando alguma coisa, setUserVisibleHint chamado antes de onCreateView
Anton Makov
A melhor solução agora está obsoleta
M.Sameer
@ M.Sameer alguma alternativa para setUserVisibleHint depreciar?
Yudi karma
8

Você pode tentar assim:

public abstract class LazyFragment extends Fragment {
    protected boolean isVisible;
    /**
     * 在这里实现Fragment数据的缓加载.
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(getUserVisibleHint()) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }
    protected void onVisible(){
        lazyLoad();
    }
    protected abstract void lazyLoad();
    protected void onInvisible(){}

protected abstract void lazyLoad();
protected void onInvisible(){}
Terry
fonte
essa melhor resposta!
Radesh,
7

Primeiro Adicionar

   boolean isFragmentLoaded = false;

do que

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser && !isFragmentLoaded) {
        //Load Your Data Here like.... new GetContacts().execute();

        isFragmentLoaded = true;
    }
else{
     }
}
Arpit J.
fonte
6
mas este método chama antes de onCreateView
Zar E Ahmer
2

ViewPager é o padrão para carregar a próxima página (Fragmento) que você não pode alterar por setOffscreenPageLimit (0). Mas você pode fazer algo para hackear. Você pode implementar a função onPageSelected em Activity contendo o ViewPager. No próximo Fragment (que você não deseja carregar), você escreve uma função, digamos showViewContent (), onde você insere todo o código init de consumo de recursos e não faz nada antes do método onResume (). Em seguida, chame a função showViewContent () dentro de onPageSelected. Espero que isso ajude.

Friends_little_black
fonte
1

este pode ser um tópico antigo, mas parece funcionar para mim. Substitua esta função:

@Override
public void setMenuVisibility(boolean menuVisible) { 
    super.setMenuVisibility(menuVisible);

    if ( menuVisible ) {
        /**
         * Load your stuffs here.
         */
    } else  { 
        /**
         * Fragment not currently Visible.
         */
    } 
}

felizes codificações ...

Ralphgabb
fonte
o que está no webview? Eu sugiro que você crie um novo tópico para isso.
ralphgabb
1

no meu caso, eu queria iniciar algumas animações em visualizações, mas com setUserVisibleHint tive alguns problemas ...
minha solução é:

1 / addOnPageChangeListener para o seu adaptador:

mViewPager.addOnPageChangeListener(this);

2 / implementar OnPageChangeListener:

public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener

3 / substituir os 3 métodos:

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{

}

@Override
public void onPageSelected(int position)
{ 
}

@Override
public void onPageScrollStateChanged(int state)
{
}

4 / declare e inicialize esta variável em sua classe

private static int mTabState = 1;

aviso : eu tenho três fragmentos em meu adaptador e uso mTabState para setCurrentItem e a posição atual do adaptador que reconhece qual fragmento é mostrado ao usuário a tempo ... 5 / no método onPageSelected adicione estes códigos:

if (mTabState == 0 || position == 0)
    {
        Intent intent = new Intent("animation");
        intent.putExtra("current_position", position);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
    }

se a página anterior ou a página atual for a página 0 (fragmento na posição 0), faça isso

6 / agora em sua classe de fragmento (fragmento na posição 0 do adaptador), você deve criar broadcast receiver e registrá-lo no método onResume e cancelar o registro onPause methos:

BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (Objects.equals(intent.getAction(), "animation"))
        {
            int currentPosition = intent.getIntExtra("current_position", 0);
            if (currentPosition == 0)
            {
                startAnimation();
                setViewsVisible();
            } else
            {
                setViewsInvisible();
            }
        }
    }
};

@Override
public void onResume()
{
    super.onResume();
    LocalBroadcastManager.getInstance(mContext).registerReceiver(broadcastReceiver, new IntentFilter("animation"));
}

@Override
public void onPause()
{
    super.onPause();
    LocalBroadcastManager.getInstance(mContext).unregisterReceiver(broadcastReceiver);
}

resumo: eu tenho Fragment Pager Adapter que mostra três fragmentos nele, eu quero mostrar algumas animações em Views em Fragment na posição 0 do Adapter, para isso eu uso BroadcastReceiver. Quando o fragmento é selecionado, eu inicio o método de animação e mostro as visualizações para o usuário, quando o fragmento não está sendo exibido para o usuário, tento visualizar as visualizações ...

AREF
fonte
1

Tente este código para resolver o problema de atualização da visualização no Viewpager ....

/* DO NOT FORGET! The ViewPager requires at least “1” minimum OffscreenPageLimit */
int limit = (mAdapter.getCount() > 1 ? mAdapter.getCount() - 1 : 1);
mViewPager.setOffscreenPageLimit(limit);
Abhijeet Sharma
fonte
0

para a função "instantiateItem", apenas prepare o fragmento, mas não carregue o conteúdo pesado.

Use "onPageChangeListener", para que cada vez que você vá para uma página específica, você carregue seu conteúdo pesado e o mostre.

desenvolvedor android
fonte
0

Eu meio que tenho o mesmo problema. Encontrei algum código útil neste site e o transformei.

O mínimo int para mViewPager.setOffscreenPageLimit (...); é 1, então, mesmo se você alterá-lo para 0, ainda terá 2 páginas carregadas.

A primeira coisa a fazer é criar um int estático que chamaremos maxPageCount e substituiremos o método getCount () de FragmentStatePagerAdapter para retornar maxPageCount:

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

Crie então um método estático acessível de qualquer lugar no programa que permitirá que você altere este maxCount:

public static void addPage(){
    maxPageCount++; //or maxPageCount = fragmentPosition+2
    mFragmentStatePagerAdapter.notifyDataSetChanged(); //notifyDataSetChanged is important here.
}

Agora inicialize maxPageCount como 1. Sempre que quiser, você pode adicionar outra página. No meu caso quando precisei que o usuário tratasse a página atual primeiro antes de gerar a outra. Ele faz isso e então, sem problemas, pode passar para a próxima página.

Espero que ajude alguém.

K20
fonte
0

Use // crie um booleano para buscar dados private boolean isViewShown = false;

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (getView() != null) {
        isViewShown = true;
        // fetchdata() contains logic to show data when page is selected mostly asynctask to fill the data
        fetchData();
    } else {
        isViewShown = false;
    }
} 
Rohit Sharma
fonte