最近ViewPager2發佈了1.0.0-alpha04版本,新增offscreenPageLimit功能,該功能在ViewPager上並不友好,如下是官方對於ViewPager2的介紹:
衆所周知,ViewPager有兩個毛病:不能關閉預加載和更新Adapter不生效
,因此開頭我爲何說offscreenPageLimit在ViewPager上十分不友好;本質上是由於offscreenPageLimit不能設置成0(設置成0就是想象中的關閉預加載)。
上面是ViewPager默認狀況下的加載示意圖,當切換到當前頁面時,會默認預加載左右兩側的佈局到ViewPager中,儘管兩側的View並不可見的,咱們稱這種狀況叫預加載;因爲ViewPager對offscreenPageLimit設置了限制,頁面的預加載是不可避免。ViewPager的源碼以下:android
private static final int DEFAULT_OFFSCREEN_PAGES = 1; public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) {//不容許小於1 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(); } }
不過,ViewPager強制預加載的邏輯在Fragment配合ViewPager使用時依然存在。爲了避免進行預加載,可使用Fragment懶加載。先說一下ViewPager配合PagerAdapter實現懶加載的方法,經常使用的有如下幾個方法:數組
先說setPrimaryItem(ViewGroup container, int position, Object object),該方法表示當前頁面正在顯示主要Item,何爲主要Item?若是預加載的ItemView已經劃入屏幕,當前的PrimaryItem依然不會改變,除非新的ItemView徹底劃入屏幕,且滑動已經中止纔會判斷,涉及代碼以下:
因爲ViewPager不可避免的進行佈局預加載,形成PagerAdapter必須提早調用instantiateItem(ViewGroup container, int position)方法,instantiateItem()是建立ItemView的惟一入口方法,因此PagerAdapter的實現類FragmentPagerAdapter和FragmentStatePagerAdapter必須抓住該方法進行Fragment對象的建立。例如:
須要說明的是,FragmentPagerAdapter和FragmentStatePagerAdapter一股腦的在instantiateItem()中進行建立且進行add或attach操做,並無在setPrimaryItem()方法中對Fragment進行操做。所以,預加載會致使不可見的Fragment一股腦的調用onCreate、onCreateView、onResume等方法,用戶只能經過Fragment.setUserVisibleHint()方法進行識別。
大多數的懶加載都是對Fragment作手腳,結合生命週期方法和setUserVisibleHint狀態,控制數據延遲加載,而佈局只能提早進入;緩存
使用ViewPager2以前,須要先引入ViewPager2庫,引入時只須要在build.gradle添加以下依賴便可:網絡
implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha04'
而後,在佈局中引入:app
<androidx.viewpager2.widget.ViewPager2 android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
ViewPager2能夠和Adapter一塊兒使用,也能夠和Fragment一塊兒使用。ide
ViewPager2 viewPager = findViewById(R.id.view_pager2); viewPager.setAdapter(new RecyclerView.Adapter<ViewHolder>() { @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_card_layout, parent, false); ViewHolder viewHolder = new ViewHolder(itemView); return viewHolder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.labelCenter.setText(String.valueOf(position)); } @Override public int getItemCount() { return SIZE; } })); static class ViewHolder extends RecyclerView.ViewHolder{ private final TextView labelCenter; public ViewHolder(@NonNull View itemView) { super(itemView); labelCenter = itemView.findViewById(R.id.label_center); } }
固然,ViewPager2也能夠和Fragment配合使用。工具
viewPager.setAdapter(new FragmentStateAdapter(this) { @NonNull @Override public Fragment getItem(int position) { return new VSFragment(); } @Override public int getItemCount() { return SIZE; } });
ViewPager2和ViewPager在使用方式上差很少,主要有如下一些API:佈局
在1.0.0-alpha04版本中,ViewPager2提供了離屏加載功能,該功能和ViewPager的預加載存的的意義彷佛是同樣的。fetch
public static final int OFFSCREEN_PAGE_LIMIT_DEFAULT = 0; public void setOffscreenPageLimit(int limit) { if (limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT) { throw new IllegalArgumentException( "Offscreen page limit must be OFFSCREEN_PAGE_LIMIT_DEFAULT or a number > 0"); } mOffscreenPageLimit = limit; // Trigger layout so prefetch happens through getExtraLayoutSize() mRecyclerView.requestLayout(); }
爲了方便分析ViewPager2和ViewPager的區別,咱們看一下佈局方面的對比:gradle
爲了對比二者加載佈局的效果,我準備了LinearLayout同時展現ViewPager和ViewPager2,設置相同的Item佈局和數據源,而後用Android佈局分析工具抓取二者的佈局結構,效果以下:
從分析結果來看,ViewPager會默認會預佈局兩側各一個佈局,ViewPager2默認不進行預佈局,主要由各自的默認offscreenPageLimit參數決定,ViewPager默認爲1且不容許小於1,ViewPager2默認爲0。
分析運行結果,在設置相同的offscreenPageLimit時,二者都會預佈局左右(上下)二者的offscreenPageLimit個ItemView;
從對比結果上來看,ViewPager2的offscreenPageLimit和ViewPager運行結果同樣,可是ViewPager2最小offscreenPageLimit能夠設置爲0;
ViewPager2預加載即RecyclerView的預加載,代碼在RecyclerView的GapWorker中。ViewPager2會默認開啓預加載,表現形式是在拖動控件或者Fling時,可能會預加載一條數據;下面是預加載的示意圖:
若是要關閉預加載,可使用下面的代碼:
((RecyclerView)viewPager.getChildAt(0)).getLayoutManager().setItemPrefetchEnabled(false);
預加載的開關在LayoutManager上,只須要獲取LayoutManager並調用setItemPrefetchEnabled()便可控制開關;
ViewPager2默認會緩存2條ItemView,並且在最新的RecyclerView中能夠自定義緩存Item的個數,方法以下:
public void setItemViewCacheSize(int size) { mRecycler.setViewCacheSize(size); }
預加載和緩存在View層面沒有本質的區別,都是已經準備了佈局,可是沒有加載到parent視圖上; 預加載和離屏加載在View層面有本質的區別,離屏加載的View已經添加到parent上;
目前,ViewPager2對Fragment的支持只能使用FragmentStateAdapter來實現,使用起來也是很是簡單,例如:
默認狀況下,ViewPager2是開啓預加載關閉離屏加載的,這種狀況下,切換頁面對Fragment生命週期有何影響呢?如下是來自網絡上的兩個圖:
問題一:關閉預加載對Fragment的影響: 通過驗證,是否開啓預加載,對Fragment的生命週期沒有影響,結果和默認上圖是同樣的;
問題二:開啓離屏加載對Fragment的影響: 設置offscreenPageLimit=1時:
從打印日誌能夠看出,offscreenPageLimit=1時,ViewPager2在第1頁會加載兩條數據,而且會把下一頁View提早加載進來;之後每滑一頁,會加載下一頁數組,直到第5頁,會移除第1頁的Fragment;第6頁會移除第2頁的Fragment。
針對這次的更新,ViewPager2主要有如下一些特性:
經過此次ViewPager2更新,官方貌似要發力替換ViewPager了,不管是它高效的複用仍是自帶懶加載,亦或是更新有效的Adapter,都要比ViewPager更增強大。