Android开源框架ViewPageIndicator和ViewPager实现Tab导航

少年恃险若平地,独倚长剑凌清秋。这篇文章主要讲述Android开源框架ViewPageIndicator和ViewPager实现Tab导航相关的知识,希望能为你提供帮助。
前言:    关于使用ViewPageIndicator和ViewPager实现Tab导航,在开发社区里已经有一堆的博客对其进行了介绍,假设我还在这里写怎样去实现。那简直就是老生常谈,毫无新奇感,并且。我也不觉得自己对ViewPageIndicator的理解会比别人好,毕竟我也是看着大神的帖子。在学习实践着。
    那我还写这个有啥意义呢?事实上么,就是想在这里记录下。在使用ViewPageIndicator和ViewPager实现Tab导航时,大家有可能会遇到的坑。这个坑。须要我们开发时尽量去避免的。
    啥?你问我为啥子知道有这些坑?
    由于我刚刚遇到过,刚刚攻克了。所以来此分享经验啦!


一、推荐博客    在介绍具体实现的代码之前。我先介绍两篇博客呗,毕竟我就是依据他们写的来学习的。第一篇《Android 开源框架ViewPageIndicator 和 ViewPager 仿网易新闻clientTab标签》这篇博客对怎样实现tab已经介绍的很具体了,包含怎样去自己定义tab样式,请学习膜拜吧。。。假设你学习能力较差,那好吧,在慕课网航有个教学视频。这但是鸿翔大神录制的哦,4-1 ViewPagerIndicator与ViewPager实现Tab,假设你看了视频还不会,那我,呵呵呵。。。


二、怎样在android Studio中导入ViewPageIndicator    看过之前我推荐博客和视频的人会问,在Eclipse中,我们直接引入ViewPageIndicator项目。再把就好了android-support-v4.jar 包进行下处理就好了。 那,怎样在Android Studio中导入ViewPageIndicator项目呢?
    好,我来告诉大家。请看下图:

Android开源框架ViewPageIndicator和ViewPager实现Tab导航

文章图片


    看到了吧,我们仅须要在此处搜索com.inkapplications.viewpageindicator,选中。点击OK。gradle会自己主动帮我把改project载入进来的。
    让我们看下子导入后。源代码在哪了:
Android开源框架ViewPageIndicator和ViewPager实现Tab导航

文章图片


    当你须要改动资源文件的时候。能够直接在res文件夹下的资源文件里进行改动,是不是非常方便!!


    此刻有一点要记住。由于你改项目中已经引入了android-support-v4,所以你无需再引入其他的v4包,切记切记!。!


三、实现Tab导航 3.1 主页面布局
< LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> < com.viewpagerindicator.TabPageIndicator android:id="@+id/tab_page_indicator" android:layout_width="fill_parent" android:layout_height="60dp" /> < android.support.v4.view.ViewPager android:id="@+id/study_viewpager" android:layout_width="fill_parent" android:layout_height="fill_parent" /> < /LinearLayout>

 
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文件:

< ?
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" android:orientation="vertical"> < TextView android:id="@+id/text_name" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:text="无缘" android:textSize="25sp" /> < /LinearLayout>


 
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文件里。我们加入:
< !-- 去掉tab顶部的黑边 --> < style name="no_title" parent="@android:style/Theme.Light.NoTitleBar"> < item name="android:windowContentOverlay"> @null< /item> < /style> < style name="MyTheme" parent="no_title"> < item name="vpiTabPageIndicatorStyle"> @style/MyWidget.TabPageIndicator< /item> < item name="android:windowNoTitle"> true< /item> < item name="android:animationDuration"> 5000< /item> < item name="android:windowContentOverlay"> @null< /item> < /style> < style name="MyWidget.TabPageIndicator" parent="Widget"> < item name="android:gravity"> center< /item> < item name="android:background"> @drawable/vpi__tab_indicator< /item> < item name="android:paddingLeft"> 22dip< /item> < item name="android:paddingRight"> 22dip< /item> < item name="android:paddingTop"> 1dp< /item> < item name="android:paddingBottom"> 1dp< /item> < item name="android:textAppearance"> @style/MyTextAppearance.TabPageIndicator< /item> < item name="android:textSize"> 14sp< /item> < item name="android:maxLines"> 1< /item> < /style> < style name="MyTextAppearance.TabPageIndicator" parent="Widget"> < item name="android:textStyle"> bold< /item> < item name="android:textColor"> #2c3e50< /item> < /style>


样式定义好之后。仅仅须要为Activity配置该theme就可以:

< activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@style/MyTheme"> < intent-filter> < action android:name="android.intent.action.MAIN" /> < category android:name="android.intent.category.LAUNCHER" /> < /intent-filter> < /activity>

好了,快来看下效果:
Android开源框架ViewPageIndicator和ViewPager实现Tab导航

文章图片


              怎么样?开心了吧,你想要的tab最终出来了,你满足了么?
    请千万不要满足。由于这个Tab还不稳定,它还有漏洞,以下我们来看下。


四、Tab导航中的坑和解决方式 4.1 Fragment二次载入onCreateView方法时会报异常:java.lang.IllegalStateException    对于之前实现的tab导航,当我们来回切换tab时。会发现,系统会崩溃:
Android开源框架ViewPageIndicator和ViewPager实现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时载入布局。之后切换时,都不会再载入布局了。
    美哉啊!!!
【Android开源框架ViewPageIndicator和ViewPager实现Tab导航】    在执行下该项目,发现是不是不会再崩溃了?!


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使用并不难。最重要的还是去避免上文中提到的几个坑。这样才会有较快的开发效率。


源代码下载地址(免费):  http://download.csdn.net/detail/zuiwuyuan/9039533




















    推荐阅读