ViewPager2的使用

Jetpack與ViewPager2

Jetpack 是一套庫且预、工具和指南蜈项,可幫助開發(fā)者更輕松地編寫優(yōu)質(zhì)應用定硝。這些組件可幫助您遵循最佳做法瘪菌、讓您擺脫編寫樣板代碼的工作并簡化復雜任務撒会,以便您將精力集中放在所需的代碼上。

2018年Google推出了Jetpack架構(gòu)組件师妙,旨在幫助開發(fā)者輕松構(gòu)建更穩(wěn)定诵肛、更健壯、以及更可維護的應用程序默穴。Jetpack為之前混亂的Android開發(fā)生態(tài)提供了一種更規(guī)范的解決方案怔檩,可以說是Google為了規(guī)范Android開發(fā)生態(tài)邁進的一大步。

Google對于Jetpack也是寄予厚望蓄诽,不斷推出新的jetpack組件為其添磚加瓦薛训,于是在2019年期待已久的ViewPage2正式發(fā)布。

Viewpager2

概述

ViewPager2是基于RecyclerView實現(xiàn)的仑氛,自然繼承了RecyclerView的眾多優(yōu)點乙埃,并且針對ViewPager存在的問題做了優(yōu)化。

  • 支持垂直方向的滑動且實現(xiàn)極其簡單锯岖。
  • 完全支持RecyclerView的相關配置功能介袜。
  • 支持多個PageTransformer。
  • 支持DiffUtil出吹,局部數(shù)據(jù)刷新和Item動畫遇伞。
  • 支持模擬用戶滑動與禁止用戶操作。

添加依賴

目前最新穩(wěn)定版本還是1.0.0捶牢,而且使用ViewPager2項目必須遷移到Androidx鸠珠。

dependencies {
    implementation "androidx.viewpager2:viewpager2:1.0.0"
}

ViewPager2 + View

1. 布局

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/view_pager2"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:layout_constraintHeight_percent="0.5"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

2. Adapter

因為ViewPager2是基于RecyclerView的巍耗,所以在使用上與RecyclerView的使用基本一致,而且Adapter用的也是RecyclerView.Adapter跳芳。

class ViewPager2Adapter extends RecyclerView.Adapter{

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(ViewPager2Activity.this).inflate(R.layout.fragment_normal, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        holder.itemView.setBackgroundColor(Color.parseColor(Util.getRandColor()));
    }

    @Override
    public int getItemCount() {
        return viewList.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder{
        View rootView;
        public MyViewHolder(@NonNull View itemView) {
        super(itemView);
        this.rootView = itemView;
        }
    }
}
viewPager2Adapter = new ViewPager2Adapter();
//設置滾動方向
viewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
viewPager2.setAdapter(viewPager2Adapter);

//頁面切換監(jiān)聽
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
    @Override
        public void onPageSelected(int position) {
        super.onPageSelected(position);
        Log.d(TAG, "ViewPager onPageSelected " + position);
        }
});

OnPageChangeCallback是一個抽象類芍锦,同時提供了onPageScrolled()onPageScrollStateChanged()監(jiān)聽方法,可以按需求重寫飞盆。

3. 禁止用戶操作與模擬用戶滑動

//禁止用戶操作
viewPager2.setUserInputEnabled(true);

//模擬用戶滑動事件
//開啟模擬滑動
viewPager2.beginFakeDrag();
//fakeDragBy返回一個boolean值娄琉,表示是否正在滑動
boolean isDragging = viewPager2.fakeDragBy(100);
//結(jié)束模擬滑動
viewPager2.endFakeDrag();

4. PageTransformer

setPageTransformer是ViewPager中就提供的方法,用于設置頁面切換動畫效果吓歇。相較于ViewPager在ViewPager2中setPageTransformer的功能要更加的強大孽水,除了可以設置頁面切換動畫,還可以用來設置頁面邊距而且支持同時設置多個PageTransformer城看。

MarginPageTransformer

ViewPager2中取消了在ViewPager中的setPageMargin()方法女气,改為通過提供的MarginPageTransformer來設置頁面間距。

MarginPageTransformer pageTransformer = new MarginPageTransformer(20);
viewPager2.setPageTransformer(pageTransformer);

效果如下:

image

5. CompositePageTransformer與自定義Transformer

上面我們提到了ViewPager2支持設置多個Transformer就是通過CompositePageTransformer實現(xiàn)的测柠。

CompositePageTransformer類中通過List來維護多個Transformer炼鞠。

//設置Transformer
CompositePageTransformer compositePageTransformer = new CompositePageTransformer();
//添加邊距Transformer
compositePageTransformer.addTransformer(new MarginPageTransformer(20));
//添加縮放效果的Transformer
compositePageTransformer.addTransformer(new ScaleInTransformer());
viewPager2.setPageTransformer(compositePageTransformer);

上面的代碼中除了系統(tǒng)提供的MarginPageTransformer還添加了一個ScaleInTransformerScaleInTransformer就是我們自定義的Transformer轰胁。

自定義Transformer的實現(xiàn)也很簡單跟ViewPager中的實現(xiàn)基本一致谒主,只不過需要繼承實現(xiàn)的接口變成ViewPager2.PageTransformer,同時實現(xiàn)transformPage方法即可赃阀,transformPage方法有兩個參數(shù):第一個是發(fā)生改變的頁面對象霎肯,第二個是偏移量。

  • View page 發(fā)生改變的頁面對象榛斯。
  • float position 頁面偏移量观游,取值范圍(-1,1)當前頁面顯示位置值為0,頁面向左滑動時驮俗,當前頁面從屏幕位置向左移動時取值從0變?yōu)?1懂缕,完全不可見時取值趨向-1,右側(cè)頁面向左移動到當前屏幕位置取值從1變?yōu)?意述,頁面右滑時同理提佣。
image
public class ScaleInTransformer implements ViewPager2.PageTransformer {
    public static final float DEFAULT_MIN_SCALE = 0.85f;
    public static final float DEFAULT_CENTER = 0.5f;
    
    private float mMinScale = DEFAULT_MIN_SCALE;
    @Override
    public void transformPage(@NonNull View page, float position) {
        int pageWidth = page.getWidth();
        int pageHeight = page.getHeight();
        //動畫錨點設置為View中心
        page.setPivotX(pageWidth/2);
        page.setPivotY(pageHeight/2);
        if(position < -1){
            //屏幕左側(cè)不可見時
            page.setScaleX(mMinScale);
            page.setScaleY(mMinScale);
            page.setPivotY(pageWidth / 2);
        } else if(position <= 1){
            if (position < 0) {
                //屏幕左側(cè)
                //(0,-1)
                float scaleFactor = (1 + position) * (1 - mMinScale) + mMinScale;
                page.setScaleX(scaleFactor);
                page.setScaleY(scaleFactor);
                page.setPivotX(pageWidth);
            } else {
                //屏幕右側(cè)
                //(1,0)
                float scaleFactor = (1 - position) * (1 - mMinScale) + mMinScale;
                page.setScaleX(scaleFactor);
                page.setScaleY(scaleFactor);
                page.setPivotX(pageWidth * ((1 - position) * DEFAULT_CENTER));
            }
        } else{
            //屏幕右側(cè)不可見
            page.setPivotX(0f);
            page.setScaleY(mMinScale);
            page.setScaleY(mMinScale);
        }
    }
}

效果如下:

image

6. 實現(xiàn)一屏多頁效果

一屏多頁也是一個相對比較常用的效果,先回顧下ViewPager中的實現(xiàn)方式荤崇。

1. 在ViewPager和父布局中設置
    android:clipChildren="false"
        
2. 給ViewPager設置marginLeft和marginRight
    android:layout_marginLeft="50dp"
    android:layout_marginRight="50dp"

雖然此方式在ViewPager2中此方式不起作用拌屏,但是實現(xiàn)原理是一樣的。且因為ViewPager2是基于RecyclerView實現(xiàn)的术荤,所以我們需要拿到RecyclerView來進行設置倚喂。

//預加載頁面數(shù)量
viewPager2.setOffscreenPageLimit(1);
//一屏多頁
View recyclerView = viewPager2.getChildAt(0);
if(recyclerView != null && recyclerView instanceof RecyclerView){
    recyclerView.setPadding(100, 0, 100, 0);
    ((RecyclerView) recyclerView).setClipToPadding(false);
}

使用setOffscreenPageLimit來設置ViewPager2至少預加載左右各一個頁面,否則可能存在左右頁面未初始化,導致不顯示問題端圈。

效果如下:

image

ViewPager2 + Fragment

1. Adapter

ViewPager2廢棄了FragmentStatePagerAdapterFragmentPagerAdapter焦读,新增了FragmentStateAdapter來替換他們。實現(xiàn)方式就很簡單了舱权。

    class FragmentPager2Adapter extends FragmentStateAdapter {

        public FragmentPager2Adapter(@NonNull FragmentActivity fragmentActivity) {
            super(fragmentActivity);
        }

        public FragmentPager2Adapter(@NonNull Fragment fragment) {
            super(fragment);
        }

        public FragmentPager2Adapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
            super(fragmentManager, lifecycle);
        }

        @NonNull
        @Override
        public Fragment createFragment(int position) {
            return fragmentList.get(position);
        }

        @Override
        public int getItemCount() {
            return fragmentList.size();
        }
    }

上面是一種最簡單的實現(xiàn)矗晃。

2. 懶加載

在使用ViewPager顯示Fragment時,最麻煩的可能就是ViewPager的預加載問題了宴倍,最常用的解決方案有以下幾種:

  1. 通過重寫ViewPager來解除setOffscreenPageLimit(int)必須大于等于1的限制张症。
  • ViewPager提供了setOffscreenPageLimit(int)可以用來控制左右預加載頁面的數(shù)量,但是限制了至少預加載一頁鸵贬。
    • 可以通過拷貝ViewPager源碼俗他,重寫setOffscreenPageLimit方法來解除限制,從而實現(xiàn)ViewPager不進行預加載阔逼。
    • 此方法只能以某一版本的ViewPager源碼為基礎進行修改兆衅,適配性很差。且需要重寫ViewPager部分邏輯嗜浮。
    • 不推薦使用羡亩。
  1. 通過Fragment的setUserVisibleHint(boolean)判斷Fragment可見性,從而實現(xiàn)懶加載危融。

  2. adnroidx之后可以使用Lifecycle實現(xiàn)更簡潔的懶加載實現(xiàn)方式夕春。

上面回顧了ViewPager中的Fragment懶加載實現(xiàn)方式,在ViewPager2中如何實現(xiàn)呢专挪?答案是:ViewPager2也是通過上面提到的第三條實現(xiàn)方式來實現(xiàn)懶加載的。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末片排,一起剝皮案震驚了整個濱河市寨腔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌率寡,老刑警劉巖迫卢,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冶共,居然都是意外死亡乾蛤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門捅僵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來家卖,“玉大人,你說我怎么就攤上這事庙楚∩系矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵馒闷,是天一觀的道長酪捡。 經(jīng)常有香客問我叁征,道長,這世上最難降的妖魔是什么逛薇? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任捺疼,我火速辦了婚禮,結(jié)果婚禮上永罚,老公的妹妹穿的比我還像新娘啤呼。我一直安慰自己,他們只是感情好尤蛮,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布媳友。 她就那樣靜靜地躺著,像睡著了一般产捞。 火紅的嫁衣襯著肌膚如雪醇锚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天坯临,我揣著相機與錄音焊唬,去河邊找鬼。 笑死看靠,一個胖子當著我的面吹牛赶促,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挟炬,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼鸥滨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谤祖?” 一聲冷哼從身側(cè)響起婿滓,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎粥喜,沒想到半個月后凸主,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡额湘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年卿吐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锋华。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡嗡官,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毯焕,到底是詐尸還是另有隱情谨湘,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站紧阔,受9級特大地震影響坊罢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜擅耽,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一活孩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乖仇,春花似錦憾儒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至警儒,卻和暖如春训裆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜀铲。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工边琉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人记劝。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓变姨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親厌丑。 傳聞我的和親對象是個殘疾皇子定欧,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容