天天看點

android viewpager 間隔,ViewPager 超詳解:玩出十八般花樣

原标題:ViewPager 超詳解:玩出十八般花樣

一款仿miui,仿小米,月曆,周月曆,月月曆,月視圖、周視圖滑動切換,農曆,Andriod Calendar , MIUI Calendar,小米月曆。

android viewpager 間隔,ViewPager 超詳解:玩出十八般花樣

https://github.com/yannecer/NCalendar

本文作者

作者:OCNYang

連結:

http://www.jianshu.com/p/f8bf9e8b0a39

本文由作者投稿釋出。

1

概述

雖然沒有 RecyclerView 這種清單控件常用些,但是在開發中你ViewPager 肯定也是不可或缺的控件,引導頁、輪播圖、卡片畫廊等效果總是缺少不了 ViewPager 的身影。

相信每一位朋友對 ViewPager 的基礎使用都已經很熟練了,今天在這裡就從簡至繁将 ViewPager 的每個用法都梳理一邊。

android viewpager 間隔,ViewPager 超詳解:玩出十八般花樣

主要包括以下内容:

ViewPager 基本使用(簡介、擴充卡)

ViewPager + TabLayout + Fragment 的使用

ViewPager 輪播圖的使用(訓示器、标題、自動輪播、首尾循環)

ViewPager 的切換效果(PageTransformer)

ViewPager 切換效果進階

ViewPager 的基礎使用

對于 ViewPager ,官方的描述大概是這樣的:頁面允許左右滑動的布局管理器,而不同頁面帶有不同的資料。

這裡簡單歸結如下:

ViewPager 是 v4 包中的一個類。

ViewPager 類直接繼承了 ViewGroup 類,它是一個容器類,可以在其中添加其他的 view 。

類似于 ListView,也有自己的擴充卡,用來填充資料頁面。

關于 ViewPager 在布局檔案中的聲明,這裡就不再說了。其實是沒什麼好說的,并沒有什麼可以直接聲明的特殊屬性,由于繼承于 ViewGroup 有的也都是些 ViewGroup 的屬性。

這裡值得介紹的也就是幾個可以動态設定方法了,常用的有以下幾個:

setAdapter(PagerAdapter adapter) 設定擴充卡

setOffscreenPageLimit(int limit) 設定緩存的頁面個數,預設是 1

setCurrentItem(int item) 跳轉到特定的頁面

setOnPageChangeListener(..) 設定頁面滑動時的監聽器(現在API中建議使用 addOnPageChangeListener(..))

setPageTransformer(..PageTransformer) 設定頁面切換時的動畫效果

setPageMargin(int marginPixels) 設定不同頁面之間的間隔

setPageMarginDrawable(..) 設定不同頁面間隔之間的裝飾圖也就是 divide ,要想顯示設定的圖檔,需要同時設定 setPageMargin()

謹記上面這幾個方法,玩轉 ViewPager 其實都是圍繞它們進行的,能不能玩出花樣,就看你把它們運用的怎麼樣了。

上面的方面大多一看說明就明白了,這裡值得一提的就是 ViewPager 的擴充卡了。

2

PagerAdapter

PagerAdapter 是抽象的類,是以使用時隻能使用它的子類,實作子類必須要實作以下四個方法:

getCount(); 是擷取目前窗體界面數,也就是資料的個數。

isViewFromObject(View view, Object object); 這個方法用于判斷是否由對象生成界面,官方建議直接傳回 return view == object;。

instantiateItem(View container, int position); 要顯示的頁面或需要緩存的頁面,會調用這個方法進行布局的初始化。

destroyItem(ViewGroup container, int position, Object object); 如果頁面不是目前顯示的頁面也不是要緩存的頁面,會調用這個方法,将頁面銷毀。

相信大家對上面這些方法的實作并不陌生,這裡就不詳細介紹了。

另外我們知道官方給我們提供的還有 PagerAdapter 的兩個直接子類 FragmentPagerAdapter 和 FragmentStatePagerAdapter 。而我們常常會在 ViewPager 和 Fragment 結合使用的時候來使用這兩個擴充卡。具體的用法和它們之間的差別,我們在下個章節講。

3

ViewPager + TabLayout + Fragment 的結合使用

在引導頁中我們常常用 ViewPager 和 Fragment 結合使用,而像新聞分類的頁面我們會再加上一個 TabLayout 三者關聯使用。而此時,我們不會再使用 PagerAdapter 了,而是直接使用官方提供的專門用于與 Fragment 結合使用的 FragmentPagerAdapter。

FragmentPagerAdapter 它将每一個頁面表示為一個 Fragment,并且每一個 Fragment 都将會儲存到 FragmentManager 當中。而且,當使用者沒可能再次回到頁面的時候,FragmentManager 才會将這個 Fragment 銷毀。

使用 FragmentPagerAdapter 需要實作兩個方法:

public Fragment getItem(int position) 傳回的是對應的 Fragment 執行個體,一般我們在使用時,會通過構造傳入一個要顯示的 Fragment 的集合,我們隻要在這裡把對應的 Fragment 傳回就行了。

public int getCount() 這個上面介紹過了傳回的是頁面的個數,我們隻要傳回傳入集合的長度就行了。

使用起來是非常簡單的,FragmentStatePagerAdapter 的使用也和上面一樣,那兩者到底有什麼差別呢?

差別如下:

FragmentPagerAdapter:對于不再需要的 fragment,選擇調用 onDetach() 方法,僅銷毀視圖,并不會銷毀 fragment 執行個體。

FragmentStatePagerAdapter:會銷毀不再需要的 fragment,當目前事務送出以後,會徹底的将 fragmeng 從目前 Activity 的FragmentManager 中移除,state 标明,銷毀時,會将其 onSaveInstanceState(Bundle outState) 中的 bundle 資訊儲存下來,當使用者切換回來,可以通過該 bundle 恢複生成新的 fragment,也就是說,你可以在 onSaveInstanceState(Bundle outState) 方法中儲存一些資料,在 onCreate 中進行恢複建立。

由上總結:

使用 FragmentStatePagerAdapter 更省記憶體,但是銷毀後建立也是需要時間的。一般情況下,如果你是制作首頁面,就 3、4 個 Tab,那麼可以選擇使用 FragmentPagerAdapter,如果你是用于 ViewPager 展示數量特别多的條目時,那麼建議使用 FragmentStatePagerAdapter。

那 Tablayout 如何和 Viewpager 關聯呢?由于我們這裡主要是講解 ViewPager 的,所謂 “術業有專攻” 是以關于 TabLayout 的使用我們就不再摻和了。

第一步,初始化 TabLayout 和 ViewPager 後隻要通過調用 TabLayout 的 tabLayout.setupWithViewPager(viewPager) 方法就将兩者綁定在一起了。

第二步,重寫 PagerAdapter 的 public CharSequence getPageTitle(int position) 方法,而 TabLayout 也正是通過 setupWithViewPager() 方法底部會調用 PagerAdapter 中的getPageTitle() 方法來實作關聯的。

4

ViewPager 輪播圖的使用

關于此章本想給大家細細到來,才寫上面兩章都這麼多篇幅了,我們還有給下面兩章重點講的部分留點空間呢。如果非得想看還不嫌我啰嗦,那我有時間再把這段給不出來。

這章就這樣結束,當然沒有,雖然不負責任,但是也不能撩完妹子就閃人啊!這裡還是要基本原理給大家論道論道的。

android viewpager 間隔,ViewPager 超詳解:玩出十八般花樣

從上圖我們可以知道,一般我們使用 ViewPager 做 Banner 時主要有以上幾個元素:

标題 & 訓示器:

我們可以把标題和訓示器直接寫在我們 Banner 的 item 的布局中,這樣通過在 PageAdapter 的 instantiateItem() 方法初始化頁面時,直接設定。

但是一般我們不會這樣做(如果标題沒有陰影的話,可以如上面說的那樣),因為這樣在頁面滑動的時候,會顯得特别生硬,尤其是訓示器。

那該如何呢?一般我們會在 ViewPager 所在的布局檔案中,聲明訓示器和标題布局,如下:

...>

android:id="@+id/viewPager"

..."/>

android:layout_gravity="bottom"

...>

android:id="@+id/bannerIndicators"

.../>

android:id="@+id/bannerTitle"

.../>

那如何才能實作當頁面滑動時,标題和訓示器伴随改變呢?

還記不記得,一開始介紹 ViewPager 時,它有一個可以設定監聽頁面改變的方法 addOnPageChangeListener(),在 OnPageChangeListener 監聽器中有一個頁面滑動結束時的回調方法 onPageSelected(int position) ,我們隻需要在這個方法中,來設定标題和訓示器跟随變化就行了。

自動輪播:

實作自動輪播的原理其實更簡單,隻要我們每隔一定時間發送一個切換頁面的事件就行了。實作這個功能有很多種方法,相信作為 Android 開發者,你最快想到的就是 Handler.sendEmptyMessageDelayed(int what, long delayMillis) 了吧。

Handler mHandler = newHandler() {

@Override

publicvoidhandleMessage(Message msg){

if(mAutoPlay) {

//mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);//無限輪播時

mViewPager.setCurrentItem((mViewPager.getCurrentItem()+ 1) % mViewPagerItemCount)

this.sendEmptyMessageDelayed(MSG_WHAT, delayMillis);

}

}

};

當然不要忘了在外部,初始化完成後調用一次mHandler.sendEmptyMessageDelayed(MSG_WHAT, delayMillis);。

首尾循環無限輪播:

當然,我們在設定自動輪播時,已經做到了首尾循環無限輪播了呀。

其實這裡所說的無限輪播是指:當我們手動滑到最後一個頁面時,依然可以向後伴随手指滑動,并跳轉顯示的是第一個頁面;反之滑到首個頁面也是一樣。

旁邊那位腦子靈光的大兄弟又說了,那還不簡單,在頁面監聽器 OnPageChangeListener 裡,通過 position 分辨滑動的是不是首頁或者最後一頁,然後通過 setCurrentItem() 設定一下不就行了,還那麼麻煩。

大胸弟,你且消消氣!這裡并不使用這種方式是因為,這種方式的跳轉是十分生硬的,同時是不能實作“伴随手指滑動”這個條件的。

那究竟如何如何才能實作呢?

目前江湖流傳的有一下兩種方法:

使 adapter 的 getCount() 傳回 Integer.MAX_VALUE,再在初始化時設定目前頁面為幾千頁(如:ViewPager.setCurrentItem(1000*data.size)),其實就是障眼法,大爺心情好的話,向前滑動幾千頁也不是不可能的;

通過監聽 viewpager 的滑動來設定頁面。如目前有資料 123,則設定頁面為 31231,當頁面滑動到第一個 3 時,設定目前頁面為第二個 3,那麼左右都可以滑動,當其滑動到第二個 1 時同理。

關于第一種是目前流行最廣的方法,如果大家想檢視詳細的說明可以參考下面這篇文章(網上随便找的,對可靠性不作擔保啊):

ViewPager真正的無限輪播

http://blog.csdn.net/zhiyuan0932/article/details/52673169

關于第二種方式,嚴格意義上分析是會在滑動過程中産生生硬的跳動的。不過有位江湖義士聲稱已經解決了這種不和諧情況的發生,附上文章位址(可靠性更不作擔保啊):

http://blog.csdn.net/anyfive/article/details/52525262

打造真正的無限循環viewpager (不負責的我真的沒有測試這個可靠性)

5

自定義 ViewPager 的切換效果

本來最近封裝一個了 ViewPager 十八般花樣、樣樣都有的 PageTransformer 動效庫,想着前面少啰嗦點,然後把這章作為重點來講的。沒想到前面還是啰裡啰嗦這麼多(恍然間,我好像找到自己一直撩妹不成功單身的原因了~),好了,步入正題。

關于 ViewPager 的切換動畫,官方提供了一個内部接口 ViewPager.PageTransformer 來供我們實作自定義切換動效。這個接口裡隻提供了一個方法 public void transformPage(View view, float position),但是千萬不要小看了這兩個方法,這裡面的道道有很多呢。

transformPage 方法兩個參數,一個是 View ,這個好了解就是目前要設定動效的頁面。這個頁面并不單單是指目前顯示的頁面,即将滑出的頁面、即将滑入的頁面、已經隐藏的頁面,也就是說這個 View 是指所有的頁面。那如何分辨 View 到底是指哪個頁面呢,這個需要根據第二個參數 position 來辨識。

你千萬不要把 position 了解成了 ViewPager 頁面的下标,一定要看仔細,這個 position 可是 float 類型,下标怎麼可能是浮點型呢!

從 doc 注釋來看,目前選中的 item 的 position 永遠是 0 ,被選中 item 的前一個為 -1,被選中 item 的後一個為 1。

其實這裡文檔的描述并不是完全正确的,前後 item position 為 -1 和 1 的前提是你沒有給 ViewPager 設定 pageMargin。

如果你設定了 pageMargin,前後 item 的 position 需要分别加上(或減去,前減後加)一個偏移量(偏移量的計算方式為 pageMargin / pageWidth)。

在使用者滑動界面的時候,position 是動态變化的,下面以左滑為例(以向左為正方向):

選中 item 的 position:從 0 漸至 -1 - offset (pageMargin / pageWidth)

前一個 item 的 position:從 -1 漸至 -1 - offset (pageMargin / pageWidth)

前兩個 item 的 position:從 -2 漸至 -2 - offset (pageMargin / pageWidth),再往前就以此類推

後一個 item 的 position:從 1 + offset (pageMargin / pageWidth) 漸至 0,再往後就以此類推

每一次滑動,每個 View 對應的 position 是一個在一個區間範圍内動态漸變的過程,是以我們可以将 position 的值應用于 setAlpha(), setTranslationX(), 或者 setScaleY() 等等方法,進而實作自定義的切換動畫有一個漸變的效果。

這裡給大家舉一個視差切換動效的實作方式,我們先來看一下案例:

實作起來很簡單,就是滑動時給頁面再設定一個頁面橫向滑動的動畫,讓頁面實作滑動的速度慢于手指滑動的速度,這樣就會有種視差的效果:

@Override

publicvoidtransformPage(View page, floatposition){

intwidth = page.getWidth();

//我們給不同狀态的頁面設定不同的效果

//通過position的值來分辨頁面所處于的狀态

if(position < - 1) { //滑出的頁面

page.setScrollX(( int) (width * 0.75* - 1));

} elseif(position <= 1) { //[-1,1]

if(position < 0) { //[-1,0]

page.setScrollX(( int) (width * 0.75* position));

} else{ //[0,1]

page.setScrollX(( int) (width * 0.75* position));

}

} else{ //即将滑入的頁面

page.setScrollX(( int) (width * 0.75));

}

}

其實這裡處于大于1或者小于-1的狀态的頁面都好了解。需要詳細講解的就是 [-1,1] 這個區間狀态的頁面。大家可以想象一下:

android viewpager 間隔,ViewPager 超詳解:玩出十八般花樣

-像左滑動-

由上圖可以看出,當滑動時,(如果沒有偏移量)界面上最多出現兩個 item,一個即将滑出即将隐藏的頁面(postion變化為:從0漸到-1),一個滑入即将完全顯示的頁面(postion變化為:從1漸到0)。

這樣給每個不同狀态的頁面設定不同的動效就達到我們想要的目的了。回到上面例子中的代碼,剛才那位大兄弟又說話了,你的代碼明明 [0 -> -1] 和 [1 -> 0] 兩個狀态的 item 設定的是一樣的動效啊。這裡隻是代碼一樣,動效實際上是不一樣的,因為一個position 是大于 0 的,一個 position 是小于 0 的,滑動的方向自然是相反的。難道你非得讓我寫成 page.setScrollX((int) (width * -0.75 * -position)) 這樣嗎?

想實作更多炫酷的動效,可以檢視為大家封裝好的 PageTransformerHelp 庫,

GitHub位址:https://github.com/OCNYang/PageTransformerHelp

6

ViewPager 切換效果進階

其實上面已經把該講的自定義的方面都講的很清楚了;整個梳理下來,上面一開始給大家列舉的着重強調的幾個 ViewPager 的動态設定的方法就剩 setPageMargin(int marginPixels) 沒有說了,那麼這個方法又會給我們帶來什麼樣的神奇效果呢?

android viewpager 間隔,ViewPager 超詳解:玩出十八般花樣

那是如何實作上面這種效果呢,可以肯定的是:兩邊兩個 item 被縮小且透明度變低,是通過設定上面所說的自定義動效完成的。那如何在一個界面能夠同時看到 3 個 item 呢?那麼就通過下圖的分析和解釋告訴實作的原理:

android viewpager 間隔,ViewPager 超詳解:玩出十八般花樣

-卡片式輪播效果實作原理分析-

看了上面的解釋有沒有一種恍然大悟,迫不及待想試試的沖動?

如果通過上圖仍然略有疑惑可以看看鴻洋的這篇文章,可以說是很全面了:

巧用ViewPager 打造不一樣的廣告輪播切換效果

結尾

到此,ViewPager 的基本使用方式已經講的差不多了。想檢視更多 切換動畫 的效果,可以到本文的源碼位址進行檢視。

源碼位址:

https://github.com/OCNYang/PageTransformerHelp

參考文章:

http://blog.csdn.net/lmj623565791/article/details/51339751

http://blog.csdn.net/qq_30716173/article/details/51589251傳回搜狐,檢視更多

責任編輯: