ViewPager 的使用

一. 簡介

ViewPager 是support v4 包提供的控件眯停,可以實(shí)現(xiàn)一組View 切換顯示的效果济舆。

二. 使用

使用ViewPager 也比較簡單,主要有以下幾步:

  1. 獲取ViewPager 實(shí)例莺债,包括從XML 或直接new

  2. 自定義ViewPager 的adapter滋觉,并為ViewPager 設(shè)置adapter

  3. 設(shè)置ViewPager 的切換效果(可選)

  4. 設(shè)置ViewPager 的事件監(jiān)聽(可選)

三. 自定義Adapter

ViewPager 的adapter 類型為PagerAdapter,它是一個(gè)抽象類齐邦,support 包提供了兩種實(shí)現(xiàn)椎侠,一個(gè)FragmentPagerAdapter,另一個(gè)是FragmentStatePagerAdapter侄旬,這兩個(gè)Adapter 是ViewPager 和Fragment 結(jié)合使用時(shí)所用肺蔚,稍后介紹。

現(xiàn)在介紹一下儡羔,繼承PagerAdapter 的自定義Adapter宣羊。繼承PagerAdapter 必須要重寫兩個(gè)方法,即 getCount汰蜘、isViewFromObject仇冯,同時(shí)為了保證功能的實(shí)現(xiàn),還應(yīng)該重寫instantiateItem 和 destroyItem 方法族操,示例代碼如下:

    private class MyPagerAdapter extends PagerAdapter { 

        private List<Uri> data; 

        MyPagerAdapter(List<Uri> data) { 
            this.data = data; 
        } 

        @Override 
        public Object instantiateItem(ViewGroup container, int position) { 
            // 創(chuàng)建 Item, 本例為顯示圖片
            View view = LayoutInflater.from(container.getContext()).inflate(R.layout.item_two, container, false); 
            ImageView image = view.findViewById(R.id.show_image_iv); 

            Picasso.with(container.getContext()) 
                    .load(data.get(position)) 
                    .into(image); 

            container.addView(view); 
            return view; 
        } 

        @Override 
        public void destroyItem(ViewGroup container, int position, Object object) { 

            if (object instanceof View) { 
                container.removeView((View)object); 
            } 
        } 

        @Override 
        public int getItemPosition(Object object) { 
            return POSITION_UNCHANGED;     // 默認(rèn)返回苛坚,與刷新有關(guān)
        } 

        @Override 
        public boolean isViewFromObject(View view, Object object) { 
            return object == view; 
        } 

        @Override 
        public int getCount() { 
            return data.size(); 
        } 

        public void setData(List<Uri> data) { 
            this.data = data; 
            notifyDataSetChanged(); 
        } 
    }

效果:


pager.gif

PagerAdapter 工作流程:

PagerAdapter 作為 ViewPager 的適配器,無論 ViewPager 有多少頁色难,PagerAdapter 在初始化時(shí)也只初始化開始的2個(gè) View泼舱,即調(diào)用2次instantiateItem 方法。而接下來每當(dāng) ViewPager 滑動時(shí)枷莉,PagerAdapter 都會調(diào)用 destroyItem 方法將距離該頁2個(gè)步幅以上的那個(gè) View 銷毀娇昙,以此保證 PagerAdapter 最多只管轄3個(gè) View,且當(dāng)前 View 是3個(gè)中的中間一個(gè)笤妙,如果當(dāng)前 View 缺少兩邊的 View冒掌,那么就 instantiateItem噪裕,如里有超過2個(gè)步幅的就 destroyItem。

在實(shí)現(xiàn)Adapter 時(shí)股毫,有幾點(diǎn)需要注意膳音,可能會出坑。

  • 第一個(gè)就是destroyItem 方法铃诬,如果使用 container.removeViewAt(index) 方法祭陷,可能會導(dǎo)致出現(xiàn) 越界 crash,所以應(yīng)該使用removeView(object) 方法氧急。

  • 第二個(gè)是getItemPosition 方法颗胡,默認(rèn)的返回值是 POSITION_UNCHANGED毫深,還有另外一種值是 POSITION_NONE吩坝,這兩個(gè)值的區(qū)別在于刷新數(shù)據(jù)時(shí),

Viewpager 的刷新過程是這樣的哑蔫,在每次調(diào)用 PagerAdapter 的 notifyDataSetChanged() 方法時(shí)钉寝,都會激活 getItemPosition(Object object) 方法,該方法會遍歷 ViewPager 的所有 Item(由緩存的 Item 數(shù)量決定闸迷,默認(rèn)為當(dāng)前頁和其左右加起來共3頁嵌纲,這個(gè)可以自行設(shè)定,但是至少會緩存2頁)腥沽,為每個(gè) Item 返回一個(gè)狀態(tài)值(POSITION_NONE/POSITION_UNCHANGED)逮走,

如果是 POSITION_NONE,那么該 Item 會被 destroyItem(ViewGroup container, int position, Object object) 方法 remove 掉今阳,然后重新加載师溅,

如果是 POSITION_UNCHANGED,就不會重新加載盾舌,默認(rèn)是 POSITION_UNCHANGED墓臭,所以如果不重寫 getItemPosition(Object object),修改返回值妖谴,就無法看到 notifyDataSetChanged() 的刷新效果窿锉。

注:當(dāng)ViewPager 的Item 是Fragment 時(shí),系統(tǒng)提供了FragmentPagerAdapter 和 FragmentStatePagerAdapter膝舅,但是動態(tài)修改時(shí)可能還是會有一些問題嗡载,需要進(jìn)行一些特殊處理。

四. 切換效果

有時(shí)候我們可能覺得默認(rèn)的切換太平淡仍稀,想來點(diǎn)花哨的效果洼滚,Android 提供了一個(gè)接口 PageTransformer,實(shí)現(xiàn)這個(gè)接口琳轿,并為ViewPager 設(shè)置Transformer 即可實(shí)現(xiàn)切換特效判沟。

PageTransformer 接口只有一個(gè)方法耿芹,即 public void transformPage(View page, float position) ,它有兩個(gè)參數(shù)挪哄,

page表示 ViewPager 中的一頁吧秕,

position表示page當(dāng)前的位置,[-1, 0)表示屏幕左邊的page(部分可見)迹炼,[0, 0]表示屏幕上的page(完全可見)砸彬,(0, 1]表示屏幕右邊的page(部分可見),具體看下圖:

image.png

當(dāng)page向左邊滑動時(shí)斯入,position從0向-1變化砂碉,當(dāng)position==-1時(shí)完全不可見;當(dāng)page向右滑動時(shí)刻两,position從0向1變化增蹭,當(dāng)position==1時(shí)完全不可見。這樣磅摹,我們根據(jù)position 的變化來對View 進(jìn)行相關(guān)的操作滋迈,比如旋轉(zhuǎn)、縮放户誓、透明度等饼灿,就可以實(shí)現(xiàn)不同的切換效果。如下例帝美,

private class GalleryTransformer implements ViewPager.PageTransformer { 
        private static final float MAX_ROTATION = 20.0F; 
        private static final float MIN_SCALE = 0.75f; 
        private static final float MAX_TRANSLATE = 20.0F; 

        private int width = UiUtils.getScreenWidth(ViewActivity.this); 
        private boolean firstPage;      // 是否第一頁 
        private boolean firstTime = true;   // 是否第一遍執(zhí)行 
        private int count;      // 執(zhí)行次數(shù), 與setOffscreenPageLimit 有關(guān) 

        GalleryTransformer() { 
            firstPage = true; 
        } 

        // 第一頁和第一遍的邏輯碍彭,用于處理一頁顯示多個(gè)Item 時(shí),初始狀態(tài)第二頁的狀態(tài) 

        @Override 
        public void transformPage(View page, float position) { 
            if (count++ == 3) { 
                firstTime = false;      // 第一遍執(zhí)行完 
            } 

            float offset = (float)(width - page.getWidth()) / 2;        // 因?yàn)橐豁擄@示多個(gè)Item 
            if (page.getWidth() != 0) { 
                position = position - offset / page.getWidth();         // 所以要對position 進(jìn)行相關(guān)偏移悼潭,否則將顯示異常 
            } 

            if (position < -1.0) {                      // (-∞, -1) 
                page.setTranslationX(MAX_TRANSLATE); 
                page.setScaleX(MIN_SCALE); 
                page.setScaleY(MIN_SCALE); 
                page.setRotationY(-MAX_ROTATION); 
            } else if (firstPage || (!firstTime && position <= 1.0)) {      // [-1, 1] 
                firstPage = false; 

                page.setTranslationX(-MAX_TRANSLATE * position); 
                float scale = MIN_SCALE + (1-MIN_SCALE) * (1.0f - Math.abs(position)); 
                page.setScaleX(scale);      // 縮放 
                page.setScaleY(scale); 
                page.setRotationY(MAX_ROTATION * position);     // 旋轉(zhuǎn) 

            } else if (firstTime || position > 1.0){        // (1, +∞) 

                page.setTranslationX(-MAX_TRANSLATE); 
                page.setScaleX(MIN_SCALE); 
                page.setScaleY(MIN_SCALE); 
                page.setRotationY(MAX_ROTATION); 
            } 
        } 
    }

形成的效果如下:


share3.gif
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庇忌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子女责,更是在濱河造成了極大的恐慌漆枚,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抵知,死亡現(xiàn)場離奇詭異墙基,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)刷喜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門残制,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人掖疮,你說我怎么就攤上這事初茶。” “怎么了浊闪?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵恼布,是天一觀的道長螺戳。 經(jīng)常有香客問我,道長折汞,這世上最難降的妖魔是什么倔幼? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮爽待,結(jié)果婚禮上损同,老公的妹妹穿的比我還像新娘。我一直安慰自己鸟款,他們只是感情好膏燃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著何什,像睡著了一般组哩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上富俄,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天禁炒,我揣著相機(jī)與錄音,去河邊找鬼霍比。 笑死,一個(gè)胖子當(dāng)著我的面吹牛暴备,可吹牛的內(nèi)容都是我干的悠瞬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼涯捻,長吁一口氣:“原來是場噩夢啊……” “哼浅妆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起障癌,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凌外,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后涛浙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體康辑,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年轿亮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疮薇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡我注,死狀恐怖按咒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情但骨,我是刑警寧澤励七,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布智袭,位于F島的核電站,受9級特大地震影響掠抬,放射性物質(zhì)發(fā)生泄漏补履。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一剿另、第九天 我趴在偏房一處隱蔽的房頂上張望箫锤。 院中可真熱鬧,春花似錦雨女、人聲如沸谚攒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馏臭。三九已至,卻和暖如春讼稚,著一層夾襖步出監(jiān)牢的瞬間括儒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工锐想, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帮寻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓赠摇,卻偏偏與公主長得像固逗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子藕帜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

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