1. 程式人生 > >ViewPager的詳細介紹和圖片滑動功能例項

ViewPager的詳細介紹和圖片滑動功能例項

1、使用場景

在APP設計中,如果涉及到左右滑動功能,如:下方導航條、上方導航條、圖片滑動、翻頁能功能時,則可以直接使用ViewPager進行開發,ViewPager自帶滑動和翻頁效果,可以用ViewPager自身的adapter進行開發,要方便很多。

2、屬性詳解

ViewPager

ViewPager 是負責翻頁的一個 View。準確說是一個 ViewGroup,包含多個 View 頁,在手指橫向滑動螢幕時,其負責對 View 進行切換。為了生成這些 View 頁,需要提供一個 PagerAdapter 來進行和資料繫結以及生成最終的 View 頁。

PagerAdapter

PageAdapter 是 ViewPager 的支持者,ViewPager 將呼叫它來取得所需顯示的頁,而 PageAdapter 也會在資料變化時,通知 ViewPager。這個類也是FragmentPagerAdapter 以及 FragmentStatePagerAdapter 的基類。如果繼承自該類,至少需要實現 instantiateItem(), destroyItem(), getCount() 以及 isViewFromObject()。

  • getItemPosition()
    • 該函式用以返回給定物件的位置,給定物件是由 instantiateItem() 的返回值。
    • 在 ViewPager.dataSetChanged() 中將對該函式的返回值進行判斷,以決定是否最終觸發 PagerAdapter.instantiateItem() 函式。
    • 在 PagerAdapter 中的實現是直接傳回 POSITION_UNCHANGED。如果該函式不被過載,則會一直返回 POSITION_UNCHANGED,從而導致 ViewPager.dataSetChanged() 被呼叫時,認為不必觸發 PagerAdapter.instantiateItem()。很多人因為沒有過載該函式,而導致呼叫
       PagerAdapter.notifyDataSetChanged() 後,什麼都沒有發生。
  • instantiateItem()
    • 在每次 ViewPager 需要一個用以顯示的 Object 的時候,該函式都會被 ViewPager.addNewItem() 呼叫。
  • notifyDataSetChanged()
    • 在資料集發生變化的時候,一般 Activity 會呼叫 PagerAdapter.notifyDataSetChanged(),以通知 PagerAdapter,而 PagerAdapter 則會通知在自己這裡註冊過的所有 DataSetObserver。其中之一就是在 ViewPager.setAdapter() 中註冊過的 PageObserver。PageObserver 則進而呼叫 ViewPager.dataSetChanged(),從而導致 ViewPager 開始觸發更新其內含 View 的操作。

FragmentPagerAdapter

FragmentPagerAdapter 繼承自 PagerAdapter。相比通用的 PagerAdapter,該類更專注於每一頁均為 Fragment 的情況。如文件所述,該類內的每一個生成的 Fragment 都將儲存在記憶體之中,因此適用於那些相對靜態的頁,數量也比較少的那種;如果需要處理有很多頁,並且資料動態性較大、佔用記憶體較多的情況,應該使用FragmentStatePagerAdapter。FragmentPagerAdapter 過載實現了幾個必須的函式,因此來自 PagerAdapter 的函式,我們只需要實現 getCount(),即可。且,由於 FragmentPagerAdapter.instantiateItem() 的實現中,呼叫了一個新增的虛擬函式 getItem(),因此,我們還至少需要實現一個 getItem()。因此,總體上來說,相對於繼承自 PagerAdapter,更方便一些。

  • getItem()
    • 該類中新增的一個虛擬函式。函式的目的為生成新的 Fragment 物件。過載該函式時需要注意這一點。在需要時,該函式將被 instantiateItem() 所呼叫。
    • 如果需要向 Fragment 物件傳遞相對靜態的資料時,我們一般通過 Fragment.setArguments() 來進行,這部分程式碼應當放到 getItem()。它們只會在新生成 Fragment 物件時執行一遍。
    • 如果需要在生成 Fragment 物件後,將資料集裡面一些動態的資料傳遞給該 Fragment,那麼,這部分程式碼不適合放到 getItem() 中。因為當資料集發生變化時,往往對應的 Fragment 已經生成,如果傳遞資料部分程式碼放到了 getItem() 中,這部分程式碼將不會被呼叫。這也是為什麼很多人發現呼叫 PagerAdapter.notifyDataSetChanged() 後,getItem() 沒有被呼叫的一個原因。
  • instantiateItem()
    • 函式中判斷一下要生成的 Fragment 是否已經生成過了,如果生成過了,就使用舊的,舊的將被 Fragment.attach();如果沒有,就呼叫 getItem() 生成一個新的新的物件將被 FragmentTransation.add()
    • FragmentPagerAdapter 會將所有生成的 Fragment 物件通過 FragmentManager 儲存起來備用,以後需要該 Fragment 時,都會從 FragmentManager 讀取,而不會再次呼叫 getItem() 方法
    • 如果需要在生成 Fragment 物件後,將資料集中的一些資料傳遞給該 Fragment,這部分程式碼應該放到這個函式的過載裡。在我們繼承的子類中,過載該函式,並呼叫 FragmentPagerAdapter.instantiateItem() 取得該函式返回 Fragment 物件,然後,我們該 Fragment 物件中對應的方法,將資料傳遞過去,然後返回該物件。
    • 否則,如果將這部分傳遞資料的程式碼放到 getItem()中,在 PagerAdapter.notifyDataSetChanged() 後,這部分資料設定程式碼將不會被呼叫。
  • destroyItem()
    • 該函式被呼叫後,會對 Fragment 進行 FragmentTransaction.detach()。這裡不是 remove(),只是 detach(),因此 Fragment 還在 FragmentManager 管理中,Fragment 所佔用的資源不會被釋放。

FragmentStatePagerAdapter

FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 一樣,是繼承子 PagerAdapter。但是,和 FragmentPagerAdapter 不一樣的是,正如其類名中的 'State' 所表明的含義一樣,該 PagerAdapter 的實現將只保留當前頁面,當頁面離開視線後,就會被消除,釋放其資源;而在頁面需要顯示時,生成新的頁面(就像 ListView 的實現一樣)。這麼實現的好處就是當擁有大量的頁面時,不必在記憶體中佔用大量的記憶體。

  • getItem()
    • 一個該類中新增的虛擬函式。
    • 函式的目的為生成新的 Fragment 物件。
    • Fragment.setArguments() 這種只會在新建 Fragment 時執行一次的引數傳遞程式碼,可以放在這裡。
    • 由於 FragmentStatePagerAdapter.instantiateItem() 在大多數情況下,都將呼叫 getItem() 來生成新的物件,因此如果在該函式中放置與資料集相關的 setter 程式碼,基本上都可以在 instantiateItem() 被呼叫時執行,但這和設計意圖不符。畢竟還有部分可能是不會呼叫 getItem() 的。因此這部分程式碼應該放到 instantiateItem() 中。
  • instantiateItem()
    • 除非碰到 FragmentManager 剛好從 SavedState 中恢復了對應的 Fragment 的情況外,該函式將會呼叫 getItem() 函式,生成新的 Fragment 物件。新的物件將被 FragmentTransaction.add()。
    • FragmentStatePagerAdapter 就是通過這種方式,每次都建立一個新的 Fragment,而在不用後就立刻釋放其資源,來達到節省記憶體佔用的目的的。
  • destroyItem()

3、Kotlin實現圖片滑動功能

(1)效果圖如下:

(2)首先先建立一個Activity

class TopSlipViewPagerActivity : BaseActivity(){
    var listBean = ArrayList<PagerBean>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.view_pager_layout)
        initView()
    }

    /**
     * 初始化控制元件
     */
    fun initView(){
        valueAdd()
        view_pager!!.adapter=MyPagerAdapter(this,listBean)
    }

    fun valueAdd(){
        for(i in 1..3){
            var bean =PagerBean()
            bean!!.title=i.toString() + "/3"
            bean!!.pictureUrl="222"
            bean!!.picCategory="0"
            listBean!!.add(bean)
        }
    }
}

(3)建立該Activity的xml檔案

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v4.view.ViewPager>
</LinearLayout>

(4)建立Activity的Adapter

class TopSlipViewPagerActivity : BaseActivity(){
    var listBean = ArrayList<PagerBean>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.view_pager_layout)
        initView()
    }

    /**
     * 初始化控制元件
     */
    fun initView(){
        valueAdd()
        view_pager!!.adapter=MyPagerAdapter(this,listBean)
    }

    fun valueAdd(){
        for(i in 1..3){
            var bean =PagerBean()
            bean!!.title=i.toString() + "/3"
            bean!!.pictureUrl="222"
            bean!!.picCategory="0"
            listBean!!.add(bean)
        }
    }
}

(5)建立adapter的xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:orientation="vertical">

    <TextView
        android:id="@+id/view_pager_textview_1"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:gravity="center"
        android:text="1/3"
        android:textColor="@color/white"
        android:textSize="@dimen/text_size_18" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:layout_gravity="center"
            android:background="@drawable/xuan_chaun"
            android:gravity="center" />
    </LinearLayout>
</LinearLayout>

4、總結

好了,ViewPager就講解完了,是不是很方便。只要記住,ViewPager是和它本身的Adapter搭配使用,只要將需要的值賦給Adapter就可以了,如果View都相似,可以通過PagerAdapter來迴圈一個頁面新增資料。如果每個View不同,就可以建立多個Fragment頁,通過FragmentPagerAdapter來實現左右滑動。