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?


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

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

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(){}

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{
     }
}

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.


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 ...


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 ...


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

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.


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.


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;
    }
} 
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.