Olhando para o título, você pode ficar um pouco confuso sobre o que é ViewPager, porque há muito tempo atrás usávamos ViewPager, mas agora cada vez mais ViewPager2 é usado, então ViewPager (ViewPager, ViewPager2) é usado para substituir os dois. o principal é apresentar a diferença entre os dois.
A arquitetura de fragmentos aninhados do ViewPagers pode ser vista em todos os lugares em nossos aplicativos comumente usados, como a página inicial de Douyin, a página inicial dos principais aplicativos de comércio eletrônico (Taobao, JD.com, Pinduoduo), etc. ; mas por causa do mecanismo de pré-carregamento do ViewPager , vamos primeiro dar uma olhada no código-fonte do ViewPager:
public void setOffscreenPageLimit(int limit) {
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
+ DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
}
Quando definimos o valor de offscreenPageLimit (carregamento fora da tela), podemos ver que o valor do limite é limitado e não pode ser menor que DEFAULT_OFFSCREEN_PAGES
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
Isso significa que o ViewPager suporta pré-carregamento por padrão. Vejamos a imagem abaixo.
Se você deseja a versão completa do arquivo de otimização de desempenho do Android, clique para obtê-lo gratuitamente
Se a área vermelha for padronizada para a página inicial, de acordo com o limite de pré-carregamento padrão do ViewPager, as páginas à esquerda e à direita também serão carregadas.Se houver uma solicitação de rede, ou seja, não abrimos a página à esquerda, ele já fez uma solicitação de rede por padrão. Essa experiência é muito ruim porque consome tráfego secretamente.
Idealmente, o que precisamos é carregar uma página quando ela é aberta, então precisamos otimizá-la através do carregamento lento.
1 Otimização de carregamento lento do ViewPager
1.1 Mecanismo de cache do ViewPager
Muitas vezes, quando usamos o Fragment, descobrimos que a página aberta volta e a página não é reconstruída e atualizada. Muitas pessoas pensam que o Fragment está armazenado em cache. Na verdade, não é que o Fragment tenha cache, mas que o ViewPager tenha capacidade de cache ;
Se algum amigo tiver usado a arquitetura de atividade única + vários fragmentos, eles descobrirão que quando a página aberta for retornada novamente, o fragmento será reconstruído, portanto, ambas as arquiteturas têm prós e contras. A chave depende de como escolhemos. Vamos dar uma olhada veja abaixo o mecanismo de cache do ViewPager.
public void setAdapter(@Nullable PagerAdapter adapter) {
if (mAdapter != null) {
①
mAdapter.setViewPagerObserver(null);
mAdapter.startUpdate(this);
for (int i = 0; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
mAdapter.destroyItem(this, ii.position, ii.object);
}
mAdapter.finishUpdate(this);
mItems.clear();
removeNonDecorViews();
mCurItem = 0;
scrollTo(0, 0);
}
②
final PagerAdapter oldAdapter = mAdapter;
mAdapter = adapter;
mExpectedAdapterCount = 0;
③
if (mAdapter != null) {
if (mObserver == null) {
mObserver = new PagerObserver();
}
mAdapter.setViewPagerObserver(mObserver);
mPopulatePending = false;
final boolean wasFirstLayout = mFirstLayout;
mFirstLayout = true;
mExpectedAdapterCount = mAdapter.getCount();
if (mRestoredCurItem >= 0) {
mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
setCurrentItemInternal(mRestoredCurItem, false, true);
mRestoredCurItem = -1;
mRestoredAdapterState = null;
mRestoredClassLoader = null;
} else if (!wasFirstLayout) {
④
populate();
} else {
⑤
requestLayout();
}
}
// Dispatch the change to any listeners
if (mAdapterChangeListeners != null && !mAdapterChangeListeners.isEmpty()) {
for (int i = 0, count = mAdapterChangeListeners.size(); i < count; i++) {
mAdapterChangeListeners.get(i).onAdapterChanged(this, oldAdapter, adapter);
}
}
}
O método principal é setAdapter. Assim como o RecyclerView, porque há um cache, quando a página desliza, se a página existir no cache, ela será buscada no cache. Caso contrário, você precisa criar uma nova página, então vamos fazer primeiro . Preste atenção ao PagerAdapter
public abstract class PagerAdapter {
private final DataSetObservable mObservable = new DataSetObservable();
private DataSetObserver mViewPagerObserver;
public static final int POSITION_UNCHANGED = -1;
public static final int POSITION_NONE = -2;
public abstract int getCount();
//开始更新
public void startUpdate(@NonNull ViewGroup container) {
startUpdate((View) container);
}
//初始化页面
@NonNull
public Object instantiateItem(@NonNull ViewGroup container, int position) {
return instantiateItem((View) container, position);
}
//销毁页面
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
destroyItem((View) container, position, object);
}
//结束刷新
public void finishUpdate(@NonNull ViewGroup container) {
finishUpdate((View) container);
}
}
PagerAdapter é uma classe abstrata, portanto esses métodos devem ser implementados por classes de implementação específicas. Se usarmos ViewPager para aninhar fragmentos, usaremos FragmentPageAdapter
Em seguida, retorne ao método setAdapter
①: Existe uma variável global mAdapter. Se for carregada primeiro, então mAdapter estará vazio. Vá para ②
②: Aqui atribuímos o adaptador que passamos para mAdapter
③: Neste momento, o mAdapter não está vazio. Existem vários parâmetros que precisam ser observados aqui:
wasFirstLayout = true
mRestoredCurItem = -1
Então vá diretamente para ⑤ e chame o método requestLayout, que será executado onMeasure. Neste método, o método populate será executado (você mesmo pode subir as escadas)
O que o povoado fez? Há muito código, então não vou publicá-lo aqui, basta ir em frente e carregá-lo na imagem.
Se for o cache padrão (mOffscreenPageLimit = 1), então 3 fragmentos serão armazenados em cache em mItems
private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
Quando a página desliza, a página2 se torna a página atual, então o que o preenchimento do ViewPager faz?
(