正如RecyclerView橫空出世取代ListView和GridView那樣,Android也推出了二代翻頁視圖ViewPager2,打算替換原來的翻頁視圖ViewPager。與ViewPager相比,ViewPager2支持更豐富的界面特效,包括但不限於下列幾點:
1、不但支持水平方向翻頁,還支持垂直方向翻頁;
2、支持RecyclerView.Adapter,允許調用適配器對象的notifyItem***方法,從而動態刷新某項視圖;
3、除了當前頁,也支持展示左右兩頁的部分區域;
4、支持在翻頁過程中展示自定義的切換動畫;
雖然ViewPager2增加了這麼棒的功能,但它用起來非常簡單,掌握下面幾個方法就夠了:
setAdapter:設置二代翻頁視圖的頁面適配器。
setOrientation:設置二代翻頁視圖的翻頁方向。其中ViewPager2.ORIENTATION_HORIZONTAL表示水平方向,ViewPager2.ORIENTATION_VERTICAL表示垂直方向。
setPageTransformer:設置二代翻頁視圖的頁面轉換器,以便展示切換動畫。
接下來利用循環適配器搭配二代翻頁視圖,演示看看ViewPager2的界面效果。注意RecyclerView與ViewPager2擁有各自的AndroidX庫,故需修改模塊的build.gradle,在dependencies節點內部補充以下兩行依賴配置:
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.viewpager2:viewpager2:1.0.0'
接着新建一個活動頁面,往該頁面的XML文件添加如下所示的ViewPager2標籤:
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp2_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
因爲ViewPager2仍然需要適配器,所以先編寫每項視圖的佈局文件,下面便是一個XML佈局例子,佈局上方是圖像視圖,下方是文本視圖。
<!-- ViewPager2要求每頁的寬高都必須是match_parent -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_pic"
android:layout_width="match_parent"
android:layout_height="360dp"
android:scaleType="fitCenter" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
然後給上面的視圖項補充對應的循環適配器代碼,傳入一個商品列表,再展示每個商品的圖片與文字描述。適配器的代碼片段示例如下:
public class MobileRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext; // 聲明一個上下文對象
private List<GoodsInfo> mGoodsList = new ArrayList<GoodsInfo>(); // 聲明一個商品列表
public MobileRecyclerAdapter(Context context, List<GoodsInfo> goodsList) {
mContext = context;
mGoodsList = goodsList;
}
// 創建列表項的視圖持有者
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup vg, int viewType) {
// 根據佈局文件item_mobile.xml生成視圖對象
View v = LayoutInflater.from(mContext).inflate(R.layout.item_mobile, vg, false);
return new ItemHolder(v);
}
// 綁定列表項的視圖持有者
public void onBindViewHolder(RecyclerView.ViewHolder vh, final int position) {
ItemHolder holder = (ItemHolder) vh;
holder.iv_pic.setImageResource(mGoodsList.get(position).pic);
holder.tv_desc.setText(mGoodsList.get(position).desc);
}
// 定義列表項的視圖持有者
public class ItemHolder extends RecyclerView.ViewHolder {
public ImageView iv_pic; // 聲明列表項圖標的圖像視圖
public TextView tv_desc; // 聲明列表項描述的文本視圖
public ItemHolder(View v) {
super(v);
iv_pic = v.findViewById(R.id.iv_pic);
tv_desc = v.findViewById(R.id.tv_desc);
}
}
}
回到測試頁面的Java代碼,把二代翻頁視圖的排列方向設爲水平方向,並將上述的循環適配器對象設成二代翻頁視圖的適配器。只要以下寥寥幾行代碼就搞定了:
// 從佈局文件中獲取名叫vp2_content的二代翻頁視圖
ViewPager2 vp2_content = findViewById(R.id.vp2_content);
// 設置二代翻頁視圖的排列方向爲水平方向
vp2_content.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
// 構建一個商品信息列表的循環適配器
MobileRecyclerAdapter adapter = new MobileRecyclerAdapter(this, GoodsInfo.getDefaultList());
vp2_content.setAdapter(adapter); // 設置二代翻頁視圖的適配器
運行測試App,水平方向的翻頁過程如下圖所示。
如果把翻頁方向改爲垂直方向,那麼翻頁之時的界面如下圖所示。
以上的效果圖看起來僅僅多了垂直翻頁,稍等片刻給它加上其它特效。先在測試頁面的Java代碼中補充下面幾行:
// ViewPager2支持展示左右兩頁的部分區域
RecyclerView cv_content = (RecyclerView) vp2_content.getChildAt(0);
cv_content.setPadding(Utils.dip2px(this, 60), 0, Utils.dip2px(this, 60), 0);
cv_content.setClipToPadding(false); // false表示不裁剪下級視圖
重新運行測試App,此時頁面效果如下圖所示,可見除了顯示當前商品之外,左右兩頁也呈現了邊緣區域。
撤銷剛加的邊緣特效代碼,再給測試頁面的Java代碼中補充下面幾行:
// ViewPager2支持在翻頁時展示切換動畫
// 創建頁面轉換器,用於計算切換動畫的各項參數
ViewPager2.PageTransformer animator = new ViewPager2.PageTransformer() {
@Override
public void transformPage(@NonNull View page, float position) {
page.setRotation(position * 360); // 設置頁面的旋轉角度
}
};
vp2_content.setPageTransformer(animator); // 設置二代翻頁視圖的頁面轉換器
重新運行測試App,此時翻頁過程如下面兩圖所示,其中第一張圖爲開始翻頁不久的界面效果,第二張圖爲翻頁即將結束的界面效果,從中可見翻頁時展示了旋轉動畫。
ViewPager2不僅支持循環適配器,同樣支持翻頁適配器,還是新的哦。原先ViewPager採用的翻頁適配器叫做FragmentStatePagerAdapter,而ViewPager2採用了FragmentStateAdapter,兩個適配器的名稱差了個“Pager”。一看名稱不同,用法肯定有差別,儘管它倆都支持碎片Fragment,但具體的方法就不一樣了。
比如下面是採用FragmentStateAdapter的新型適配器代碼例子:
public class MobilePagerAdapter extends FragmentStateAdapter {
private List<GoodsInfo> mGoodsList = new ArrayList<GoodsInfo>(); // 聲明一個商品列表
// 碎片頁適配器的構造方法,傳入碎片管理器與商品信息列表
public MobilePagerAdapter(FragmentActivity fa, List<GoodsInfo> goodsList) {
super(fa);
mGoodsList = goodsList;
}
// 創建指定位置的碎片Fragment
@NonNull
@Override
public Fragment createFragment(int position) {
return MobileFragment.newInstance(position,
mGoodsList.get(position).pic, mGoodsList.get(position).desc);
}
// 獲取碎片Fragment的個數
@Override
public int getItemCount() {
return mGoodsList.size();
}
}
運行測試App觀察到的界面效果跟循環適配器差不多,因爲展示商品信息的場景比較簡單,所以循環適配器和翻頁適配器看不出區別。就實際開發而言,簡單的業務場景適合採用循環適配器,複雜的業務場景適合採用翻頁適配器。
ViewPager有個標籤欄搭檔PagerTabStrip,然而ViewPager2拋棄了PagerTabStrip,直接跟TabLayout搭配了。如果要讓ViewPager聯動TabLayout,得先給ViewPager註冊頁面變更監聽器,一旦監聽到翻頁事件就切換對應的標籤;再給TabLayout註冊標籤選中監聽器,一旦監聽到標籤事件就翻到對應的頁面。現在有了ViewPager2,搭配TabLayout便輕鬆多了,只要一行代碼即可綁定ViewPager2與TabLayout。下面是將二者聯結起來的操作步驟。
1、創建測試頁面,並往頁面的XML文件先後加入TabLayout標籤和ViewPager2標籤,具體內容如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 標籤佈局TabLayout節點需要使用完整路徑 -->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- 二代翻頁視圖ViewPager2節點也需要使用完整路徑 -->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp2_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
2、打開該頁面的Java代碼,分別獲取TabLayout和ViewPager2的視圖對象,再利用TabLayoutMediator把標籤佈局跟翻頁視圖連爲一體,關鍵代碼示例如下:
// 從佈局文件中獲取名叫tab_title的標籤佈局
TabLayout tab_title = findViewById(R.id.tab_title);
// 從佈局文件中獲取名叫vp2_content的二代翻頁視圖
ViewPager2 vp2_content = findViewById(R.id.vp2_content);
// 構建一個商品信息的翻頁適配器
MobilePagerAdapter adapter = new MobilePagerAdapter(this, mGoodsList);
vp2_content.setAdapter(adapter); // 設置二代翻頁視圖的適配器
// 把標籤佈局跟翻頁視圖通過指定策略連爲一體,二者在頁面切換時一起聯動
new TabLayoutMediator(tab_title, vp2_content, new TabLayoutMediator.TabConfigurationStrategy() {
@Override
public void onConfigureTab(TabLayout.Tab tab, int position) {
tab.setText(mGoodsList.get(position).name); // 設置每頁的標籤文字
}
}).attach();
重新運行測試App,初始的演示頁面如下圖所示。
接着點擊上方標籤欄的第二個標籤,此時頁面下方翻到了第二頁商品,如下圖所示。
然後手指在商品處向左滑動,此時翻到了第三頁商品,同時標籤欄也切到了第三個標籤,如下圖所示。由此驗證了標籤佈局與翻頁視圖的確是綁定到一塊了。