鍍金池/ 教程/ Android/ 第十七章-ViewPager切換界面
第十八章-ViewPager+FragmentStatePagerAdapter實現(xiàn)仿微信Tab
第十五章-GridView實現(xiàn)動態(tài)添加和刪除子項
第九章-進度條ProgressBar
第十二章-經(jīng)典的ListView
第十四章-GridView控件
第八章-時間相關(guān)控件
第七章-下拉框Spinner控件
第二章-EditText探秘
第二十章-Android菜單之上下文菜單
第十一章-各種對話框Dialog集錦
第二十一章-Android菜單之子菜單
第六章-切換類TextSwitcher和ImageSwitcher
第十七章-ViewPager切換界面
第五章-開關(guān)按鈕ToggleButton和Switch
第二十二章-PopupWindow浮動窗
第十六章-幻燈片ViewFlipper
第二十四章-RecyclerView動態(tài)添加、刪除及點擊事件
第三章-交互之王Button控件
第二十三章-全新控件RecyclerView
第一章-好玩的TextView
第十三章-ListView擴展(多選、全選、反選)
第四章-玩轉(zhuǎn)單選和多選按鈕
第十章-可以拖動的ProgressBar-SeekBar
第十九章-Android菜單之選項菜單

第十七章-ViewPager切換界面

ViewFlipper一般僅用于圖片的展示,如果要進行布局文件的切換就要用到ViewPager控件,其繼承結(jié)構(gòu)如下:

public class
ViewPager
extends ViewGroup
java.lang.Object
   ?    android.view.View
       ?    android.view.ViewGroup
           ?    android.support.v4.view.ViewPager

繼承自ViewGroup可以看出來是一個容器類,類前包名是android.support.v4,這是一個兼容包,注意在布局文件中引入該控件時,標(biāo)簽要寫全即:< android.support.v4.view.ViewPager >。API文檔中對ViewPager進行了描述,總結(jié)如下:

  • ViewPager類直接繼承自ViewGroup類,作為一個容器類,可以向其中添加Viewl類
  • 數(shù)據(jù)源和顯示之間需要一個適配器類PagerAdapter進行適配
  • ViewPager經(jīng)常和Fragemnet一起使用,并且提供專門的適配器類FragmentPagerAdapter和FragmentStatePagerAdapter類供開發(fā)者調(diào)用。

實現(xiàn)PageAdapter必須實現(xiàn)四個方法,這里進行介紹:

  • public Object instantiateItem(ViewGroup container, int position) :初始化一個子View
  • public void destroyItem(ViewGroup container, int position,Object object):銷毀一個子View
  • public int getCount():返回子View的個數(shù)
  • public boolean isViewFromObject(View arg0, Object arg1):返回一個布爾型變量,判斷子View是否來自O(shè)bject。 主布局文件(activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

注意,標(biāo)簽內(nèi)需要填入包.類名,否則會報錯。 子布局文件(view1.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">
    <TextView
        android:layout_width="match_parent"
        android:gravity="center"
        android:text="頁面1"
        android:textSize="30sp"
        android:layout_height="match_parent" />
</LinearLayout>

一共有三個子View文件作為演示,每個子View中都包含一個TextView,由于只是text屬性的不同,這里僅貼出view1的代碼。 適配器類(MyViewPagerAdapter.java)

public class MyViewPagerAdapter extends PagerAdapter {
    private List<View> datas;
   public  MyViewPagerAdapter(List<View> datas ){
       this.datas=datas;
   }
    @Override
    public int getCount() {//返回頁卡數(shù)量
        return datas.size();
    }
    @Override
    public boolean isViewFromObject(View view, Object object) {//判斷View是否來自O(shè)bject
        return view==object;
    }
    @Override
    public Object instantiateItem(ViewGroup container, int position) {//初始化一個頁卡
         container.addView(datas.get(position));
        return datas.get(position);
    }
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {//銷毀一個頁卡
        container.removeView(datas.get(position));
    }
}

自定義適配器類MyViewPagerAdapter繼承自PagerAdapter,編寫了構(gòu)造函數(shù),用于傳入datas數(shù)據(jù)集。此外,覆寫了四個必須要覆寫的方法,這四個方法的含義參照注釋。 (MainActivity.java)

public class MainActivity extends Activity {
    private ViewPager viewPager;
    private List<View> datas;
    private MyViewPagerAdapter myViewPagerAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager=(ViewPager)findViewById(R.id.viewPager);
        initDatas();//初始化數(shù)據(jù)集
        myViewPagerAdapter=new MyViewPagerAdapter(datas);
        viewPager.setAdapter(myViewPagerAdapter);//設(shè)置適配器
    }
    private void initDatas() {
        datas=new ArrayList<>();
        View view1= LayoutInflater.from(this).inflate(R.layout.view1,null);
        View view2= LayoutInflater.from(this).inflate(R.layout.view2,null);
        View view3= LayoutInflater.from(this).inflate(R.layout.view3,null);
        datas.add(view1);
        datas.add(view2);
        datas.add(view3);
    }
}

總結(jié)一下,PagerView的實現(xiàn)可以分為三個步驟:

  1. 準(zhǔn)備數(shù)據(jù)源(initDatas)
  2. 準(zhǔn)備適配器類并初始化(MyViewPagerAdapter)
  3. 設(shè)置適配器(setAdapter)

運行實例如下:

http://wiki.jikexueyuan.com/project/twenty-four-Scriptures/images/17-1.png" alt="這里寫圖片描述" />

http://wiki.jikexueyuan.com/project/twenty-four-Scriptures/images/17-2.png" alt="這里寫圖片描述" />

這時,左右滑動屏幕就可以切換不同的View了,下面我們看一下如何添加頂部或底部導(dǎo)航,Android提供了兩種方式供我們選擇,分別是PagerTitleStrip和PagerTabStrip,下面分別研究一下兩者的異同點。

  • PagerTitleStrip API中這么定義:是一個非交互的當(dāng)前頁面指示器,一般指示ViewPager中的前一頁、當(dāng)前頁和下一頁三個頁面。可以通過PagerTitleStrip標(biāo)簽添加到xml布局當(dāng)中。我們可以設(shè)置layout_gravity屬性為TOP或者BOTTOM來決定在頁面頂部或者底部顯示,添加PagerTitleStrip要在適配器中覆寫getPageTitle方法。 上面是抽象的理論描述,下面通過一個實例來看一下如何在ViewPager中添加PagerTitleStrip控件。

主布局文件(activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v4.view.PagerTitleStrip
            android:id="@+id/pagerTitleStrip"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </android.support.v4.view.PagerTitleStrip>
    </android.support.v4.view.ViewPager>
</RelativeLayout>

PagerTitleStrip標(biāo)簽也要設(shè)置全路徑,并放在ViewPager標(biāo)簽內(nèi),默認沒有添加layout_gravity屬性,標(biāo)簽顯示在頁面頂部,若想設(shè)置在底部,添加這一屬性設(shè)置其值為BOTTOM即可。 適配器類(MyViewPagerAdapter.java)

public class MyViewPagerAdapter extends PagerAdapter {
    private List<View> datas;
    private List<String> titles;
   public  MyViewPagerAdapter(List<View> datas,List<String> titles ){
       this.datas=datas;
       this.titles=titles;
   }
    @Override
    public int getCount() {//返回頁卡數(shù)量
        return datas.size();
    }
    @Override
    public boolean isViewFromObject(View view, Object object) {//判斷View是否來自O(shè)bject
        return view==object;
    }
    @Override
    public Object instantiateItem(ViewGroup container, int position) {//初始化一個頁卡
         container.addView(datas.get(position));
        return datas.get(position);
    }
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {//銷毀一個頁卡
        container.removeView(datas.get(position));
    }
    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }
}

為了方便觀察,較上一個實例增加或修改的代碼部分進行了加粗,首先是修改了構(gòu)造方法,多傳入了一個標(biāo)題的數(shù)據(jù)集,然后覆寫了一個getPagerTitle的方法,這個方法可以根據(jù)position參數(shù)返回對應(yīng)的title。

MainActivity(MainActivity.java)

public class MainActivity extends Activity {
    private ViewPager viewPager;
    private PagerTitleStrip pagerTitleStrip;
    private List<View> datas;
    private List<String> titles;
    private MyViewPagerAdapter myViewPagerAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager=(ViewPager)findViewById(R.id.viewPager);
      pagerTitleStrip=(PagerTitleStrip)findViewById(R.id.pagerTitleStrip);
        initDatas();
        myViewPagerAdapter=new MyViewPagerAdapter(datas,titles);
        viewPager.setAdapter(myViewPagerAdapter);
    }
    private void initDatas() {
        datas=new ArrayList<>();
        titles=new ArrayList<>();
        View view1= LayoutInflater.from(this).inflate(R.layout.view1,null);
        View view2= LayoutInflater.from(this).inflate(R.layout.view2,null);
        View view3= LayoutInflater.from(this).inflate(R.layout.view3,null);
        datas.add(view1);
        datas.add(view2);
        datas.add(view3);
        titles.add("第一頁");
        titles.add("第二頁");
        titles.add("第三頁");
    }
}

較上一個實例來講,這里添加了一個標(biāo)題的數(shù)據(jù)集titles,初始化MyViewPagerAdapter的時候傳入了兩個參數(shù),頁面布局數(shù)據(jù)集(datas)和標(biāo)題數(shù)據(jù)集(titles)。 運行實例如下:

http://wiki.jikexueyuan.com/project/twenty-four-Scriptures/images/17-3.png" alt="這里寫圖片描述" />

點擊頂部的標(biāo)題欄,不會進行頁面切換,正如API文檔里描述的那樣-non-interactive indicator,只能作為一個頁面指示器,不具有交互作用,下面我們共同來實踐一下具有交互效果的PagerTabStrip。

  • PagerTabStrip API中這么描述PagerTabStrip:

PagerTabStrip is an interactive indicator of the current, next, and previous pages of a ViewPager. It is intended to be used as a child view of a ViewPager widget in your XML layout. Add it as a child of a ViewPager in your layout file and set its android:layout_gravity to TOP or BOTTOM to pin it to the top or bottom of the ViewPager. The title from each page is supplied by the method getPageTitle(int) in the adapter supplied to the ViewPager.

For a non-interactive indicator, see PagerTitleStrip. 這里把英文的API文檔貼出來,帶領(lǐng)大家大致翻譯一下:PagerTabStrip是一個關(guān)于當(dāng)前頁、下一頁和上一頁可交互的頁面指示器。作為一個子View布局在ViewPager控件內(nèi)部。同時,也可以通過設(shè)置layout_gravity屬性為TOP或BOTTOM來決定顯示在頁面頂部或底部。每個頁面標(biāo)題是通過適配器類中覆寫getPageTitle方法提供給ViewPager的。最后一句也點明了,若要使用一個非交互指示器,可以參考PagerTitleStrip。

從API文檔上可以看出,兩個方式使用方法一樣,因此,這里只要在布局文件中更換一下標(biāo)簽如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v4.view.PagerTabStrip
            android:id="@+id/pagerTabStrip"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </android.support.v4.view.PagerTabStrip>
    </android.support.v4.view.ViewPager>
</RelativeLayout>

將標(biāo)簽換成android.support.v4.view.PagerTabStrip。 MainActivity.java中,將PagerTitleStrip換成PagerTabStrip即可,其余代碼不變:

private PagerTabStrip pagerTabStrip= (PagerTabStrip)findViewById(R.id.pagerTabStrip);

運行實例如下:

http://wiki.jikexueyuan.com/project/twenty-four-Scriptures/images/17-4.png" alt="這里寫圖片描述" />

點擊頂部指示頁,可以進行頁面切換,除此之外,較PagerTitleStrip而言,PagerTabStrip當(dāng)前頁的下面還多了一個小橫標(biāo),以上功能基本實現(xiàn)了,下面來研究一下,如何讓外觀變得更漂亮,Android也給我們提供了一些方法用于改變指示欄的樣式。常用方法參考下表:

http://wiki.jikexueyuan.com/project/twenty-four-Scriptures/images/17-5.png" alt="這里寫圖片描述" />

在MainActivity.java的onCreate方法中加入如下代碼:

pagerTabStrip.setDrawFullUnderline(false);//取消標(biāo)題欄子View之間的分割線
pagerTabStrip.setTabIndicatorColor(Color.WHITE);//改變指示器顏色為白色
pagerTabStrip.setTextColor(Color.WHITE);//該變字體顏色為白色
pagerTabStrip.setBackgroundResource(android.R.drawable.alert_dark_frame);//設(shè)置標(biāo)題欄背景圖片

再次運行實例如下:

http://wiki.jikexueyuan.com/project/twenty-four-Scriptures/images/17-6.png" alt="這里寫圖片描述" />

上面講解了加載布局文件的ViewPager,由API文檔可知,ViewPager還可以加載Fragment控件,也有兩個適配器類(FragmentPagerAdapter和FragmentStatePagerAdapter)可以實現(xiàn),下面分別實現(xiàn)并介紹相關(guān)異同點。

  • FragmentPagerAdapter實現(xiàn)
public abstract class
FragmentPagerAdapter
extends PagerAdapter
java.lang.Object
   ?    android.support.v4.view.PagerAdapter
       ?    android.support.v4.app.FragmentPagerAdapter

由繼承結(jié)構(gòu)可以看出FragmentPagerAdapter繼承自PagerAdapter,子頁面由Fragment組成,該適配器沒有實現(xiàn)頁面銷毀的方法,所有的頁面都保存在內(nèi)存當(dāng)中,當(dāng)頁面比較大時要考慮使用FragmentStatePagerAdapter適配器類。 實現(xiàn)FragmentPagerAdapter時必須要覆寫的方法是getItem和getCount方法。

下面通過一個實例進行實現(xiàn),分三個步驟實現(xiàn):

  • 準(zhǔn)備Fragment的數(shù)據(jù)集
  • 編寫適配器類
  • 初始化數(shù)據(jù)集,設(shè)置適配器 Fragment代碼(MyFragment1.java)
public class MyFragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.view1,null);
    }
}

Fragment中只覆寫了onCreateView方法,其他兩個Fragment差別只是載入了不同的view,這里就不再貼出。

適配器類代碼(MyFragmentViewPagerAdapter.java)

public class MyFragmentViewPagerAdapter extends FragmentPagerAdapter{
   private  List<Fragment> datas;
    private List<String> titles;
    public MyFragmentViewPagerAdapter(FragmentManager fm, List<Fragment> datas,List<String> titles) {
        super(fm);
        this.titles=titles;
        this.datas=datas;
    }
    @Override
    public Fragment getItem(int position) {
        return datas.get(position);
    }
    @Override
    public int getCount() {
        return datas.size();
    }
    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }
}

這里構(gòu)建構(gòu)造方法時傳入了FragmentManager參數(shù),并傳入了Fragment類的數(shù)據(jù)集合String型的標(biāo)題集。必須要覆寫的方法只有g(shù)etI tem(獲取子項)和getCount(獲取子項個數(shù))兩個。為了顯示標(biāo)題欄,這里覆寫了getPageTitle方法。

MainActivity(MainActivity.java)

public class MainActivity extends FragmentActivity {
    private ViewPager viewPager;
    private PagerTabStrip pagerTabStrip;
    private List<Fragment> datas;//數(shù)據(jù)源
    private List<String> titles;
    private MyFragmentViewPagerAdapter myFragmentViewPagerAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager=(ViewPager)findViewById(R.id.viewPager);
        pagerTabStrip=(PagerTabStrip)findViewById(R.id.pagerTabStrip);
        pagerTabStrip.setDrawFullUnderline(false);//取消標(biāo)題欄和子View直接的分割線
        pagerTabStrip.setTabIndicatorColor(Color.WHITE);
        pagerTabStrip.setTextColor(Color.WHITE);
        pagerTabStrip.setBackgroundResource(android.R.drawable.alert_dark_frame);
        initDatas();
        myFragmentViewPagerAdapter=new MyFragmentViewPagerAdapter(getSupportFragmentManager(),datas,titles);
        viewPager.setAdapter(myFragmentViewPagerAdapter);
    }
    private void initDatas() {
        datas=new ArrayList<>();
        titles=new ArrayList<>();
        datas.add(new MyFragment1());
        datas.add(new MyFragment2());
        datas.add(new MyFragment3());
        titles.add("第一頁");
        titles.add("第二頁");
        titles.add("第三頁");
    }
}

初始化數(shù)據(jù)源時,加入的是Fragment對象,初始化適配器類MyFragmentViewPagerAdapter時要傳入FragmentManager對象,這里使用getSupportFragmentManager方法獲取,不過,要注意這時MainActivity要繼承自FragmentActivity,才好調(diào)用這個方法。 運行實例如下:

http://wiki.jikexueyuan.com/project/twenty-four-Scriptures/images/17-7.png" alt="這里寫圖片描述" />

運行效果和PagerAdapter類實現(xiàn)的一樣。

  • FragmentStatePagerAdapter實現(xiàn) 同樣也是繼承自PagerAdapter,此適配器更適用于大量頁面的情形,因為不被顯示的頁面會被回收,可以大大降低內(nèi)存的使用率。在使用FragmentStatePagerAdapter作為適配器時,其余都不用改動,只要覆寫instantiateItem(初始化子頁面)和destroyItem(銷毀子頁面)兩個方法即可。如下:
@Override
public Object instantiateItem(ViewGroup container, int position) {
    return super.instantiateItem(container, position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
}

此方式可以銷毀不可見的頁面(不在標(biāo)題欄中的頁面,標(biāo)題欄中一般存在三個頁面),回收內(nèi)存,在實際開發(fā)中推薦使用。 ViewPager控件提供了頁面切換時的事件監(jiān)聽,下面就在MainActivity中實現(xiàn)一下:

public class MainActivity extends FragmentActivity implements ViewPager.OnPageChangeListener{
    private ViewPager viewPager;
    private PagerTabStrip pagerTabStrip;
    private List<Fragment> datas;
    private List<String> titles;
    private MyFragmentViewPagerAdapter myFragmentViewPagerAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager=(ViewPager)findViewById(R.id.viewPager);
        pagerTabStrip=(PagerTabStrip)findViewById(R.id.pagerTabStrip);
        pagerTabStrip.setDrawFullUnderline(false);//取消標(biāo)題欄和子View直接的分割線
        pagerTabStrip.setTabIndicatorColor(Color.WHITE);
        pagerTabStrip.setTextColor(Color.WHITE);
        pagerTabStrip.setBackgroundResource(android.R.drawable.alert_dark_frame);
        initDatas();
        myFragmentViewPagerAdapter=new MyFragmentViewPagerAdapter(getSupportFragmentManager(),datas,titles);
        viewPager.setAdapter(myFragmentViewPagerAdapter);
        viewPager.setOnPageChangeListener(this);
    }
    private void initDatas() {
        datas=new ArrayList<>();
        titles=new ArrayList<>();
        datas.add(new MyFragment1());
        datas.add(new MyFragment2());
        datas.add(new MyFragment3());
        titles.add("第一頁");
        titles.add("第二頁");
        titles.add("第三頁");
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        Toast.makeText(MainActivity.this,"當(dāng)前是第:"+(position+1)+"頁",Toast.LENGTH_SHORT).show();

    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
}

實現(xiàn)了onPageChangeListener接口,需要覆寫三個方法:

  • void onPageScrolled(int position, float positionOffset, int positionOffsetPixels):頁面滾動時觸發(fā);
  • void onPageSelected(int position):頁面選擇時觸發(fā);
  • void onPageScrollStateChanged(int state):頁面滾動狀態(tài)切換時觸發(fā);

在頁面選擇觸發(fā)的方法里通過position參數(shù)獲得當(dāng)前頁面信息,然后由Toast輸出信息。

運行實例如下:

http://wiki.jikexueyuan.com/project/twenty-four-Scriptures/images/17-8.png" alt="這里寫圖片描述" />

切換頁面后會Toast出當(dāng)前所在頁。