持續(xù)更新中...
前言
早在2019年2月7日Google就發(fā)布了ViewPager2,經(jīng)常查閱官方文檔的大佬肯定第一時(shí)間就發(fā)現(xiàn)了這個(gè)部件,奈何一直都是開(kāi)發(fā)版本需要一步步進(jìn)行完成,經(jīng)過(guò)開(kāi)發(fā)期的Alpha、Beta、RC三個(gè)階段版本之后終于在2019年11月20日Google發(fā)布了ViewPager2正式版取代ViewPager,從此再也不用因?yàn)閂iewPager各種難題而發(fā)愁了。
ViewPager2 和 ViewPager 的區(qū)別
- ViewPager2 API最大的變化是它現(xiàn)在使用RecyclerView。
- 使用 ViewPager2 需要遷移到 AndroidX ,因?yàn)閍ndroid.support庫(kù)中不支持ViewPager2。
- FragmentStateAdapter 替換 FragmentStatePagerAdapter
- RecyclerView.Adapter 替代 PagerAdapter
- registerOnPageChangeCallback 替換 addPageChangeListener
ViewPager2 新增功能
查看源碼可以看到ViewPager2是基于RecyclerView構(gòu)建的,既然是通過(guò)RecyclerView構(gòu)建,那么就可以在Adapter類(lèi)中將RecyclerView.Adapter與ViewPager2一起使用。關(guān)于RecyclerView的強(qiáng)大之處就不用我說(shuō)了,基本上都知道,不知道的自己去查閱相關(guān)資料。
關(guān)于為什么使用ViewPager2而不是繼續(xù)使用ViewPager的原因,請(qǐng)看下面的ViewPager2變化。
- ViewPager2 基于 RecyclerView
-
允許垂直分頁(yè),說(shuō)明也支持LayoutManager,在源碼中也可以看到。
viewpager_2.png
- 支持RTL布局,國(guó)內(nèi)一般適配的很少,到目前為止我還沒(méi)有見(jiàn)過(guò),可誰(shuí)知道產(chǎn)品的想法呢。
- 改善數(shù)據(jù)更改通知
- 支持使用代碼滾動(dòng)ViewPager2
- 引入了MarginPageTransformer 以提供在頁(yè)面之間創(chuàng)建空間的功能。
- 引入CompositePageTransformer 來(lái)組合多個(gè)Page Transformer。
- getCurrentItem() 和 getCurrentItem() 方法的隱式使用
- 由于RecyclerView包含ViewPager2的一部分,因此支持DiffUtil
- 引入ItemDecorator可以對(duì)行進(jìn)行操作,和RecyclerView一致
深入了解ViewPager2
在查閱ViewPager2源碼的時(shí)候我發(fā)現(xiàn)該類(lèi)被final修飾,那么就說(shuō)明這個(gè)類(lèi)是無(wú)法被繼承擴(kuò)展。所以我比較好奇,就開(kāi)始比較ViewPager2和ViewPager的源碼, 發(fā)現(xiàn)ViewPager有將近3000行代碼,而ViewPager2只有1500行,因?yàn)樗赗ecyclerView,因此ViewPager2隱式使用RecyclerView意味著與RecyclerView沒(méi)有太大的區(qū)別。
在ViewPager2 中的Constructor 方法中,有一個(gè)稱(chēng)為initialize() 的方法。
Google開(kāi)發(fā)人員對(duì)RecyclerView 進(jìn)行了少許修改,以利用可訪問(wèn)性,滾動(dòng)控件以及初始化的LinearLayoutManager,并通過(guò)修改后的LinearLayoutManager 在禁用用戶(hù)滾動(dòng)時(shí)調(diào)整可訪問(wèn)性。畢竟,應(yīng)該將LayoutManager 對(duì)象設(shè)置為RecyclerView。
在viewPager 中定義的setOrientation() 方法。ViewPager2具有垂直和水平支持,默認(rèn)設(shè)置為水平。
你可能已經(jīng)在RecyclerView中使用PagerSnapHelper。PagerSnapHelper可以幫助實(shí)現(xiàn)與ViewPager類(lèi)似的行為,因此PagerSnapHelper附加到ViewPager2中的RecyclerView。
實(shí)現(xiàn)ViewPager2
在build.gradle中導(dǎo)入ViewPager2的依賴(lài),當(dāng)前最新版本為1.0.0,使用時(shí)請(qǐng)到 https://developer.android.google.cn/jetpack/androidx/releases/viewpager2?hl=zh_cn#androidx-deps 查看最新版本
同步build.gradle之后項(xiàng)目中已經(jīng)有了ViewPager2,現(xiàn)在可以到xml中使用ViewPager2了。
<androidx.viewpager2.widget.ViewPager2
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/vp2_demo"
tools:context=".MainActivity"
>
</androidx.viewpager2.widget.ViewPager2>
- 初始化數(shù)據(jù)源。
ArrayList<String> mList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
mList.add(String.valueOf(i));
}
- 創(chuàng)建一個(gè)ViewPager2需要使用到的Adapter,這個(gè)Adapter是RecyclerView.Adapter;
public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.ViewHolder> {
private ArrayList<String> mList;
private Context mContext;
private int[] colors = {0xFFDC143C,0xFFFF1493,0xFFFF00FF,0xFF4B0082,0xFF0000FF,
0xFF1E90FF,0xFF00CED1,0xFF7FFFAA,0xFF228B22,0xFFFFFF00};
public DemoAdapter(ArrayList<String> list) {
mList = list;
}
@NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mContext = parent.getContext();
View inflate = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_demo_item, parent, false);
return new ViewHolder(inflate);
}
@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.mTextView.setText(
String.format(
mContext.getResources().getString(R.string.adapter_demo_content)
,position));
holder.mTextView.setBackgroundColor(colors[position]);
}
@Override public int getItemCount() {
return mList.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private TextView mTextView;
ViewHolder(@NonNull View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.adapter_demo_tv);
}
}
}
- 為ViewPager2設(shè)置Adapter
//ViewPager2設(shè)置Adapter
DemoAdapter mAdapter = new DemoAdapter(mList);
mVpDemo.setAdapter(mAdapter);
-
到這里已經(jīng)實(shí)現(xiàn)了一個(gè)ViewPager2,現(xiàn)在運(yùn)行查看結(jié)果
ViewPager2效果
關(guān)于DiffUtil
使用過(guò)DiffUtil都知道它的好處,沒(méi)有用使用過(guò)也沒(méi)有關(guān)系,聽(tīng)我給你說(shuō)說(shuō)。
- 可以通過(guò)計(jì)算判斷兩個(gè)列表之間的差異進(jìn)行局部刷新,而平時(shí)使用的notifyDataSetChanged();是一個(gè)無(wú)腦刷新的操作,會(huì)刷新整個(gè)列表,對(duì)性能和視覺(jué)上并不是很友好。
- 使用差分進(jìn)化算法計(jì)算更新的最小系數(shù)。
需要注意的是,如果你的列表數(shù)據(jù)量較大,建議在后臺(tái)線程執(zhí)行這個(gè)操作,DiffResult在主線程中執(zhí)行。
如果啟用了移動(dòng)檢測(cè),則需要花費(fèi)額外的O(N ^ 2)時(shí)間,其中N是已添加和已刪除項(xiàng)目的總數(shù)。如果您的列表已經(jīng)按相同的約束排序(例如,為帖子列表創(chuàng)建的時(shí)間戳),則可以禁用移動(dòng)檢測(cè)以提高性能。
關(guān)于DiffUtil具體使用方法自行百度查閱資料。
使用FragmentStateAdapter實(shí)現(xiàn)ViewPager2
- FragmentVP2Activity.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".FragmentVP2Activity"
>
<androidx.appcompat.widget.AppCompatButton
android:onClick="addFragmentOnClick"
android:text="@string/add_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.appcompat.widget.AppCompatButton
android:onClick="removeFragmentOnClick"
android:text="@string/remove_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.appcompat.widget.AppCompatButton
android:onClick="setOrientationOnClick"
android:text="@string/demo_orientation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp2_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout >
- 創(chuàng)建一個(gè)Adapter設(shè)置到ViewPager2,這個(gè)Adapter是FragmentStateAdapter;
public class DemoFragmentAdapter extends FragmentStateAdapter {
private List<Fragment> mFragmentList;
DemoFragmentAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
mFragmentList = new ArrayList<>();
}
public DemoFragmentAdapter(@NonNull Fragment fragment) {
super(fragment);
mFragmentList = new ArrayList<>();
}
public DemoFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
mFragmentList = new ArrayList<>();
}
//添加一個(gè)Fragment
void addFragment(Fragment fragment){
mFragmentList.add(fragment);
notifyDataSetChanged();
}
//刪除一個(gè)Fragment
void removeFragment(){
if (mFragmentList.size() > 0 ){
mFragmentList.remove(mFragmentList.size() - 1);
notifyDataSetChanged();
}
}
@NonNull @Override public Fragment createFragment(int position) {
return mFragmentList.get(position);
}
@Override public int getItemCount() {
return mFragmentList.size();
}
}
設(shè)置Adapter
DemoFragmentAdapter mAdapter = new DemoFragmentAdapter(this);
fragment_vp2.setAdapter(mAdapter);
- 創(chuàng)建Activity點(diǎn)擊事件,這里為方便測(cè)試動(dòng)態(tài)添加Fragment,刪除Fragment,切換ViewPager2方向。
//設(shè)置方向 ViewPager2 獨(dú)有
public void setOrientationOnClick(View v){
if (fragment_vp2.getOrientation() == ViewPager2.ORIENTATION_HORIZONTAL){
fragment_vp2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
}else {
fragment_vp2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
}
}
//添加一個(gè)fragment
public void addFragmentOnClick(View v){
mAdapter.addFragment(new DemoFragment());
}
//刪除一個(gè)fragment
public void removeFragmentOnClick(View v){
mAdapter.removeFragment();
}
-
在ViewPager2中添加Fragment已經(jīng)實(shí)現(xiàn)了,下面是已實(shí)現(xiàn)的效果。
viewpager2.gif


