Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android進階——ViewPager詳解之初識ViewPager(一)

Android進階——ViewPager詳解之初識ViewPager(一)

編輯:關於Android編程

引言

最近在工作中由於需要客制化系統的關系,接觸到了很多ViewPager相關的UI,發現很多底層原生的界面也還是依然采用ViewPager+Fragment的布局方式,事實上這依然還是主流,微信5.0到6.0等還是采用這種布局方法,所以還是有必要總結下ViewPager的相關知識,增強下記憶和理解,畢竟伴隨著經驗的提升即使再去閱讀同一本書也會有不一樣的體會。

一、ViewPager

1、概述

ViewPager繼承自ViewGroup,是左右兩個屏幕平滑地切換的一個容器,容器裡呈現的視圖由對應的Adapter決定,和其他標准的AdapterView類似。簡而言之就是我們通過Adapter把View放到ViewPager裡,動動手指我們就可以實現左右滑動互相切換View了。另外ViewPager的更新不是直接由ViewPager本身去完成的,而是通過觀察者去調用PagerAdapter的notifyDataSetChanged等相關方法去完成界面更新工作。

2、重要的成員

成員 說明 public ViewPager (Context context)   public ViewPager (Context context, AttributeSet attrs)       interface ViewPager.OnPageChangeListener 內部接口當切換不同Page時觸發對應回調方法     void addView(View child, int index, ViewGroup.LayoutParams params) 添加View到ViewPager裡 boolean dispatchKeyEvent(KeyEvent event) 派發鍵盤事件到下一個視圖 ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)   PagerAdapter getAdapter() 獲取對應的適配器 int getCurrentItem() 返回當前page的index int getOffscreenPageLimit() Returns the number of pages that will be retained to either side of the current page in the view hierarchy in an idle state. int getPageMargin() 返回Page之間的外間距 boolean onTouchEvent(MotionEvent ev) 重寫這個方法可以處理Touch事件 void removeView(View view) 注意不能在 draw(android.graphics.Canvas), onDraw(android.graphics.Canvas), dispatchDraw(android.graphics.Canvas)或其他相關方法調用 void setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer) 當the scroll position改變時觸發,可以用於設置自定義的動畫效果,即只要實現PageTransformer接口和其唯一的方法transformPage(View view, float position) void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) 設置Page切換事件監聽 void setPageMarginDrawable(int resId)   void setAdapter(PagerAdapter adapter) 設置Adapter

二、重要的PagerAdapter

PagerAdapter其實是一個抽象類封裝了一些接口、回調和功能方法,在實際開發過程中我們往往是使用它及其子類——FragmentPagerAdapterFragmentStatePagerAdapter的子類來填充到ViewPager中,通俗點來說,ViewPager只是個空皮囊,只有填充了PagerAdapter才能發揮其作用,正所謂巧婦也難為無米之炊,PagerAdapter就是這裡的米,有了米才能各顯神通做出各種各樣的飯。
這裡寫圖片描述

1、實現PagerAdapter時必須要重寫的四個成員方法

ViewPager不是直接與View關聯的,而是通過一個key對象,通過這個key對象我們可以追蹤並且唯一識別指定的page在PagerAdapter的位置。

Object instantiateItem(ViewGroup container, int position)——實例化item,PagerAdapter將選擇將這個對象填充到在當前ViewPager裡。

void destroyItem(ViewGroup container, int position, Object object)——將item從指定的位置移出容器

int getCount()——獲取item的總數,一般都是獲取集合的size

isViewFromObject(View,Object)——判斷容器裡的View是否與一個key值相關聯,比如說例如Fragment+ViewPager的處理,每個頁面都由它是對應的Fragment來呈現,但ViewPager並不是直接與View關聯,而是關聯一個key。我們實現這個方法也很簡單谷歌推薦我們只需一句——return view == object;

public class ViewPagerAdapter extends PagerAdapter {  
    private List mList;  
    public ViewPagerAdapter(List list) {  
        this.mList = list;  
    }  
    @Override  
    public int getCount() {  
        if (mList != null && mList.size() > 0) {  
            return mList.size();  
        } else {  
            return 0;  
        }  
    }  
    @Override  
    public boolean isViewFromObject(View view, Object object) {  
        return view == object;  
    }  
    @Override  
    public void destroyItem(ViewGroup container, int position, Object object) {  
        container.removeView((View) object);  
    }  
    @Override  
    public Object instantiateItem(ViewGroup container, int position) {  
        container.addView(mList.get(position));  
        return mList.get(position);  
    }  
    @Override  
    public int getItemPosition(Object object) {  
        return POSITION_NONE;  
    }  
}

2、PagerAdapter重要的成員方法

PagerAdapter的工作過程其實很簡單,當ViewPager裡的內容要改變的時候,就調用startUpdate方法來完成,隨即也會多次調用instantiateItem或者destroyItem方法,finishUpdate最後被調用來完成更新工作。finishUpdate最終會返回需要添加到容器裡的View和對應的 key對象(通過instantiateItem完成);而一些傳遞到destroyItem的則被移除。

成員方法 說明 void finishUpdate(ViewGroup container) 當完成page的更新工作時被調用 int getItemPosition(Object object) 但需要確定item位置是否改變時被調用,默認實現永遠返回-1 void notifyDataSetChanged() 但需要去更新的時候我們可以去調用這個方法完成 void registerDataSetObserver(DataSetObserver observer) 注冊觀察者 void unregisterDataSetObserver(DataSetObserver observer) 取消注冊 void restoreState(Parcelable state, ClassLoader loader)   Parcelable saveState()  

3、FragmentPagerAdapter

FragmentPagerAdapter繼承自PagerAdapter,主要的成員方法功能都是大同小異的,這個適配器主要就是用於快速實現Fragment在ViewPager裡面進行滑動切換的,所以,如果我們想實現Fragment的左右滑動,優先選擇ViewPager+FragmentPagerAdapter模式,因為FragmentPagerAdapter擁有自己的緩存策略,當配合ViewPager配合使用時,會緩存當前Fragment以及左邊一個、右邊一個(一共三個Fragment對象)假設有三個Fragment,那麼在ViewPager初始化之後,3個fragment都會加載完成,中間的Fragment在整個生命周期裡面只會加載一次,當最左邊的Fragment處於顯示狀態,最右邊的Fragment由於超出緩存范圍,會被銷毀,當再次滑到中間的Fragment的時候,最右邊的Fragment會被再次初始化。所以比較適合用戶制作較少頁面切換的tab界面(最多3個),FragmentPagerAdapter會對我們浏覽過Fragment進行緩存,保存這些界面的臨時狀態,這樣當我們左右滑動的時候,界面切換更加的流暢。但是,這樣也會增加程序占用的內存。那麼3個以上的話,谷歌推薦移步到FragmentStatePagerAdapter

3.1、當我們使用FragmentPagerAdapter的時候,它的宿主ViewPager**必須設置對應的id,同時必須實現兩個方法和構造方法**。

getCount()——返回的是ViewPager頁面的數量,即Fragement的數量。

getItem(int position)——返回的是要顯示的fragment對象。

//實現一個FragmentPagerAdapter最少只需要實現getCount和getItem方法即可
FragmentPagerAdapter fragmentadapter = new FragmentPagerAdapter(  
             getSupportFragmentManager()) {  
         @Override  
         public int getCount() {  
             return fragments.size();  //getCount()返回的是ViewPager頁面的數量,多少個Fragement對應多少個頁面
         }  
         @Override  
         public Fragment getItem(int position) {  
             return fragments.get(position);//返回的是要顯示的fragment對象。
         }  
      }  
   };  

4、FragmentStatePagerAdapter

當然除了FragmentPagerAdapter之外,還有一個類也是專門用於快速實現多個(3個以上)Fragment在ViewPager裡面進行適配並滑動切換的,即FragmentStatePagerAdapterFragmentStatePagerAdapter也直接繼承自PagerAdapter的,其工作方式和ListView十分相似的。當Fragment對用戶不可見的時候,整個Fragment會被銷毀並且保存Fragment的保存狀態。基於這樣的特性,FragmentStatePagerAdapter比FragmentPagerAdapter更適合用於很多界面之間的轉換,而且消耗更少的內存資源。

3.1、當我們使用FragmentStatePagerAdapter的時候,它的宿主ViewPager必須設置對應的id,同時必須實現兩個方法

getCount()——返回的是ViewPager頁面的數量,即Fragement的數量。

getItem(int position)——返回的是要顯示的fragment對象。

5、關於使用中發現,在刪除或者修改數據的時候,PagerAdapter無法像BaseAdapter那樣僅通過notifyDataSetChanged方法通知刷新View。

通常情況下調用notifyDataSetChanged方法會讓ViewPager通過Adapter的getItemPosition方法查詢一遍所有child view,如果所有child view位置均為POSITION_NONE,表示所有的child view都不存在,ViewPager會調用destroyItem方法銷毀,並且重新生成,從而加大系統開銷,並在一些復雜情況下導致邏輯問題。特別是對於只是希望更新child view內容的時候,但造成了完全不必要的開銷,

列表內容如果是針對於child view比較簡單的情況(例如僅有TextView、ImageView等,沒有ListView等展示數據的情況),可以重寫PagerAdapter的方法getItemPosition
@Override    
public int getItemPosition(Object object) {    
    return POSITION_NONE;    
}
但復雜的情況則需要根據自己的需求來實現notifyDataSetChanged的功能,比如,在僅需要對某個View內容進行更新時,在instantiateItem()時,用View.setTag方法加入標志,在需要更新信息時,通過findViewWithTag的方法找到對應的View進行局部更新。

6、FragmentPagerAdapter和FragmentStatePagerAdapter

FragmentPagerAdapter適合在較少Fragment滑動切換的界面使用的,劃過的fragment會保存在內存中,盡管已經劃過。而FragmentStatePagerAdapter和ListView有點類似,只會保存當前界面,以及下一個界面和上一個界面(如果有),最多保存3個,其他會被銷毀掉。最後要注意的是FragmentStatePagerAdapter可能不經意間會造成內存未正常回收,嚴重導致內存溢出,比如圖片資源沒有釋放,資源引用問題。(之前在網上看到過EditText由於保存焦點導致Fragment未被釋放而導致OOM,設置edtText.saveEanble(false)就可以解決此問題)。最後PagerAdapter都只是數據集,數據集的改變只能發生在主線程裡並且以必須調用notifyDataSetChanged() 來通知完成更新。

小結

這篇主要是簡單描述了下ViewPager的主要功能以及PagerAdapter的角色和一些簡略的原理,由於篇幅問題,下一篇再結合實例來總結下。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved