## 一、簡介
**1.特點:**可以左右滑動的控件,需要PagerAdapter配合使用,由v4包提供
類全名: android.support.v4.view.ViewPager
**2.作用**:ViewPager作用主要是能使界面左右滑動。比如最常用的使用是做一個引導界面;多張圖片的預覽或自動變換的圖片展示(如淘寶首頁面上的廣告);viewpager還可以結合fragment作為主界面框架(如微信主界面)。。。
**3.viewpager中的幾個重要的方法**
1)setAdapter()設置ViewPager的適配器。
2)setOnPageChangeListener()設置頁面改變事件監聽器
3)setCurrentItem(int position) 顯示第幾頁
4)setCurrentItem(int position,boolean smoothScroll) 顯示第幾頁,是否執行滾動動畫
5)setOffscreenPageLimit(int limit) 設置脫離屏幕的頁面限制--最多同時加載的頁面數,limit默認是1
-?這個方法解釋一下:默認情況下,viewpager在顯示頁面時,會加載當前顯示頁和它左右的頁面,這也是為什么移動頁面時可以顯示下一頁面一部分。如果想設置其他數,viewpager會加載對應值的頁面,比如設置2,則當前顯示頁和它左兩頁和右兩頁都會加載。
## 二、相關類介紹
**1.viewpager相關的幾個重要的類**
**1.1OnPageChangeListener:**
–onPageScrollStateChanged(int state) 頁面滾動發生或停止時
–onPageScrolled(int position, float offset, int offsetPixes) 滾動時
–onPageSelected(int position) 頁面位置確定時
**1.2 PagerAdapter**
–作用:主要配合ViewPager顯示相關的View
–用法:
1)創建類,并繼承PagerAdatper
2) 必須實現的方法
–getCount() 獲取View的數量
–instantiateItem(ViewGroup, int poistion) 實例化指定位置的View對象
–destroyItem(ViewGroup, int poistion, Object) 刪除指定位置的View
–isViewFromObject(View, Object) 判斷當前的View是否為Object
**1.3FragmentPagerAdaper**
–作用:與PagerAdapter的功能相同,不過顯示的View改為Fragment
–FragmentStatePagerAdapter: 如果需要處理有很多頁,并且數據動態性較大、占用內存較多的情況,應該使用FragmentStatePagerAdapter 保存當前界面,以及下一個界面和上一個界面(如果有),最多保存3個,其他會被銷毀掉。
–FragmentPagerAdaper和FragmentStatePagerAdapter的區別:
FragmentPagerAdaper銷毀的只是視圖,數據沒有銷毀,FragmentStatePagerAdapter則是全部銷毀。拿fragment生命周期來說,使用FragmentPagerAdaper,當滑動頁面時,被銷毀的fragment會執行到onDestyoyView()但沒有執行onDestroy()。而使用FragmentStatePagerAdapter時,會執行onDestroy()方法。后面實例會看到。
## 三、使用
**1.使用步驟:**
1) 在布局文件中使用標簽
~~~
<android.support.v4.view.ViewPager/>
~~~
2) 代碼中增加顯示的頁面
3) 在Activity里實例化ViewPager組件,并設置它的Adapter
需要注意的是:根據不同的場景,選擇不同的適配器父類,不結合fragment使用時使用繼承PagerAdapter的適配器,結合Fragment使用時,根據需求,可以選擇父類為FragmentPagerAdapter和FragmentStatePagerAdapter,這兩個的區別在后面前面已經講過了。
**2.案例一:用viewpager做一個歡迎界面**
先看效果圖:

**1) 布局文件**
~~~
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<!-- 其顯示的頁面需要通過PagerAdapter適配器增加或移除 -->
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- 導航布局 ,小點點 -->
<LinearLayout
android:id="@+id/ll_dots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="10dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp" >
</LinearLayout>
</FrameLayout>
~~~
**2) 然后看看幾個主要的方法體:**
**第一個是自定義的適配器**,各種方法都有注釋。
~~~
// 聲明PageAdapter子類,用于管理viewpager中顯示的控件
class WelComePageAdapter extends PagerAdapter {
@Override
public int getCount() {
// TODO 返回頁面的數量
return views.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
// TODO 獲取指定位置的View(UI),并增加到ViewPager中,同時作為當前頁面的數據返回
Log.i("--", "instantiateItem--" + position);
View view = views.get(position);
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// TODO 當前位置與VIewPager中顯示頁面的位置的間隔超出一頁面,需要將當前位置的頁面移除
Log.i("--", "destroyItem--" + position);
container.removeView(views.get(position));
}
@Override
public boolean isViewFromObject(View view, Object obj) {
// TODO 判斷當前顯示的頁面的UI和數據對象是否一致
return view == obj;
}
}
~~~
**第二個是viewpager滑動監聽事件**,這里自定義一個類看著清楚點,繼承OnPageChangeListener,然后事項相應的方法,特別注意onPageScrolled和onPageSelected這兩個方法,onPageScrolled在滑動時會一直回調,根據回調的三個參數,在這里可以進行相應的操作,比如微信那個,當你滑動時,相鄰兩個標簽會有顏色的漸變,就在這里處理的。onPageSelected方法在滑動一個頁面停止后回調。
~~~
class WelcomPageChangeListner implements OnPageChangeListener {
@Override
public void onPageScrollStateChanged(int state) {
Log.i("--", "onPageScrollStateChanged" + state);
// TODO 頁面滾動狀態發生變化事件:開始滾動、停止滾動、正在設置頁面
// ViewPager.SCROLL_STATE_DRAGGING 開始滾動
// ViewPager.SCROLL_STATE_IDLE 停止滾動
// ViewPager.SCROLL_STATE_SETTLING 正在設置頁面,即將要停止,并且設置當前顯示的頁面
// setDot(position);
// switch (state) {
// case ViewPager.SCROLL_STATE_DRAGGING:
// Log.i("--", "-SCROLL_STATE_DRAGGING-");
// break;
// case ViewPager.SCROLL_STATE_IDLE:
// Log.i("--", "-SCROLL_STATE_IDLE-");
// break;
// case ViewPager.SCROLL_STATE_SETTLING:
// Log.i("--", "-SCROLL_STATE_SETTLING-");
// break;
// }
}
~~~
~~~
/**
* 第一個參數:滾動頁面開始的位置 <br>
* 第二個參數:兩個頁面之間滾動的偏移量,范圍:0-1<br>
* 第三個頁面:兩個頁面之間的滾動的像素偏移量<br>
*/
@Override
public void onPageScrolled(int position, float offset, int offsetPixwls) {
// TODO 從當前頁面位置開始滾動事件
// Log.i("--", "-onPageScrolled-" + position + ",[" + offset + "],"
// + "[" + offsetPixwls + "]");
}
@Override
public void onPageSelected(int position) {
// TODO 指定位置的頁面被選擇
setDot(position);
}
}
~~~
**第三個是動態改變導航欄小點點的背景圖。**
~~~
/**
* 選擇指定位置的導航圖片為選擇圖片,之前選擇的導航圖片重置為未選擇圖片
*
* @param position
*/
private void setDot(int position) {
ImageView imageView = null;
// 遍歷導航布局張所有的子控件,判斷子控件的位置是否未選擇位置,若是則設置為選擇圖片
for (int i = 0; i < LlDot.getChildCount(); i++) {
imageView = (ImageView) LlDot.getChildAt(i);// 獲取布局中指定位置的子控件
if (i == position) {
imageView.setImageResource(R.drawable.page_now);
} else {
imageView.setImageResource(R.drawable.page);
}
}
}
~~~
其他還有小點點的點擊事件,具體看源代碼。
**3.案例二:viewpager和view結合做一個主界面**
效果圖:

1)布局:最上面的導航欄是一個HorizonalScrollView,里面有一個LinearLayout(因為HorizonalScrollView里只能有一個子視圖),然后在LinearLayout定義你想要的導航模塊,TextView即可。
~~~
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<HorizontalScrollView
android:id="@+id/hScrollView"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginBottom="2dp"
android:scrollbars="none" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- 頂部模塊的布局 -->
<LinearLayout
android:id="@+id/navLayout"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_model1"
android:layout_width="150dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="第一"
android:textColor="#000"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_model2"
android:layout_width="150dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="第二"
android:textColor="#000"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_model3"
android:layout_width="150dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="第三"
android:textColor="#000"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_model4"
android:layout_width="150dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="第四"
android:textColor="#000"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_model5"
android:layout_width="150dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="第五"
android:textColor="#000"
android:textSize="20sp" />
</LinearLayout>
<!-- 指示器控件 -->
<View
android:id="@+id/view_nav"
android:layout_width="150dp"
android:layout_height="4dp"
android:background="#0cf" />
<!-- 基準線 -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#0cf" />
</LinearLayout>
</HorizontalScrollView>
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_below="@id/hScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
~~~
**還需要幾個fragment頁,這里只展示一個,其他類似:**
~~~
<?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:orientation="vertical"
android:gravity="center"
android:background="#80f0">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="第一個頁面"
android:textSize="30sp"
/>
</RelativeLayout>
~~~
**2)代碼中需要做的事情有:**
2).1 指示器的移動,也就是模塊下那個粗線條的移動,viewpager本身就是可滑動的控件,這里需要指示器隨著手指的滑動進行滑動
2).2選擇導航模塊的位置,將水平滾動到當前模塊位置的中心點,
2).3模塊點擊事件,點擊模塊,顯示相應的page
那么來一步步實現上面的工作:
2.1指示器的移動
要求是指示器隨著手指的滑動,起始也就是動態的改變指示器的leftMargin
~~~
/**
* 指示器移動
* @param position
* @param offset
*/
private void navIndicateMove(int position, float offset){
int leftMargin = (int) (indicateParams.width * (position + offset));
indicateParams.leftMargin = leftMargin;
navIndicate.setLayoutParams(indicateParams);
}
~~~
2.2選擇導航模塊的位置,將水平滾動到當前模塊位置的中心點
因為整個導航模塊都在一個HorizonalScrollView中,HorizonalScrollView中有兩個方法可以指定的移動位置,一個是**hScrollView.scrollTo(x, y);//非平滑移動**
**hScrollView.smoothScrollTo(x, y);//平滑移動**
所以根據選擇的模塊得到移動值即可。
~~~
/**
* 選擇導航模塊的位置,將水平滾動到當前模塊位置的中心點
*
* @param position
*/
private void selectNav(int position) {
TextView modelTv = (TextView) navLayout.getChildAt(position);
int left = modelTv.getLeft();// 獲取當前控件的左邊位置
// 怎么放到中間?
// int scWidth = (getResources().getDisplayMetrics().widthPixels / 2);
int offset = left
- (getResources().getDisplayMetrics().widthPixels / 2)
+ modelTv.getWidth() / 2;
hScrollView.smoothScrollTo(offset, 0);// 水平滾動到指定位置
//設置被選中的模塊
for(int i = 0; i < navLayout.getChildCount(); i++){
modelTv = (TextView) navLayout.getChildAt(i);
if(i == position){
modelTv.setTextColor(Color.argb(100, 255, 0, 0));
}else{
modelTv.setTextColor(Color.argb(255, 0, 0, 0));
}
}
}
~~~
**3.模塊點擊事件,點擊模塊,顯示相應的page**
給每個模塊添加點擊事件,然后點擊一個模塊,只用調用viewPager的
viewPager.setCurrentItem(item);//非平滑滑動
viewPager.setCurrentItem(item, smoothScroll);//平滑滑動
~~~
/**
* 模塊點擊事件
*/
private void modelTvClickEvent() {
for(int i = 0; i < navLayout.getChildCount(); i++){
final TextView tv = (TextView) navLayout.getChildAt(i);
tv.setTag(i);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int position = (Integer)tv.getTag();
// selectNav(position);
// navIndicateMove(position, 0);
viewPager.setCurrentItem(position, true);
}
});
}
}
~~~
**這樣,viewpager結合導航欄都可以同步滑動了。**
**4.案例三:viewpager和fragment結合做一個主界面**
效果圖:

結合fragment使用和結合view使用很相似,只是繼承的適配器不同。
上面繼承PagerAdapter,這里繼承FragmentPagerAdapter或者FragmentStatePagerAdapter。
1)定義一個ListFragment
2)自定義適配器
3)初始化數據源
ListFragment的使用前面有一篇講過,這里就不附代碼了。
來看看自定義適配器:很簡單就完事
~~~
class InfoFragmentAdapter extends FragmentStatePagerAdapter{
public InfoFragmentAdapter(FragmentManager fm){
super(fm);
}
@Override
public int getCount() {
return fragments.size();
}
@Override
public Fragment getItem(int position) {
//返回指定位置的碎片
return fragments.get(position);
}
}
~~~
**最后可以借這個案例看一看FragmentPagerAdapter和FragmentStatePagerAdapter區別:**
ViewPager:默認創建自己和左右兩頁,當某個頁面與顯示的頁面間放大點隔大于1,則銷毀UI界面(注意,只是UI,里面的數據則不會銷毀)
FragmentPagerAdapter:管理fragment時銷毀的是UI界面,(fragment生命周期只會執行到onDestroyView)
FragmentStatePagerAdapter:管理fragment時,完全銷毀(fragment生命周期會執行onDestroy)
只用把自定義適配器類的父類改一改,然后看fragment各生命周期 打印的日志即可看到區別,這里不做演示了。
現在,結合案例二和案例三就可以做一個現在很流行的主界面了。
[ 源碼下載](http://download.csdn.net/detail/u011102153/9120819)