ViewPager使用詳解1

一、簡介

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各生命週期
打印的日誌即可看到區別,這裏不做演示了。

現在,結合案例二和案例三就可以做一個現在很流行的主界面了。

源碼下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章