Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開源框架ViewPageIndicator和ViewPager實現Tab導航

Android開源框架ViewPageIndicator和ViewPager實現Tab導航

編輯:關於Android編程

前言:

關於使用ViewPageIndicator和ViewPager實現Tab導航,在開發社區裡已經有一堆的博客對其進行了介紹,如果我還在這裡寫如何去實現,那簡直就是老生常談,毫無新鮮感,而且,我也不認為自己對ViewPageIndicator的理解會比別人好,畢竟我也是看著大神的帖子,在學習實踐著。

那我還寫這個有啥意義呢?其實麼,就是想在這裡記錄下,在使用ViewPageIndicator和ViewPager實現Tab導航時,大家有可能會遇到的坑。這個坑,需要我們開發時盡量去避免的。

啥?你問我為啥子知道有這些坑?

因為我剛剛遇到過,剛剛解決了,所以覺得分享經驗啦!

 

一、推薦博客

在介紹具體實現的代碼之前,我先介紹兩篇博客呗,畢竟我就是根據他們寫的來學習的。第一篇《Android 開源框架ViewPageIndicator 和 ViewPager 仿網易新聞客戶端Tab標簽》這篇博客對如何實現tab已經介紹的非常詳細了,包括如何去自定義tab樣式,請學習膜拜吧。。。如果你學習能力較差,那好吧,在慕課網航有個教學視頻,這可是鴻翔大神錄制的哦,4-1 ViewPagerIndicator與ViewPager實現Tab,如果你看了視頻還不會,那我,呵呵呵。。。

 

二、如何在Android Studio中導入ViewPageIndicator

看過之前我推薦博客和視頻的人會問,在Eclipse中,我們直接引入ViewPageIndicator項目,再把就好了android-support-v4.jar 包進行下處理就好了。 那,如何在Android Studio中導入ViewPageIndicator項目呢?

好,我來告訴大家,請看下圖:

\

看到了吧,我們僅需要在此處搜索com.inkapplications.viewpageindicator,選中,點擊OK,gradle會自動幫我把改工程加載進來的。

讓我們看下子導入後,源碼在哪了:

\

當你需要修改資源文件的時候,可以直接在res目錄下的資源文件中進行修改,是不是很方便!!!

此刻有一點要記住,因為你改項目中已經引入了android-support-v4,所以你無需再引入其它的v4包,切記切記!!!

 

三、實現Tab導航

3.1 主頁面布局

 



    

    

 

3.2 定義ViewPager要顯示的Fragment

 

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.lidroid.xutils.ViewUtils;

public class FragmentStudy extends Fragment {

    private View view = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        if(view==null){
            view = inflater.inflate(R.layout.fragment_study, container, false);
        }

        ViewUtils.inject(this, view);

        return view;
    }
}
fragment_study.xml文件:

 

 




    

 

 

3.3 定義ViewPager的Adapter

 

public class ProjectPagerAdapter extends FragmentPagerAdapter {

    private static String[] titles = {全部, 計劃中, 進行中, 已完成};

    public ProjectPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;
        Bundle bundle = null;
        switch (position) {
            case 0:
                fragment = new FragmentStudy();
                bundle = new Bundle();
                bundle.putInt(pos, 0);
                fragment.setArguments(bundle);
                break;
            case 1:
                fragment = new FragmentStudy();
                bundle = new Bundle();
                bundle.putInt(pos, 1);
                fragment.setArguments(bundle);
                break;
            case 2:
                fragment = new FragmentStudy();
                bundle = new Bundle();
                bundle.putInt(pos, 2);
                fragment.setArguments(bundle);
                break;
            case 3:
                fragment = new FragmentStudy();
                bundle = new Bundle();
                bundle.putInt(pos, 3);
                fragment.setArguments(bundle);
                break;
        }
        return fragment;
    }

    @Override
    public int getCount() {
        return titles.length;
    }
    @Override
    public CharSequence getPageTitle(int position) {
        return titles[position];
    }
}

 

 

3.4 設置ViewPageIndicator和ViewPager

 

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;

import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.viewpagerindicator.TabPageIndicator;


public class MainActivity extends FragmentActivity {


    @ViewInject(R.id.tab_page_indicator)
    private TabPageIndicator tabPageIndicator;

    @ViewInject(R.id.study_viewpager)
    private ViewPager studyViewpager;

    private ProjectPagerAdapter mAdatpter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewUtils.inject(this);

        mAdatpter = new ProjectPagerAdapter(getSupportFragmentManager());// 此處,如果不是繼承的FragmentActivity,而是繼承的Fragment,則參數應該傳入getChildFragmentManager()

        studyViewpager.setAdapter(mAdatpter);

        tabPageIndicator.setViewPager(studyViewpager);
    }
}

 

 

這樣,一個基本的頂部導航Tab就出來了,接下來,我們還可以定義tab的樣式,在style.xml文件中,我們添加:

 


樣式定義好之後,只需要為Activity配置該theme即可:

 

 

 
            
                

                
            
好了,快來看下效果:
\

怎麼樣?開心了吧,你想要的tab終於出來了,你滿足了麼?

 

請千萬不要滿足,因為這個Tab還不穩定,它還有漏洞,下面我們來看下。

 

四、Tab導航中的坑和解決方案

4.1 Fragment二次加載onCreateView方法時會報異常:java.lang.IllegalStateException

對於之前實現的tab導航,當我們來回切換tab時,會發現,系統會崩潰:

\

錯誤意思是,當切換tab回到已經加載過的Fragment時,系統不允許之前的Fragment在還未移除的情況下,再次加載該View,一個View只能有一個父控件。請仔細看我們FragmentStudy的代碼:

 

if(view==null){
    view = inflater.inflate(R.layout.fragment_study, container, false);
}

我們為了保證只有一個實例,才會只對其進行是否為null的判斷,只進行了一次初始化。

 

那該如何解決呢?

有人說好辦啊,把判斷view==null直接去掉不就好了。。。額,確實好了,但是你有沒有考慮每次都會創建新的view,浪費資源性能呢?

那如何是好呢?別急,有辦法,讓我們修改下代碼:

 

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.lidroid.xutils.ViewUtils;

public class FragmentStudy extends Fragment {

    private View view = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

//        pos = getArguments().getInt(pos);
//        System.out.println(pos);

        LayoutInflater inflater = getActivity().getLayoutInflater();
        view = inflater.inflate(R.layout.fragment_study, (ViewGroup) getActivity().findViewById(R.id.study_viewpager), false);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

//        if(view==null){
//            view = inflater.inflate(R.layout.fragment_study, container, false);
//        }
        // 這句話必須加
        ViewGroup p = (ViewGroup) view.getParent();
        if (p != null) {
            p.removeAllViewsInLayout();
        }

        ViewUtils.inject(this, view);

        return view;
    }
}
有沒有發現啥? 沒錯,我們把加載布局文件創建View的過程,放在了onCreate()方法中,即只在第一次創建該Fragment時加載布局,之後切換時,都不會再加載布局了。

 

美哉啊!!!

在運行下該項目,發現是不是不會再崩潰了?!

 

4.2 ViewPager預加載Fragment造成的重復請求問題

不知道大家知不知道,ViewPager有個功能,也被成為ViewPager的一個好處,就是它可以預加載Fragment。可是如果我在每個Fragment中都有網絡請求,豈不是加載一個Fragment發送了多個請求?這種體驗可是不好的。有沒有辦法改呢?

有人說,那不簡單啊,設置ViewPager不去預加載不就好了!

對,就這麼簡單,ViewPager確實提供了相應的方法,去設置預加載Fragment的數量:

通過ViewPager的setOffscreenPageLimit(int pagenum)來設置,默認情況下參數是1,比如viewPager.setOffscreenPageLimit(2)會預加載2個頁面,viewPager.setOffscreenPageLimit(0)會不預加載頁面。

可是,尼瑪,操蛋的事來了,我嘗試了,viewPager.setOffscreenPageLimit(0)這個方法根本沒有用啊,真讓我抓狂。

該咋辦?

google了一下,還真讓我找到了方法,重寫Fragment的setUserVisibleHint方法:

 

// 使用fragment+viewpage時會發現設置setOffscreenPageLimit(0)不預加載頁面不管用,
// 可以用下邊的方法代替,下邊的方法是在子頁面(也就是fragment中)復寫下邊的方法,根據fragment是否可見來判斷是否是當前頁面,然後執行網絡加載數據
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
            //fragment可見時執行加載數據或者進度條等
            qryDataFromServer();
        } else {
            //不可見時不執行操作
        }
    }

    private void qryDataFromServer() {
        System.out.println(第+pos + 個Fragment is qryDataFromServer);
    }

來,我們運行下項目,切換Tab,看下輸出:

 

 

I/System.out﹕ 第0個Fragment is qryDataFromServer
I/System.out﹕ 第1個Fragment is qryDataFromServer
I/System.out﹕ 第2個Fragment is qryDataFromServer
I/System.out﹕ 第3個Fragment is qryDataFromServer
I/System.out﹕ 第2個Fragment is qryDataFromServer
I/System.out﹕ 第1個Fragment is qryDataFromServer
I/System.out﹕ 第0個Fragment is qryDataFromServer

這就對了,切換到哪個Fragment,才去進行網絡請求,而不是一次預加載多個網絡請求。

 

 

總結:

相信看到這裡,大家已經能夠掌握如何使用TabPageIndicator和ViewPager開發一個比較完美的頂部導航欄了,其實,TabPageIndicator使用並不難,最重要的還是去避免上文中提到的幾個坑,這樣才會有較快的開發效率!

 

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