探索取代ViewPager的ViewPager2

持續(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ū)別

  1. ViewPager2 API最大的變化是它現(xiàn)在使用RecyclerView。
  2. 使用 ViewPager2 需要遷移到 AndroidX ,因?yàn)閍ndroid.support庫(kù)中不支持ViewPager2。
  3. FragmentStateAdapter 替換 FragmentStatePagerAdapter
  4. RecyclerView.Adapter 替代 PagerAdapter
  5. registerOnPageChangeCallback 替換 addPageChangeListener

ViewPager2 新增功能

查看源碼可以看到ViewPager2是基于RecyclerView構(gòu)建的,既然是通過(guò)RecyclerView構(gòu)建,那么就可以在Adapter類(lèi)中將RecyclerView.AdapterViewPager2一起使用。關(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

  1. 在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 查看最新版本

  2. 同步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>
  1. 初始化數(shù)據(jù)源。
ArrayList<String> mList = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
      mList.add(String.valueOf(i));
    } 
  1. 創(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);
    }
  }
}
  1. 為ViewPager2設(shè)置Adapter
//ViewPager2設(shè)置Adapter
    DemoAdapter mAdapter = new DemoAdapter(mList);
    mVpDemo.setAdapter(mAdapter);
  1. 到這里已經(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

  1. 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 >
  1. 創(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);
  1. 創(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();
  }
  1. 在ViewPager2中添加Fragment已經(jīng)實(shí)現(xiàn)了,下面是已實(shí)現(xiàn)的效果。


    viewpager2.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容