什么是 ViewPager?图告诉你一切:
如图,白色矩形区域就是我们的 ViewPager,正如我们所熟悉的,ViewPager 通常会配合 tab 使用,什么是 tab?就是“网易新闻,网易体育,网易财经,网易女人”(page title) 这四个标题所在的那个区域,每个 page title 代表着一个 tab 。这个区域再上面一点就是 ToolBar 了。 tab 的实现方法有很多种,但现在最受欢迎并且最美观的就是谷歌MD提供的 TabLayout 了。现在我们主要讲下 TabLayout + ViewPager 的实现和使用吧。
 
布局文件 :
1 2 3 4 5 6 7 8 9 10 11 12 13 <android.support.design.widget.TabLayout          android:id ="@+id/tabs"          android:layout_width ="match_parent"          android:layout_height ="wrap_content"          app:tabIndicatorColor ="#ffffff"           app:tabSelectedTextColor ="#ffffff"          app:tabTextColor ="#aaffffff"          app:tabBackground ="@drawable/tab_ripple" /> <android.support.v4.view.ViewPager        android:id ="@+id/viewpager"        android:layout_width ="match_parent"        android:layout_height ="match_parent" /> 
 
这就是 TabLayout + ViewPager 的布局文件 TabLayout 在上,ViewPager 在下。
java代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ViewPager  pager  =  null ;TabLayout  tabs  =  null ;ArrayList<View> viewContainer = new  ArrayList <>(); ArrayList<String> titleContainer = new  ArrayList <>();  @Override  protected  void  onCreate (Bundle savedInstanceState)  {    super .onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     initView(); } public  void  initView ()  {        pager = (ViewPager) findViewById(R.id.viewpager);     tabs = (TabLayout) findViewById(R.id.tabs); } 
 
在代码中找到这两个控件之后,我们就要给他们添加数据了。总不能两个控件都是空内容吧。对于 TabLayout 来说,我们要给它添加的数据当然就是page title了;而对于 ViewPager 来说, ViewPager 是一个 ViewGroup 类,我们需要给他添加的数据就是添加四个子 View,这四个子View对应的就是我们的四个页面。怎么添加呢?继续看。
对于 TabLayout,我们可以将数据源放在一个 ArrayList 中:
1 2 3 4 5 6 ArrayList<String> titleContainer = new  ArrayList <>(); titleContainer.add("网易新闻" ); titleContainer.add("网易体育" ); titleContainer.add("网易财经" ); titleContainer.add("网易女人" ); 
 
对于 ViewPager,我们同样将数据源放入一个 ArrayList 中:
1 2 3 4 5 6 7 8 9 10 11 12 ArrayList<View> viewContainer = new  ArrayList <>(); TextView view1,view2,view3,view4; (view1 = new  TextView (this )).setText("页面一" ); (view2 = new  TextView (this )).setText("页面二" ); (view3 = new  TextView (this )).setText("页面三" ); (view4 = new  TextView (this )).setText("页面四" ); viewContainer.add(view1); viewCOntainer.add(view2); viewContainer.add(view3); viewContainer.add(view4); 
 
这里我们给这个 ViewPager 添加的四个子 View 是四个 TextView;
好了数据源都准备好了,那么我们应该怎样将数据送到相应的控件中呢?对于 TabLayout,方法有两种,我们先讲一种比较容易理解的:
1 2 3 for (String title : titleContainer) {   tabs.add(tabs.newTab().setText(title));   } 
 
然后我们得将 viewContainer 中的那四个 view 添加进 ViewPager 中,当然,不是我们手动的 addView 去添加,而是像那些包含 itemView 的 AdapterView 那样去设置 Adapter。适配器模式有很多好处,首先能够将视图展示和数据绑定分离,其次能够很好的管理子 View,如动态的添加移除,还可以设置动画,并且能够实现视图回收和重用,如果手动添加这些是很难做到的。
安卓提供了一个 PagerAdapter 来给 ViewPager 设置适配器,我们的子 View 就是通过这个适配器添加进 ViewPager 的:
新建一个 PagerAdapter 通常要重写和实现的方法有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  int  getCount () ;public  boolean  isViewFromObject (View view, Object object) ; public  Object instantiateItem (ViewGroup container, int  position) ; public  void  destroyItem (ViewGroup container, int  position, Object object) ; public  CharSequence getPageTitle (int  position); 
 
一个典型的 PagerAdapter 可以这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 pager.setAdapter( new  PagerAdapter () {             @Override              public  int  getCount ()  {                 return   viewContainer.size();             }             @Override              public  boolean  isViewFromObject (View view, Object object)  {                 return   view == object;             }             @Override              public  Object instantiateItem (ViewGroup container, int  position)  {                 View view;                 container.addView(view = viewContainer.get(position));                 return  view;             }             @Override              public  void  destroyItem (ViewGroup container, int  position,  Object              object)  {                                                         container.removeView(viewContainer.get(position));             }                          @Override              public  CharSequence getPageTitle (int  position)  {                 return  titleContainer.get(position);             }        }); 
 
如果我们想要监听 ViewPager 页面滑动的事件,我们可以添加监听器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pager.addOnPageChangeListener(new  ViewPager .OnPageChangeListener() {        @Override          public  void  onPageScrolled (int  position, float  positionOffset, int  positionOffsetPixels)  {              Log.d("onPageSrolled:" , "我监听页面滚动事件" );         }         @Override          public  void  onPageSelected (int  position)  {               Log.d("onPageSelected: " , "我监听页面选择事件" );         }         @Override          public  void  onPageScrollStateChanged (int  state)  {              Log.d("onPageSrollStateChanged: " , "我监听页面滚动状态改变的事件" );         }     }); 
 
现在你肯定会问怎么将 ViewPager 和 TabLayout 关联起来呢?代码中两者之间没有任何关联,但我们通常看到的是指示条会跟着 ViewPager 页面的切换滚动到相应的 page title 下,而且当前页面对应的 page title 会更“亮”。对,这也就是我当时碰到的一个坑, 少写了这一步:
1 pager.addOnPageChangeListener(new    TabLayout .TabLayoutOnPagerChangeListener(tabs)); 
 
有了这一步,tabs 就能够监听 ViewPager 切换的事件了,从而能够做出相应改变。
前面讲了,给 TabLayout 设置 page title 的方法有两种,其中一种是手动添加 ,那么现在就讲另一种方法:
这种方法是把 TabLayout 设置 page title 的的任务交给 PagerAdapter 来做,最后将 TabLayout 和 ViewPager 绑定,这样 TabLayout 就和 PagerAdapter 产生某种关联,从而 TabLayout 能够获取标题信息。具体的做法是这样的:
首先重写 PagerAdapter 中的 getPageTitle() 方法,因为我们没有手动添加 page title,而是交给 PagerAdapter 来处理,所以我们必须重写这个方法来获取 page title:
1 2 3 4 @Override public  CharSequence getPageTitle (int  position)  {    return  titleContainer.get(position); } 
 
之后,我们就要将 TabLayout 和 ViewPager 绑定:
1 tabs.setupWithViewPager(pager); 
 
这样也能达到我们预期的效果了,而且我们连这一步:
1 pager.addOnPageChangeListener(new    TabLayout .TabLayoutOnPagerChangeListener(tabs)); 
 
也不用写了,但是有一点要记住这个方法一定要是在 ViewPager  设置 PagerAdapter 之后调用,否则程序运行时会崩溃。
可能出现的问题 如果你的手机是安卓5.0以上,你的页面可能会出现下图这种情况:
在 ToolBar 和 TabLayout 之间出现了一条阴影,这是因为 Toolbar 在安卓5.0以上默认 elevation 是不为零的,而 TabLayout 的高度为零,所以在 TabLayout 上造成了阴影,解决办法是将 TabLayout 放进 AppBarLayout 这个容器里,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <android.support.design.widget.AppBarLayout         android:layout_width="match_parent"          android:layout_height="wrap_content"          android:theme="@style/AppTheme.AppBarOverlay" >         <android.support.v7.widget.Toolbar             android:id="@+id/toolbar"              android:layout_width="match_parent"              android:layout_height="?attr/actionBarSize"              android:background="?attr/colorPrimary"              app:popupTheme="@style/AppTheme.PopupOverlay"  />         <android.support.design.widget.TabLayout             android:id="@+id/tabs"              android:layout_width="match_parent"              android:layout_height="wrap_content"              app:tabIndicatorColor="#ffffff"              app:tabSelectedTextColor="#ffffff"              app:tabTextColor="#aaffffff"              app:tabBackground="@drawable/tab_ripple" /> </android.support.design.widget.AppBarLayout> 
 
这样就不会出现那条难看的阴影了。