viewpager_viewpager2 怎么使用 - CSDN
  • 前言:这两天研究研究ViewPager滚动功能,现在很多的app都有用到这个功能,我们的大虾米也有这个模块。要研究就彻底的研究研究,我从不满足于一个功能只是简单的应用,要学就学的彻底,所以我打算将ViewPager 分几篇...

    前言:这两天研究研究ViewPager滚动功能,现在很多的app都有用到这个功能,我们的大虾米也有这个模块。要研究就彻底的研究研究,我从不满足于一个功能只是简单的应用,要学就学的彻底,所以我打算将ViewPager 分几篇写,研究的哪个程度就写到哪个程度吧。今天是第一篇,基本入门篇 。

     

     

    相关文章:

    1、《ViewPager 详解(一)---基本入门》

    2、《ViewPager 详解(二)---详解四大函数》

    3、《ViewPager 详解(三)---PagerTabStrip与PagerTitleStrip添加标题栏的异同》

    4、《ViewPager 详解(四)----自主实现滑动指示条》

    5、《ViewPager 详解(五)-----使用Fragment实现ViewPager滑动》

     

    首先让大家有个全局的认识,直接上个项目,看看仅仅通过这几行代码,竟然就能完成如此强悍的功能。下篇再结合API仔细讲讲为什么要这么写。

    效果图:

    实现了三个view间的相互滑动

                         第一个VIEW向第二个VIEW滑动       第二个VIEW向第三个VIEW滑动

       

    一、新建项目,引入ViewPager控件

    ViewPager。它是google SDk中自带的一个附加包的一个类,可以用来实现屏幕间的切换。

    1.在主布局文件里加入

     

     

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        tools:context="com.example.testviewpage_1.MainActivity" >
    
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />
    
    </RelativeLayout>

     

    其中 <android.support.v4.view.ViewPager /> 是ViewPager对应的组件,要将其放到想要滑动的位置

    2、新建三个layout,用于滑动切换的视图

    从效果图中也可以看到,我们的三个视图都非常简单,里面没有任何的控件,大家当然可以往里添加各种控件,但这里是个DEMO,只详解原理即可,所以我这里仅仅用背景来区别不用layout布局。

    布局代码分别如下:

    layout1.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:background="#ffffff"
        android:orientation="vertical" >
        
    
    </LinearLayout>

    layout2.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:background="#ffff00"
        android:orientation="vertical" >
        
    
    </LinearLayout>
    

    layout3.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:background="#ff00ff"
        android:orientation="vertical" >
        
    
    </LinearLayout>
    
    

     

    二、代码实战

    先上整体代码,然后逐步讲解。

    package com.example.testviewpage_1;
    /**
     * @author  harvic
     * @date 2014.8.9
     */
    import java.util.ArrayList;
    import java.util.List;
    import java.util.zip.Inflater;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.support.v4.view.PagerAdapter;
    import android.support.v4.view.ViewPager;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    
    public class MainActivity extends Activity {
    
    	private View view1, view2, view3;
    	private ViewPager viewPager;  //对应的viewPager
    	
    	private List<View> viewList;//view数组
       
       
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            viewPager = (ViewPager) findViewById(R.id.viewpager);
            LayoutInflater inflater=getLayoutInflater();
            view1 = inflater.inflate(R.layout.layout1, null);
            view2 = inflater.inflate(R.layout.layout2,null);
            view3 = inflater.inflate(R.layout.layout3, null);
            
            viewList = new ArrayList<View>();// 将要分页显示的View装入数组中
    		viewList.add(view1);
    		viewList.add(view2);
    		viewList.add(view3);
    		
    		
    		PagerAdapter pagerAdapter = new PagerAdapter() {
    			
    			@Override
    			public boolean isViewFromObject(View arg0, Object arg1) {
    				// TODO Auto-generated method stub
    				return arg0 == arg1;
    			}
    			
    			@Override
    			public int getCount() {
    				// TODO Auto-generated method stub
    				return viewList.size();
    			}
    			
    			@Override
    			public void destroyItem(ViewGroup container, int position,
    					Object object) {
    				// TODO Auto-generated method stub
    				container.removeView(viewList.get(position));
    			}
    			
    			@Override
    			public Object instantiateItem(ViewGroup container, int position) {
    				// TODO Auto-generated method stub
    				container.addView(viewList.get(position));
    				
    				
    				return viewList.get(position);
    			}
    		};
    		
    		
    		viewPager.setAdapter(pagerAdapter);
    		
        }
    
    
    }

    代码量很小,全部放在了OnCreate()函数中。

     

    1、先看声明的变量的意义:

     

     

    private View view1, view2, view3;
    private List<View> viewList;//view数组
    private ViewPager viewPager;  //对应的viewPager

     

    首先viewPager对应 <android.support.v4.view.ViewPager/>控件。

    view1,view2 ,view3对应我们的三个layout,即layout1.xml,layout2.xml,layout3.xml

    viewList是一个View数组,盛装上面的三个VIEW

    2、接下来是他们的初始化过程:

     

    viewPager = (ViewPager) findViewById(R.id.viewpager);
    LayoutInflater inflater=getLayoutInflater();
    view1 = inflater.inflate(R.layout.layout1, null);
    view2 = inflater.inflate(R.layout.layout2,null);
    view3 = inflater.inflate(R.layout.layout3, null);
    
    viewList = new ArrayList<View>();// 将要分页显示的View装入数组中
    viewList.add(view1);
    viewList.add(view2);
    viewList.add(view3);

    初始化过程难度不大,就是将资源与变量联系起来布局,最后将实例化的view1,view2,view3添加到viewList中

     

    3、PageAdapter——PageView的适配器

     

    适配器这个东东想必大家都不莫生,在ListView中也有适配器,listView通过重写GetView()函数来获取当前要加载的Item。而PageAdapter不太相同,毕竟PageAdapter是单个VIew的合集。

    PageAdapter 必须重写的四个函数:

     

    • boolean isViewFromObject(View arg0, Object arg1)
    • int getCount() 
    • void destroyItem(ViewGroup container, int position,Object object)
    • Object instantiateItem(ViewGroup container, int position)

     

    先看看各个函数,我们上面都做了什么吧:

     

    @Override
    public int getCount() {
    	// TODO Auto-generated method stub
    	return viewList.size();
    }

    getCount():返回要滑动的VIew的个数

     

     

    @Override
    public void destroyItem(ViewGroup container, int position,
    		Object object) {
    	// TODO Auto-generated method stub
    	container.removeView(viewList.get(position));
    }
    

    destroyItem():从当前container中删除指定位置(position)的View;

     

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
    	// TODO Auto-generated method stub
    		container.addView(viewList.get(position));
    		
    		
    		return viewList.get(position);
    	}
    };

    instantiateItem():做了两件事,第一:将当前视图添加到container中,第二:返回当前View

     

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
    	// TODO Auto-generated method stub
    	return arg0 == arg1;
    }

    isViewFromObject():对于这个函数就先不做讲解,大家目前先知道它要这样重写就行了,后面我们会对它进行改写。

     

    下一篇,我将仔细讲解这几个函数的意义,与有关Key的知识,当然最后也会有个例子给大家。这篇就到这了。

     

    源码地址:http://download.csdn.net/detail/harvic880925/7733249

     

    请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/38453725  万分感激!!!!!

    如果你喜欢我的文章,你可能更喜欢我的公众号

    启舰杂谈

     

    展开全文
  • ViewPager使用详解

    2017-12-22 23:33:52
    ViewPager使用详解这两天看了鸿洋大神的一篇关于ViewPager的推送,将其中的内容实现了下,过程中依旧踩坑不断,现在将填坑过程中的一些解决方法及理解记录一下。ViewPager+PageAdapter这是最基本的一种使用,要注意...

    这两天看了鸿洋大神的一篇关于ViewPager的推送,将其中的内容实现了下,过程中依旧踩坑不断,现在将填坑过程中的一些解决方法及理解记录一下。

    ViewPager+PageAdapter

    这是最基本的一种使用,要注意的是,PagaAdapter中有4个必须要重写的方法:
    - getCount()
    需要在viewpager中显示的对象数。
    - isViewFromObject(View view, Object object)
    直接return view==object;
    - instantiateItem(ViewGroup container, int position)
    要显示页面的初始化,其中return 语句中返回的object才是显示的内容。
    - destroyItem(ViewGroup container, int position, Object object)
    页面不是当前显示页面,也不是缓存页面,则回调该方法将其销毁。

    ViewPager+FragmentPagerAdapter+TabLayout

    这里写图片描述
    用FragmentPagerAdapter的话,通常重写两个方法:

    @Override
               public Fragment getItem(int position) {
                   return list.get(position);
               }
    
               @Override
               public int getCount() {
                   return list.size();
               }

    这里要注意的是,在new一个FragmentPagerAdapter时,需要传入一个FragmentManager,而这个FragmentManager是v4包中的,它的获取方式是

    fragmentManager=getSupportFragmentManager();

    而不是常用的 fragmentManager1=getFragmentManager();
    我们用fragment时因为它能实现更多的逻辑,降低耦合性,而用view的话,也可以进行某些实现,但是代码就会全部冗杂在一块儿,后面如果修改就会很麻烦。
    我们可以结合TabLayout实现页面滑动时上层标签的内容改变。
    1.重写adapter中的getPageTitle()方法,它的返回值即标签中的内容

    @Override
                public CharSequence getPageTitle(int position) {
                    switch (position){
                        case 0:return "first";
                        case 1:return "second";
                        case 2:return "third";
                        default:return "hello";
                    }
               }

    这里case后不需要break了,因为用了return离开了该方法,也不需要用switch语句后用return,因为有default语句,不管是否有符合的值,都会return。
    2.将ViewPager与TabLayout“绑定”

    tabLayout.setupWithViewPager(viewPager);

    当两者绑定后,其实是TabLayout通过ViewPager调用它的getPageTitle()方法来实现联动。
    这里有一个知识点,就是FragmentPagerAdapter与FragmentStatePagerAdapter的区别,鸿洋大神写得很清楚了,这里就直接引用了。

    FragmentPagerAdapter:对于不再需要的 fragment,选择调用 onDetach() 方法,仅销毁视图,并不会销毁 fragment 实例。
    FragmentStatePagerAdapter:会销毁不再需要的 fragment,当当前事务提交以后,会彻底的将 fragmeng 从当前 Activity 的FragmentManager 中移除,state 标明,销毁时,会将其 onSaveInstanceState(Bundle outState) 中的 bundle 信息保存下来,当用户切换回来,可以通过该 bundle 恢复生成新的 fragment,也就是说,你可以在 onSaveInstanceState(Bundle outState) 方法中保存一些数据,在 onCreate 中进行恢复创建。

    总结起来就是:当页面多的时候,就用FragmentStatePagerAdapter来省内存;页面少时,就用FragmentPagerAdapter来提升效率。

    ViewPager的轮播

    1.自动无限轮播
    即在一定时间间隔时更换页面,这里通过handler就可以实现了。

     Handler handler=new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what==0011){
                        viewPager.setCurrentItem((viewPager.getCurrentItem()+1)%list.size());
                        this.sendEmptyMessageDelayed(0011,1000);
                    }
                }
            };
            handler.sendEmptyMessageDelayed(0011,1000);

    2.手动实现轮播
    所谓的手动实现轮播,就是在最后一个页面往后滑动时可以回到第一个页面。而这也是与banner很大的一个区别,banner是可以直接实现的,而viewpager我们则需要写一些内容。
    先上代码:

    @Override
                public int getCount() {
                    return Integer.MAX_VALUE;
                }
    
                @Override
                public Object instantiateItem(ViewGroup container, int position) {
    
                    View view=list.get(position%list.size());
                    ViewGroup viewGroup=(ViewGroup)view.getParent();
                    if (viewGroup!=null) viewGroup.removeView(view);
                    container.addView(view);
                    return view;
                }
    
                @Override
                public void destroyItem(ViewGroup container, int position, Object object) {}

    因为我们要实现“从尾到头”的变化,而这个变化也不知道要多少次,所以我们就通过getCount()给了一个绝对大的值来实现。
    重点在instantiateItem()中,如果我们按照之前的写法,即

    @Override
                public Object instantiateItem(ViewGroup container, int position) {
                    View view=list.get(position);
                    container.addView(view);
                    return  view;
                }
    
                @Override
                public void destroyItem(ViewGroup container, int position, Object object) {
                    container.removeView(list.get(position));
                }

    我们会发现当往回滑动的时候,程序会崩掉,这是因为图片第一次加载了,当往回滑动,它再一次加载时,我们需要在它的父类中先将该object删除了,再重新加载新的对象。
    这里还有一个要注意的是,因为我们在instantiateItem()中已经对图片的移除做了处理了,所以就不需要重写destroyItem()方法了。如果我们按照上面的代码重写了该方法,会发现显示的内容为空。

    ViewPager中滑动页面后文字改变

    我们可以选择在instantiateItem()中根据当前的position来获得文字内容,但这样滑动时会显得比较生硬,所以可以选择将textview与ViewPager分离开,通过对ViewPager添加监听,来达到页面变化时文字内容改变。

    viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
    
                @Override
                public void onPageSelected(int position) {
                      textView.setText(titles[position%titles.length]);
                }
    
                @Override
                public void onPageScrollStateChanged(int state) {}
            });

    同屏显示多个页面

    这里的同屏显示多个页面,并不是ViewPager中显示多个,实际上它还是只显示了一个页面,但是我们通过它与其父布局及它与前后两个页面之间的margin来达到3个页面(部分页面)的同时出现。需要用到的几个方法:
    - clipChildren=”false”
    设定值为false后,才可以使得子view的裁剪不受影响
    - viewPager.setPageMargin( )
    设置每两个页面之间的margin
    - viewPager.setOffscreenPageLimit( )
    设置需要显示在屏幕上的页面的个数
    这里贴出xml的布局:

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false">
    
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpagers"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:layout_gravity="center"
        android:layout_marginLeft="60dp"
        android:layout_marginRight="60dp" />
    </LinearLayout>

    当我们实现多屏显示后,如果觉得单调,想让页面随着滑动的位置不同,改变图片的透明度,可以调用viewPager.setPageTransformer( )来增加特效。

    viewPager.setPageTransformer(false, new ViewPager.PageTransformer() {
                private static final float DEFAULT_MIN_ALPHA = 0.5f;
                private float mMinAlpha = DEFAULT_MIN_ALPHA;
                @Override
                public void transformPage(View page, float position) {
                    if (position < -1)
                    {
                        page.setAlpha(mMinAlpha);
                    } else if (position <= 1)
                    { // [-1,1]
    
                        if (position < 0) //[0,-1]
                        {
                            float factor = mMinAlpha + (1 - mMinAlpha) * (1 + position);
                            page.setAlpha(factor);
                        } else//[1,0]
                        {
                            float factor = mMinAlpha + (1 - mMinAlpha) * (1 - position);
                            page.setAlpha(factor);
                        }
                    } else
                    { // (1,+Infinity]
                        page.setAlpha(mMinAlpha);
                    }
                }
            });

    关于这个动画,还可以看下大神的另一篇文章
    巧用ViewPager 打造不一样的广告轮播切换效果

    展开全文
  • Android触摸事件传递机制及viewpager嵌套fragment冲突处理视频教程,本课程对安卓中的触摸事件分发机制理论进行讲解,并结合开发中常出现的触摸冲突做讲解。
  • 本课程讲解Android中的ViewPager,Fragment及Android触摸事件的处理,ViewPager和Fragment相互嵌套冲突处理
  • ViewPager详解

    2016-02-22 01:03:50
    ViewPager详解

    ViewPager详解

    • ViewPager中的主要方法详解
    • OnPageChangeListener中的三个方法详解
    • 三种适配器的使用及其主要方法详解

    ViewPager用于实现页面间的切换。

    ViewPager中的主要方法详解

    • setAdapter(PagerAdapter adapter)
      该方法为ViewPager设置适配器,ViewPager有三种适配器,它们分别有不同的特性,下面我会对这三种适配器进行讲解。
    • setCurrentItem(int item)
      该方法设置显示item位置的界面。
    • setOffscreenPageLimit(int limit)
      该方法用来设置当前显示页面左右两边缓存的页面数。
    • addOnPageChangeListener(OnPageChangeListener listener)
      该方法为ViewPager添加页面切换时的监听,关于界面监听的内容,接下来对OnPageChangeListener中的方法进行讲解时,再详细说明。
    • setOnScrollChangeListener(OnScrollChangeListener l)
      该方法为ViewPager增加滚动状态监听,但该方法需要minSdkVersion为23

    OnPageChangeListener中的三个方法详解

    • onPageScrollStateChanged(int state)
      该方法在手指操作屏幕的时候发生变化。有三个值:0(END),1(PRESS) ,2(UP) 。当用手指滑动翻页时,手指按下去的时候会触发这个方法,state值为1,手指抬起时,如果发生了滑动(即使很小),这个值会变为2,然后最后变为0 。总共执行这个方法三次。一种特殊情况是手指按下去以后一点滑动也没有发生,这个时候只会调用这个方法两次,state值分别是1,0 。当setCurrentItem翻页时,会执行这个方法两次,state值分别为2 ,0 。
    • onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
      该方法在滑动过程中将一直被调用,该方法的参数说明如下:
      position:当用手指滑动时,如果手指按在页面上不动,position和当前页面index是一致的;如果手指向左拖动(相应页面向右翻动),这时候position大部分时间和当前页面是一致的,只有翻页成功的情况下最后一次调用才会变为目标页面;如果手指向右拖动(相应页面向左翻动),这时候position大部分时间和目标页面是一致的,只有翻页不成功的情况下最后一次调用才会变为原页面。当直接设置setCurrentItem翻页时,如果是相邻的情况(比如现在是第二个页面,跳到第一或者第三个页面),如果页面向右翻动,大部分时间是和当前页面是一致的,只有最后才变成目标页面;如果向左翻动,position和目标页面是一致的。这和用手指拖动页面翻动是基本一致的。如果不是相邻的情况,比如我从第一个页面跳到第三个页面,position先是0,然后逐步变成1,然后逐步变成2;我从第三个页面跳到第一个页面,position先是1,然后逐步变成0,并没有出现为2的情况。
      positionOffset:当前页面滑动比例,如果页面向右翻动,这个值不断变大,最后在趋近1的情况后突变为0。如果页面向左翻动,这个值不断变小,最后变为0。
      positionOffsetPixels:当前页面滑动像素,变化情况和positionOffset一致
    • onPageSelected(int position)
      position是被选中页面的索引,该方法在页面被选中或页面滑动足够距离切换到该页手指抬起时调用。

    三个方法的执行顺序:用手指拖动翻页时,最先执行一遍onPageScrollStateChanged(1),然后不断执行onPageScrolled,放手指的时候,直接立即执行一次onPageScrollStateChanged(2),然后立即执行一次onPageSelected,然后再不断执行onPageScrollStateChanged,最后执行一次onPageScrollStateChanged(0)

    三种适配器的使用及其主要方法详解

    三种适配器继承关系

    这里写图片描述

    PagerAdapter
    主要方法详解
    • public abstract int getCount ()
      返回有效视图的数量。
    • public int getItemPosition (Object object)
      当宿主视图尝试判断一项的位置是否改变时调用。如果给定项的位置没有改变则返回POSITION_UNCHANGED,如果该项不再存在于适配器中则返回POSITION_NONE
      在ViewPager.dataSetChanged()中将对该函数的返回值进行判断,如果返回POSITION_NONE则调用destroyItem(ViewGroup container, int position, Object object)方法将该视图销毁,如果返回POSITION_UNCHANGED则不做任何改变,如果数据改变,则触发PagerAdapter.instantiateItem(ViewGroup container, int position)方法改变视图。
      PagerAdapter中该方法的默认返回值是 POSITION_UNCHANGED。如果没有重载该函数,而导致调用PagerAdapter.notifyDataSetChanged() 后,什么都没有发生。
    • public boolean isViewFromObject (View view, Object object)
      决定一个页面view是否与instantiateItem(ViewGroup, int)方法返回的具体key对象相关联。
      viewpager不直接处理每一个视图而是将各个视图与一个键联系起来。这个键用来跟踪且唯一代表一个页面,不仅如此,该键还独立于这个页面所在adapter的位置。当pageradapter将要改变的时候他会调用startUpdate函数,接下来会调用一次或多次的instantiateItem或者destroyItem。最后在更新的后期会调用finishUpdate。当finishUpdate返回时instantiateItem返回的对象应该添加到父ViewGroup,destroyItem返回的对象应该被ViewGroup删除。isViewFromObject(View, Object)代表了当前的页面是否与给定的键相关联。
      自定义Key示例-简单的将位置position最为key
    private class MyPagerAdapter extends PagerAdapter {
    
            private List<View> mViewList;
    
            MyPagerAdapter(List<View> viewList) {
                mViewList = viewList;
            }
    
            @Override
            public int getCount() {
                Log.i(TAG, NAME + "--getCount");
                return mViewList.size();
            }
    
            @Override
            public int getItemPosition(Object object) { 
                return super.getItemPosition(object);
            }
    
            @Override
            public boolean isViewFromObject(View view, Object object) {
                Log.i(TAG, NAME + "--isViewFromObject");
                return view == mViewList.get((int)Integer.parseInt(object.toString()));
            }
    
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                View view = mViewList.get(position);
                container.addView(view);
                Log.i(TAG, NAME + "--instantiateItem++container:" + container.getChildCount() + "++position:" + position);
                return position;
            }
    
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView(mViewList.get(position));
                Log.i(TAG, NAME + "--destroyItem++container:" + container.getChildCount() + "++position:" + position);
            }
    
        }
    • public Object instantiateItem (ViewGroup container, int position)
      创建指定位置的页面视图。适配器有责任增加即将创建的View视图到给定的container中,确保在finishUpdate(viewGroup)返回时,增加视图的事情已经完成。
      该方法的返回值是新增视图页面的Object(Key),这里没必要非要返回视图本身,也可以是这个页面的其它容器,它可以返回和视图相关联的任何值。
    • public void destroyItem (ViewGroup container, int position, Object object)
      移除给定位置的view,适配器有责任将该view从container中移除,确保在finishUpdate(viewGroup)返回时,移除视图的事情已经完成。
    • public void startUpdate (ViewGroup container)
      在展示的界面中有改变将要发生时调用。
    • public void finishUpdate (ViewGroup container)
      展示界面中的改变完成时调用。在这个时间点上,你必须确保所有的页面已被合适的从container中添加或移除。
    • public void notifyDataSetChanged ()
      该方法由应用程序在适配器数据改变时主动调用。
    • public void registerDataSetObserver (DataSetObserver observer)
      注册一个观察者去接收关联到适配器数据变化的回调。
    • public void unregisterDataSetObserver (DataSetObserver observer)
      反注册去接收关联到适配器数据变化的回调的观察者。
    • public void setPrimaryItem (ViewGroup container, int position, Object object)
      调用该方法去通知当前适配器的哪一项被考虑为“primary”,它是当前展示给用户的页面。
    • public CharSequence getPageTitle (int position)
      该方法由ViewPager在获取描述页面的标题时调用。该方法默认返回null。
    • public float getPageWidth (int position)
      该方法返回给定页面的比例宽度,范围(0.f-1.f]。
    • public Parcelable saveState ()
      保存与适配器关联的实例状态,当当前UI状态需要重建时恢复。
    • public void restoreState (Parcelable state, ClassLoader loader)
      恢复之前由saveState ()保存的与适配器关联的实例状态。
    PagerAdapter使用示例

    activity_pager_adapter.xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <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="com.example.sunxiaodong.viewpager.PagerAdapterActivity">
    
        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
        </android.support.v4.view.ViewPager>
    
        <TextView
            android:id="@+id/page_num"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:textColor="#ffffff"
            android:textSize="20sp" />
    
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true">
    
            <EditText
                android:id="@+id/edit_text"
                android:layout_width="100dp"
                android:layout_height="50dp"
                android:layout_gravity="center_vertical"
                android:textColor="#000000"
                android:textSize="20sp" />
    
            <Button
                android:id="@+id/button"
                android:layout_width="80dp"
                android:layout_height="40dp"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="10dp"
                android:text="跳转"
                android:textColor="#ffffff" />
        </LinearLayout>
    
    </RelativeLayout>

    page1.xml文件

    <?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:background="#ff0000">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="page1"
            android:textColor="#ffffff"
            android:textSize="20sp" />
    
    </RelativeLayout>

    page2.xml文件

    <?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:background="#ff00ff">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="page2"
            android:textColor="#ffffff"
            android:textSize="20sp" />
    
    </RelativeLayout>

    page3.xml文件

    <?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:background="#00ff00">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="page3"
            android:textColor="#ffffff"
            android:textSize="20sp" />
    
    </RelativeLayout>

    page4.xml文件

    <?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:background="#ffff00">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="page4"
            android:textColor="#ffffff"
            android:textSize="20sp" />
    
    </RelativeLayout>

    page5.xml文件

    <?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:background="#8e35ef">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="page5"
            android:textColor="#ffffff"
            android:textSize="20sp" />
    
    </RelativeLayout>

    PagerAdapterActivity.java文件

    public class PagerAdapterActivity extends AppCompatActivity implements View.OnClickListener {
    
        private static final String NAME = PagerAdapterActivity.class.getSimpleName();
        private static final String TAG = "sxd";
    
        private ViewPager mViewPager;
        private MyPagerAdapter mMyPagerAdapter;
        private TextView mPageNum;
        private EditText mEditText;
        private Button mButton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_pager_adapter);
            initView();
        }
    
        private void initView() {
            mViewPager = (ViewPager) this.findViewById(R.id.viewpager);
            mPageNum = (TextView) this.findViewById(R.id.page_num);
            mEditText = (EditText) this.findViewById(R.id.edit_text);
            mButton = (Button) this.findViewById(R.id.button);
            mButton.setOnClickListener(this);
    
            List<View> viewList = new ArrayList<View>();
    
            LayoutInflater layoutInflater = getLayoutInflater();
            View view1 = layoutInflater.inflate(R.layout.page1, null);
            View view2 = layoutInflater.inflate(R.layout.page2, null);
            View view3 = layoutInflater.inflate(R.layout.page3, null);
            View view4 = layoutInflater.inflate(R.layout.page4, null);
            View view5 = layoutInflater.inflate(R.layout.page5, null);
    
            viewList.add(view1);
            viewList.add(view2);
            viewList.add(view3);
            viewList.add(view4);
            viewList.add(view5);
            mMyPagerAdapter = new MyPagerAdapter(viewList);
            mViewPager.setAdapter(mMyPagerAdapter);
            mViewPager.addOnPageChangeListener(new OnMyPageChangeListener());//页面变化监听
            mViewPager.setOffscreenPageLimit(2);//设置缓存页面数。当前页,左右两边(单边)最大缓存页面数。
    //        mViewPager.setOnScrollChangeListener(new OnMyScrollChangeListener());//滚动状态监听,minSdkVersion:23
    //        mViewPager.getCurrentItem();//获取当前显示页索引
    //        mViewPager.getOffscreenPageLimit();//获取缓存页面数
    //        mViewPager.onSaveInstanceState();
    //        mViewPager.setPageTransformer();
            mMyPagerAdapter.notifyDataSetChanged();
            setPageNum(0);//设置显示首页
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    goPage();
                    break;
            }
        }
    
        private void goPage() {
            String pageNumStr = mEditText.getText().toString();
            if (pageNumStr == null || pageNumStr.isEmpty()) {
                return;
            }
            int pageNum = Integer.parseInt(pageNumStr);
            if (pageNum > 0 && pageNum <= mMyPagerAdapter.getCount()) {
                mViewPager.setCurrentItem(pageNum - 1);//设置当前显示页索引
            }
        }
    
        private void setPageNum(int position) {
            String pageNum = (position + 1) + "/" + mMyPagerAdapter.getCount();
            mPageNum.setText(pageNum);
        }
    
        /*private class OnMyScrollChangeListener implements View.OnScrollChangeListener {
    
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
    
            }
        }*/
    
        /**
         * 页面变化监听器
         */
        private class OnMyPageChangeListener implements ViewPager.OnPageChangeListener {
    
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                Log.i(TAG, NAME + "--onPageScrolled++position:" + position + ",++positionOffset:" + positionOffset + ",++positionOffsetPixels:" + positionOffsetPixels);
            }
    
            @Override
            public void onPageSelected(int position) {
                Log.i(TAG, NAME + "--onPageSelected++position:" + position);
                setPageNum(position);
            }
    
            @Override
            public void onPageScrollStateChanged(int state) {
                Log.i(TAG, NAME + "--onPageScrollStateChanged++state:" + state);
            }
        }
    
        /**
         * 页面适配器
         */
        private class MyPagerAdapter extends PagerAdapter {
    
            private List<View> mViewList;
    
            MyPagerAdapter(List<View> viewList) {
                mViewList = viewList;
            }
    
            @Override
            public int getCount() {
                Log.i(TAG, NAME + "--getCount");
                return mViewList.size();
            }
    
            @Override
            public int getItemPosition(Object object) {
                Log.i(TAG, NAME + "--getItemPosition");
                return super.getItemPosition(object);
            }
    
            @Override
            public boolean isViewFromObject(View view, Object object) {
                Log.i(TAG, NAME + "--isViewFromObject");
                return view == object;
            }
    
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                View view = mViewList.get(position);
                container.addView(view);
                Log.i(TAG, NAME + "--instantiateItem++container:" + container.getChildCount() + "++position:" + position);
                return view;
            }
    
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                container.removeView(mViewList.get(position));
                Log.i(TAG, NAME + "--destroyItem++container:" + container.getChildCount() + "++position:" + position);
            }
    
        }
    
    }
    FragmentPagerAdapter

      该适配器最好用于有限个静态fragment页面的管理。尽管不可见的视图有时会被销毁,但用户所有访问过的fragment都会被保存在内存中。因此fragment实例会保存大量的各种状态,这就造成了很大的内存开销。
      使用该适配器,ViewPager在进行Fragment界面切换时,会将超过缓存数的界面销毁,但不销毁数据,即调用Fragment的onDestoryView方法,所有创建过的Fragment都会被保留。

    主要方法详解
    • public abstract Fragment getItem (int position)
      返回position位置关联的Fragment。
      FragmentPagerAdapter会将所有生成的 Fragment 对象通过 FragmentManager 保存起来备用,以后需要该 Fragment 时,都会从 FragmentManager 读取,而不会再次调用 getItem() 方法。该方法只有在创建新的Fragment时才调用。
      在需要时,该函数将被 instantiateItem() 所调用。
      如果需要向 Fragment 对象传递相对静态的数据时,我们一般通过 Fragment.setArguments() 来进行,这部分代码应当放到 getItem()。它们只会在新生成 Fragment 对象时执行一遍。
    • public long getItemId (int position)
      返回给定位置项的唯一标识。
    • public Object instantiateItem (ViewGroup container, int position)
      该方法每次生成缓存页面之外的Fragment时都会调用,这里的Fragment可能是新的生成,也可能是恢复。
      函数中判断一下要生成的Fragment是否已经生成过了,如果生成过了,就使用旧的,旧的将被Fragment.attach();如果没有,就调用getItem()生成一个新的,新的对象将被FragmentTransation.add()
      FragmentPagerAdapter会将所有生成的Fragment对象通过FragmentManager保存起来备用,以后需要该Fragment时,都会从FragmentManager读取,而不会再次调用getItem()方法。
      如果需要在生成Fragment对象后,将数据集中的一些数据传递给该Fragment,这部分代码应该放到这个函数的重载里。在我们继承的子类中,重载该函数,并调用FragmentPagerAdapter.instantiateItem()取得该函数返回 Fragment 对象,然后,我们该Fragment对象中对应的方法,将数据传递过去,然后返回该对象。
      否则,如果将这部分传递数据的代码放到getItem()中,在PagerAdapter.notifyDataSetChanged()后,这部分数据设置代码将不会被调用。
    • public void destroyItem(ViewGroup container, int position, Object object)
      超出缓存的页面,将调用该方法从视图中移除。
      该函数被调用后,会对Fragment进行FragmentTransaction.detach()。这里不是remove(),只是detach(),因此Fragment还在FragmentManager管理中,Fragment所占用的资源不会被释放。
    FragmentPagerAdapter使用示例

    FragmentPagerAdapterActivity.java文件

    /**
     * Created by sunxiaodong on 16/1/27.
     * 该适配器最好用于有限个静态fragment页面的管理。尽管不可见的视图有时会被销毁,但用户所有访问过的fragment都会被保存在内存中。因此fragment实例会保存大量的各种状态,这就造成了很大的内存开销。
     * 所以如果要处理大量的页面切换,建议使用FragmentStatePagerAdapter.
     */
    public class FragmentPagerAdapterActivity extends AppCompatActivity implements View.OnClickListener {
    
        private static final String NAME = FragmentPagerAdapterActivity.class.getSimpleName();
        private static final String TAG = "sxd";
    
        private ViewPager mViewPager;
        private MyFragmentPagerAdapter mMyFragmentPagerAdapter;
        private TextView mPageNum;
        private EditText mEditText;
        private Button mButton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_pager_adapter);
            initView();
        }
    
        private void initView() {
            mViewPager = (ViewPager) this.findViewById(R.id.viewpager);
            mPageNum = (TextView) this.findViewById(R.id.page_num);
            mEditText = (EditText) this.findViewById(R.id.edit_text);
            mButton = (Button) this.findViewById(R.id.button);
            mButton.setOnClickListener(this);
    
            List<Fragment> fragments = new ArrayList<Fragment>();
            Fragment fragment1 = MyFragment.newInstance(1);
            Fragment fragment2 = MyFragment.newInstance(2);
            Fragment fragment3 = MyFragment.newInstance(3);
            Fragment fragment4 = MyFragment.newInstance(4);
            Fragment fragment5 = MyFragment.newInstance(5);
    
    
            fragments.add(fragment1);
            fragments.add(fragment2);
            fragments.add(fragment3);
            fragments.add(fragment4);
            fragments.add(fragment5);
    
            mMyFragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments);
            mViewPager.setAdapter(mMyFragmentPagerAdapter);
            mViewPager.addOnPageChangeListener(new OnMyPageChangeListener());//页面变化监听
            mViewPager.setOffscreenPageLimit(2);//设置缓存页面数。当前页,左右两边(单边)最大缓存页面数。
    //        mViewPager.setOnScrollChangeListener(new OnMyScrollChangeListener());//滚动状态监听,minSdkVersion:23
    //        mViewPager.getCurrentItem();//获取当前显示页索引
    //        mViewPager.getOffscreenPageLimit();//获取缓存页面数
    //        mViewPager.onSaveInstanceState();
    //        mViewPager.setPageTransformer();
            setPageNum(0);//设置显示首页
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    goPage();
                    break;
            }
        }
    
        private void goPage() {
            String pageNumStr = mEditText.getText().toString();
            if (pageNumStr == null || pageNumStr.isEmpty()) {
                return;
            }
            int pageNum = Integer.parseInt(pageNumStr);
            if (pageNum > 0 && pageNum <= mMyFragmentPagerAdapter.getCount()) {
                mViewPager.setCurrentItem(pageNum - 1);//设置当前显示页索引
            }
        }
    
        private void setPageNum(int position) {
            String pageNum = (position + 1) + "/" + mMyFragmentPagerAdapter.getCount();
            mPageNum.setText(pageNum);
        }
    
        /**
         * 页面变化监听器。
         * 三个方法的执行顺序为:用手指拖动翻页时,最先执行一遍onPageScrollStateChanged(1),然后不断执行onPageScrolled,放手指的时候,直接立即执行一次onPageScrollStateChanged(2),然后立即执行一次onPageSelected,然后再不断执行onPageScrollStateChanged,最后执行一次onPageScrollStateChanged(0)。
         */
        private class OnMyPageChangeListener implements ViewPager.OnPageChangeListener {
    
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                //position:当用手指滑动时,如果手指按在页面上不动,position和当前页面index是一致的;如果手指向左拖动(相应页面向右翻动),这时候position大部分时间和当前页面是一致的,只有翻页成功的情况下最后一次调用才会变为目标页面;如果手指向右拖动(相应页面向左翻动),这时候position大部分时间和目标页面是一致的,只有翻页不成功的情况下最后一次调用才会变为原页面。
                //当直接设置setCurrentItem翻页时,如果是相邻的情况(比如现在是第二个页面,跳到第一或者第三个页面),如果页面向右翻动,大部分时间是和当前页面是一致的,只有最后才变成目标页面;如果向左翻动,position和目标页面是一致的。这和用手指拖动页面翻动是基本一致的。
                //如果不是相邻的情况,比如我从第一个页面跳到第三个页面,position先是0,然后逐步变成1,然后逐步变成2;我从第三个页面跳到第一个页面,position先是1,然后逐步变成0,并没有出现为2的情况。
                //positionOffset:当前页面滑动比例,如果页面向右翻动,这个值不断变大,最后在趋近1的情况后突变为0。如果页面向左翻动,这个值不断变小,最后变为0。
                //positionOffsetPixels:当前页面滑动像素,变化情况和positionOffset一致。
                Log.i(TAG, NAME + "--onPageScrolled++position:" + position + ",++positionOffset:" + positionOffset + ",++positionOffsetPixels:" + positionOffsetPixels);
            }
    
            @Override
            public void onPageSelected(int position) {
                //position是被选中页面的索引,该方法在页面被选中或页面滑动足够距离切换到该页手指抬起时调用。
                Log.i(TAG, NAME + "--onPageSelected++position:" + position);
                setPageNum(position);
            }
    
            @Override
            public void onPageScrollStateChanged(int state) {
                //这个方法在手指操作屏幕的时候发生变化。有三个值:0(END),1(PRESS) , 2(UP) 。
                //当用手指滑动翻页时,手指按下去的时候会触发这个方法,state值为1,手指抬起时,
                //如果发生了滑动(即使很小),这个值会变为2,然后最后变为0 。总共执行这个方法三次。
                //一种特殊情况是手指按下去以后一点滑动也没有发生,这个时候只会调用这个方法两次,state值分别是1,0 。
                //当setCurrentItem翻页时,会执行这个方法两次,state值分别为2 , 0 。
                Log.i(TAG, NAME + "--onPageScrollStateChanged++state:" + state);
            }
        }
    
        /**
         * Fragment页面适配器
         * ViewPager在进行Fragment界面切换时,会将超过缓存数的界面销毁,但不销毁数据,即调用Fragment的onDestoryView方法
         * 所有创建过的Fragment都会被保留
         */
        private class MyFragmentPagerAdapter extends FragmentPagerAdapter {
    
            private List<Fragment> mFragments;
    
            MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
                super(fm);
                mFragments = fragments;
            }
    
            @Override
            public Fragment getItem(int position) {
                //FragmentPagerAdapter 会将所有生成的 Fragment 对象通过 FragmentManager 保存起来备用,以后需要该 Fragment 时,
                // 都会从 FragmentManager 读取,而不会再次调用 getItem() 方法。该方法只有在创建新的Fragment时才调用。
                //1. 在需要时,该函数将被 instantiateItem() 所调用。
                //2. 如果需要向 Fragment 对象传递相对静态的数据时,我们一般通过 Fragment.setArguments() 来进行,这部分代码应当放到 getItem()。它们只会在新生成 Fragment 对象时执行一遍。
                Log.i(TAG, NAME + "--getItem++position:" + position);
                return mFragments.get(position);
            }
    
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                //该方法每次生成缓存页面之外的Fragment时都会调用,这里的Fragment可能是新的生成,也可能是恢复
                //1.函数中判断一下要生成的 Fragment 是否已经生成过了,如果生成过了,就使用旧的,旧的将被 Fragment.attach();如果没有,就调用 getItem() 生成一个新的,新的对象将被 FragmentTransation.add()。
                //2. FragmentPagerAdapter 会将所有生成的 Fragment 对象通过 FragmentManager 保存起来备用,以后需要该 Fragment 时,都会从 FragmentManager 读取,而不会再次调用 getItem() 方法。
                //3. 如果需要在生成 Fragment 对象后,将数据集中的一些数据传递给该 Fragment,这部分代码应该放到这个函数的重载里。在我们继承的子类中,重载该函数,并调用 FragmentPagerAdapter.instantiateItem() 取得该函数返回 Fragment 对象,然后,我们该 Fragment 对象中对应的方法,将数据传递过去,然后返回该对象。
                //否则,如果将这部分传递数据的代码放到 getItem()中,在 PagerAdapter.notifyDataSetChanged() 后,这部分数据设置代码将不会被调用。
                Log.i(TAG, NAME + "--instantiateItem++container:" + container.getChildCount() + "++position:" + position);
                return super.instantiateItem(container, position);
            }
    
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                //超出缓存的页面,将调用该方法从视图中移除
                //该函数被调用后,会对 Fragment 进行 FragmentTransaction.detach()。这里不是 remove(),只是 detach(),因此 Fragment 还在 FragmentManager 管理中,Fragment 所占用的资源不会被释放。
                Log.i(TAG, NAME + "--destroyItem++container:" + container.getChildCount() + "++position:" + position);
                super.destroyItem(container, position, object);
            }
    
            @Override
            public int getCount() {
                Log.i(TAG, NAME + "--getCount++");
                return mFragments.size();
            }
        }
    
    }
    FragmentStatePagerAdapter

      该适配器适合处理大量的页面切换,其能能够保存和恢复Fragment状态的适配器,超出缓存的Fragment会调用onDestory()方法,彻底对销毁Fragment进行销毁。

    主要方法详解
    • public Fragment getItem(int position)
      返回position位置关联的Fragment。
      该方法在滑到已经缓存的页面时,并不被调用。缓存外已经创建过并被销毁的页面,还会再调用该方法,重新创建。
    • public Object instantiateItem(ViewGroup container, int position)
      除非碰到FragmentManager刚好从SavedState中恢复了对应的Fragment的情况外(从页面缓存中恢复),该函数将会调用getItem()函数,生成新的Fragment对象。新的对象将被FragmentTransaction.add()
    • public void destroyItem(ViewGroup container, int position, Object object)
      超出缓存的页面,将调用该方法从视图中移除。
    FragmentStatePagerAdapter使用示例
    /**
         * 能够保存和恢复Fragment状态的适配器
         * 超出缓存的Fragment会调用onDestory()方法,彻底对销毁Fragment进行销毁
         */
        class MyFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
    
            private List<Fragment> mFragments;
    
            MyFragmentStatePagerAdapter(FragmentManager fm, List<Fragment> fragments) {
                super(fm);
                mFragments = fragments;
            }
    
            @Override
            public Fragment getItem(int position) {
                //该方法在滑到已经缓存的页面时,并不被调用。缓存外已经创建过并被销毁的页面,还会再调用该方法,重新创建。
                Log.i(TAG, NAME + "--getItem++position:" + position);
                return mFragments.get(position);
            }
    
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                //除非碰到 FragmentManager 刚好从 SavedState 中恢复了对应的 Fragment 的情况外(从页面缓存中恢复),该函数将会调用 getItem() 函数,生成新的 Fragment 对象。新的对象将被 FragmentTransaction.add()。
                Log.i(TAG, NAME + "--instantiateItem++container:" + container.getChildCount() + "++position:" + position);
                return super.instantiateItem(container, position);
            }
    
            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                //超出缓存的页面,将调用该方法从视图中移除
                Log.i(TAG, NAME + "--destroyItem++container:" + container.getChildCount() + "++position:" + position);
                super.destroyItem(container, position, object);
            }
    
            @Override
            public int getCount() {
                Log.i(TAG, NAME + "--getCount++");
                return mFragments.size();
            }
        }

    源码地址

    展开全文
  • ViewPager 就是其中之一。利用它,我们可以做很多事情,从最简单的导航,到页面菜单等等。 准备 在使用ViewPager之前,需要在 build.gradle 中加入如下语句: compile 'com.android.sup...

    android-support-v4.jar 是谷歌提供给我们的一个兼容低版本安卓设备的软件包,里面包囊了只有在 Android 3.0 以上可用的API。而 ViewPager 就是其中之一。利用它,我们可以做很多事情,从最简单的导航,到页面菜单等等。

    准备

    在使用ViewPager之前,需要在 build.gradle 中加入如下语句:

    compile 'com.android.support:support-v4:25.3.0'
    compile 'com.github.hackware1993:MagicIndicator:1.5.0'

    其中第一行是 android-support-v4 软件包,ViewPager 就在里面;第二行是一个开源的导航器,通过它,我们可以实现各种导航效果。

    ViewPager -- 基本用法

    ViewPager 的基本用法可以分为以下步骤:

    1. 在 layout 文件中定义一个 ViewPager 组件;
    2. 在 Activity(或Fragment等)中取得 ViewPager 的引用;
    3. 为 ViewPager 设置适配器;
    4. [为 ViewPager 设置滑动特效];
    5. [为 ViewPager 设置监听器]。

    其中最后两步是可选的,但如果没有最后两步,那我们的 ViewPager 就仅仅是一个普普通通的 ViewPager 了。

    下面我们来写一个最简单的例子,这里事先在drawable文件夹中放入了一些图片,并且这些图片都以 `page` 开头:

    复制代码

    <?xml version="1.0" encoding="utf-8"?>
    <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="com.zzw.activity.PageActivity">
    
        <android.support.v4.view.ViewPager
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/view_pager"
            android:background="@android:color/black" />
    </RelativeLayout>

    复制代码

    我们在布局文件里定义了一个 ViewPager 组件,然后需要在 Activity 中对它进行设置:

    复制代码

    pager=(ViewPager) findViewById(R.id.view_pager);
    PagerAdapter adapter=new ViewAdapter(pages);
    pager.setAdapter(adapter);
    
    class ViewAdapter extends PagerAdapter {
        private List<View> datas;
    
        public ViewAdapter(List<View> list) {
            datas=list;
        }
    
        @Override
        public int getCount() {
            return datas.size();
        }
    
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view==object;
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view=datas.get(position);
            container.addView(view);
            return view;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(datas.get(position));
        }
    }

    复制代码

    嗯,现在我们就完成了一个“图片浏览器”,效果和下图类似:

    PS:你可能注意到,我们的效果图底部有一个导航器,这是因为这个效果图是加上导航器之后的效果(实在懒得回头再改一遍了)。
    那你可能会问,这个导航器怎么实现的,为啥上面的代码里没有?别急,因为这个开源的导航器实在太牛B了,所以我想专门用一节来介绍,如果你等不及的话,也可以先去看导航器的介绍。


    好了,现在我们已经基本熟悉了 ViewPager 的基本使用方法,但是!ViewPager 的效果还远远不止于此,我们接着往下面看。

    ViewPager -- 浮现特效

    嗯,现在我们要在上面的“图片浏览器”基础上加点特效了(要不然用户肯定审美疲劳了)。试想,当我们滑动图片时,如果下一张图片能从后面“浮现”出来,是不是很棒!好了,那我们就来尝试添加这一个特效。

    但是,要从哪里入手呢?哈哈,其实Google早就为我们提供了相应的API,就是 android.support.v4.view.ViewPager.PageTransformer 这个接口,任何实现了这个接口的类都可以为 ViewPager 提供一种特效哦。

    我们发现,这个接口里只有一个方法 public void transformPage(View page, float position) ,看看它的两个参数,`page`表示 ViewPager 中的一页,`position`表示`page`当前的位置,[-1, 0)表示屏幕左边的`page`(部分可见),[0, 0]表示屏幕上的`page`(完全可见),(0, 1]表示屏幕右边的`page`(部分可见),具体看下图:


    当`page`向左边滑动时,`position`从0向-1变化,当`position==-1`时完全不可见;当`page`向右滑动时,`position`从0向1变化,当`position==1`时完全不可见。

    如果你想更加深入的了解`position`的规律(可能是我没表达清楚),可以参考鸿洋大神的文章《Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)》

    好了,经过上面的说明,我们发现只需要处理[-1, 1]范围的`position`就行了:

    复制代码

    public class ScalePageTransformer implements ViewPager.PageTransformer {
        private static final float MIN_SCALE=0.75f;
    
        @Override
        public void transformPage(View page, float position) {
            //Log.d("TAG", "<"+page.hashCode()+", "+position+">");
            // out of left screen
            if(position<-1.0f) {
                page.setScaleX(MIN_SCALE);
                page.setScaleY(MIN_SCALE);
            }
            // slide left
            else if(position<=0.0f) {
                page.setAlpha(1.0f);
                page.setTranslationX(0.0f);
                page.setScaleX(1.0f);
                page.setScaleY(1.0f);
            }
            // slide right
            else if(position<=1.0f) {
                page.setAlpha(1.0f-position);
                page.setTranslationX(-page.getWidth()*position);
                float scale=MIN_SCALE+(1.0f-MIN_SCALE)*(1.0f-position);
                page.setScaleX(scale);
                page.setScaleY(scale);
            }
            // out of right screen
            else {
                page.setScaleX(MIN_SCALE);
                page.setScaleY(MIN_SCALE);
            }
        }
    }

    复制代码

    然后还要为 ViewPager 添加这个特效:

    pager.setPageTransformer(true, new ScalePageTransformer());

    现在的效果如下:


    哈哈,我们的“图片浏览器”是不是好看了一大截。但是,别急着走,我们还可以实现更多的特效。

    ViewPager -- 旋转特效

    有了上面的基础,下面我们再来做一个旋转特效吧:

    复制代码

    public class RotatePageTransformer implements ViewPager.PageTransformer {
        private static final float MAX_ROTATION=20.0f;
    
        @Override
        public void transformPage(View page, float position) {
            if(position<-1)
                rotate(page, -MAX_ROTATION);
            else if(position<=1)
                rotate(page, MAX_ROTATION*position);
            else
                rotate(page, MAX_ROTATION);
        }
    
        private void rotate(View view, float rotation) {
            view.setPivotX(view.getWidth()*0.5f);
            view.setPivotY(view.getHeight());
            view.setRotation(rotation);
        }
    
    }

    复制代码

    这个更简单,只需要根据`position`来决定旋转的角度即可。效果如下:

    ViewPager -- 3D画廊

    下面我们再实现一个特效,不过这个特效稍微复杂,但是效果真的挺炫!我们先来看下效果:

    是不是就像自己身处3D画廊一样。好了,下面我们就来实现这个特效。

    首先还是 PageTransformer:

    复制代码

    public class GalleryPageTransformer implements ViewPager.PageTransformer {
        private static final float MAX_ROTATION=20.0f;
        private static final float MIN_SCALE=0.75f;
        private static final float MAX_TRANSLATE=20.0f;
    
        @Override
        public void transformPage(View page, float position) {
            if(position<-1) {
                page.setTranslationX(MAX_TRANSLATE);
                page.setScaleX(MIN_SCALE);
                page.setScaleY(MIN_SCALE);
                page.setRotationY(-MAX_ROTATION);
            }
            else if(position<=0) {
                page.setTranslationX(-MAX_TRANSLATE*position);
                float scale=MIN_SCALE+(1-MIN_SCALE)*(1.0f+position);
                page.setScaleX(scale);
                page.setScaleY(scale);
                page.setRotationY(MAX_ROTATION*position);
            }
            else if(position<=1) {
                page.setTranslationX(-MAX_TRANSLATE*position);
                float scale=MIN_SCALE+(1-MIN_SCALE)*(1.0f-position);
                page.setScaleX(scale);
                page.setScaleY(scale);
                page.setRotationY(MAX_ROTATION*position);
            }
            else {
                page.setTranslationX(-MAX_TRANSLATE);
                page.setScaleX(MIN_SCALE);
                page.setScaleY(MIN_SCALE);
                page.setRotationY(MAX_ROTATION);
            }
        }
    }

    复制代码

    这里的“3D”特效主要是靠 View.setRotationY(float rotation) 方法实现的,此方法的作用是让 View 绕着Y轴旋转一定角度(关于Android的三维坐标系请大家自己查询)。

    看到这,可能你会问,倒影去哪了?别急,我们并不是在 PageTransformer 里实现倒影的,而是一开始就有的。没错,我们在为 ViewPager 设置适配器时,就直接把有倒影的图片封装到适配器中了。

    下面的方法负责生成有倒影的图片:

    复制代码

    public static Bitmap getReverseBitmapById(Context context, int resId, float percent) {
        // get the source bitmap
        Bitmap srcBitmap=BitmapFactory.decodeResource(context.getResources(), resId);
        // get the tow third segment of the reverse bitmap
        Matrix matrix=new Matrix();
        matrix.setScale(1, -1);
        Bitmap rvsBitmap=Bitmap.createBitmap(srcBitmap, 0, (int) (srcBitmap.getHeight()*(1-percent)),
                srcBitmap.getWidth(), (int) (srcBitmap.getHeight()*percent), matrix, false);
        // combine the source bitmap and the reverse bitmap
        Bitmap comBitmap=Bitmap.createBitmap(srcBitmap.getWidth(),
                srcBitmap.getHeight()+rvsBitmap.getHeight()+20, srcBitmap.getConfig());
        Canvas gCanvas=new Canvas(comBitmap);
        gCanvas.drawBitmap(srcBitmap, 0, 0, null);
        gCanvas.drawBitmap(rvsBitmap, 0, srcBitmap.getHeight()+20, null);
        Paint paint=new Paint();
        LinearGradient shader=new LinearGradient(0, srcBitmap.getHeight()+20, 0, comBitmap.getHeight(),
                Color.BLACK, Color.TRANSPARENT, Shader.TileMode.CLAMP);
        paint.setShader(shader);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        gCanvas.drawRect(0, srcBitmap.getHeight()+20, srcBitmap.getWidth(), comBitmap.getHeight(), paint);
        return comBitmap;
    }

    复制代码

    其中的`percent`参数指定倒影占原图的比例。

    然后对原图进行处理:

    复制代码

    private List<View> getPages() {
        List<View> pages=new ArrayList<>();
        Field[] fields=R.drawable.class.getDeclaredFields();
        try {
            for (Field field : fields) {
                if (field.getName().startsWith("page")) {
                    ImageView view = new ImageView(this);
                    view.setImageBitmap(ImageUtils.getReverseBitmapById(this, field.getInt(null), 0.5f));
                    pages.add(view);
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return pages;
    }

    复制代码

    现在我们就有了一个绚丽的“3D画廊”了:

    MagicIndicator -- MAGIC!!!

    到此为止,对 ViewPager 的介绍也告一段落了。但是,你有没有发现,当我们滑动图片时,不知道当前是第几张图(我们的图片还很少,如果多的话就会有这个问题)!!!

    为了解决这个问题,我们还需要一个指示器/导航器来指定当前的位置。但是,一想到要从头实现一个指示器就感到头大(当然一个简单的指示器还是很容易的)。幸好,还有 MagicIndicator 来帮你。

    MagicIndicator,“人”如其名,它的特点就是“MAGIC”!!!不多说,先来看下它的效果图:


    看到这效果我直接给跪了!关于 MagicIndicator 的详细介绍可以看这几篇文章:

    1. 系列之一 —— 使用MagicIndicator打造千变万化的ViewPager指示器》
    2. 系列之二 —— MagicIndicator使用指南》
    3. 《MagicIndicator系列之三 —— MagicIndicator原理浅析及扩展MagicIndicator的4种方式》

    MagicIndicator 的GitHub地址是:https://github.com/hackware1993/MagicIndicator,关于它的具体用法也在上面,这里我们就不多说了。

     

    ViewPager与Fragment

    ViewPager 经常与 Fragment 结合来实现各种页面切换,比如我们开头提到的页面菜单。由于在 ViewPager 中使用 Fragment 和上面介绍的方式稍有不同,因此我们在这里单独介绍一下。

    仔细想想,我们之前的 ViewPager 的页面都是 View,而现在要换成 Fragment,是不是需要替换适配器?没错,的确如此,不过我们没必要从头写一个适配器,Google为我们提供了两个抽象类:FragmentPagerAdapter 和 FragmentStatePagerAdapter。

    不论是继承 FragmentPagerAdapter 还是 FragmentStatePagerAdapter,都需要实现构造器、 int getCount() 和 Fragment getItem(int position) 方法。下面是我们的例子:

    复制代码

    public class FragmentAdapter extends FragmentPagerAdapter {
        private List<Fragment> datas;
    
        public FragmentAdapter(FragmentManager fm, List<Fragment> list) {
            super(fm);
            datas=list;
        }
    
        @Override
        public int getCount() {
            return datas.size();
        }
    
        @Override
        public Fragment getItem(int position) {
            return datas.get(position);
        }
    }

    复制代码

    FragmentPagerAdapter 中的每一个 Fragment 都将保存在内存之中,因此适用于那些相对静态、数量较少的情况;如果需要处理有很多页,并且数据动态性较大、占用内存较多的情况,应该使用FragmentStatePagerAdapter。

    FragmentStatePagerAdapter 和 FragmentPagerAdapter 一样,继承自 PagerAdapter。但和 FragmentPagerAdapter 不同的是,正如其类名中的“State”所表明的含义一样,它只保留当前页面。当页面离开视线后,就会被回收,释放其资源;而在页面需要显示时,将生成新的页面。这样的好处就是当拥有大量的页面时,不会在内存中占用大量的内存。

    有了 Fragment 的适配器后,还需要为 ViewPager 设置适配器:

    FragmentAdapter adapter=new FragmentAdapter(getSupportFragmentManager(), frags);
    pager.setAdapter(adapter);

    最后的效果如下:

     

    源代码

    上述所有源代码都已上传到GitHub:
    https://github.com/jzyhywxz/ViewPager

    展开全文
  • 这两天浏览安卓开发者官网的时候,发现google悄然推出了一个新的控件:ViewPager2 ,一看名称就知道这是一个和我们常用的 ViewPager 功能相似的控件,算是ViewPager的升级版吧。目前还只是推出了第一个预览版,我们...

    这两天浏览安卓开发者官网的时候,发现google悄然推出了一个新的控件:ViewPager2 ,一看名称就知道这是一个和我们常用的 ViewPager 功能相似的控件,算是ViewPager的升级版吧。目前还只是推出了第一个预览版,我们可以直接引入来使用了:

    implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha04'
    

    我们先来看看有哪些功能和使用上的变化:https://developer.android.google.cn/jetpack/androidx/releases/viewpager2

    新功能:

    • List item
    • 支持RTL布局
    • 支持竖向滚动
    • 完整支持notifyDataSetChanged

    API的变动:

    • FragmentStateAdapter替换了原来的 FragmentStatePagerAdapter
    • RecyclerView.Adapter替换了原来的 PagerAdapter
    • registerOnPageChangeCallback替换了原来的 addPageChangeListener

    看了上面这些介绍,有一点比较吸引人的就是支持竖向滚动了,这是怎么实现的呢?ViewPager2的源码不长,我们来简单分析一下。

    简单解析

    通过查看源码得知,ViewPager2是直接继承ViewGroup的,意味着和ViewPager不兼容,类注释上也写了它的作用是取代ViewPager,不过短时间内ViewPager应该还不会被废弃掉。

    继续查看源码,发现了两个比较重要的成员变量:

    private RecyclerView mRecyclerView;
    private LinearLayoutManager mLayoutManager;
    

    所以很清楚得知,ViewPager2的核心实现就是RecyclerView+LinearLayoutManager了,因为LinearLayoutManager本身就支持竖向和横向两种布局方式,所以ViewPager2也能很容易地支持这两种滚动方向了,而几乎不需要添加任何多余的代码。

    其实在此之前也不乏有大神采用RecyclerView来实现轮播图效果的,具体实现发生略有不同,但大体思想是一致的。这次ViewPager2的推出意味着这种方法终于被扶正了。

    为了让RecyclerView变得像原来的ViewPager,需要设置下SnapHelper:

        new PagerSnapHelper().attachToRecyclerView(mRecyclerView);
    

    熟悉RecyclerView的同学都知道,SnapHelper用于辅助RecyclerView在滚动结束时将Item对齐到某个位置。PagerSnapHelper的作用让滑动结束时使当前Item居中显示,并且 限制一次只能滑动一页,不能快速滑动,这样就和viewpager的交互很像了。

    另外和viewpager一样,viewpager2可以承载fragment,我们需要继承实现它提供的FragmentStateAdapter:

    public abstract class FragmentStateAdapter extends
    RecyclerView.Adapter implements StatefulAdapter

    这是一个包含FragmentManager和数据状态恢复功能的RecyclerView.Adapter,具体实现可以参看源码。所以大家也可以用TabLayout+ViewPager2+Fragment来实现联动展示效果。

    使用

    通过android:orientation来指定滚动方向

      <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager2"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:orientation="vertical" />
    
    

    在代码中设置一个普通的RecyclerView.adapter:

       ViewPager2 viewPager2=findViewById(R.id.viewpager2);
    
       RecyclerviewAdapter adapter = new RecyclerviewAdapter(this);
       viewPager2.setAdapter(adapter);
    

    这样竖直轮播图就大功告成了。

    小结

    viewpager2利用recyclerview来实现viewpager的功能,无疑使使其可扩展性大大提升,代码也变得更优雅简洁,使用起来也更灵活。不过目前viewpager2只是第一个预览版,还存在稳定性方面的问题,不建议大家引入到正式项目中来,尝尝鲜就好

    Android开发资料+面试架构资料 免费分享 点击链接 即可领取

    《Android架构师必备学习资源免费领取(架构视频+面试专题文档+学习笔记)》

    展开全文
  • ViewPager

    2017-09-21 10:09:25
    ViewPager 一、ViewPager简介: (一)、作用: ViewPager的功能就是可以使视图滑动,就像Lanucher左右滑动那样。ViewPager用于实现多页面的切换效果。该类存在于Google的兼容包里面,android.support.v4...
  • ViewPager 全面总结

    2019-07-17 13:48:44
    Viewpager,视图翻页工具,提供了多页面切换的效果。Android 3.0后引入的一个UI控件,位于v4包中。低版本使用需要导入v4包,但是现在我们开发的APP一般不再兼容3.0及以下的系统版本,另外现在大多数使用Android ...
  • 先简单介绍一下 微信中的左右滑动可以切换联系人和消息,有些应用图片自动播放就是用ViewPager做的;介绍过之后直接讲解;既然轮播的是视图自然咋们的线性布局或者相对布局中就要使用 ViewPager此View当然此...
  • ViewPager的介绍和使用

    2019-09-05 21:44:59
    ViewPager简介和用处 Viewpager,视图翻页工具,提供了多页面切换的效果。Android 3.0后引入的一个UI控件,位于v4包中。低版本使用需要导入v4包,但是现在我们开发的APP一般不再兼容3.0及以下的系统版本,另外现在...
  • viewpager就是其中之一利用它,我们可以做很多事情,从最简单的导航,到页面菜单等等。那如何使用它呢,与LisstView类似,我们也需要一个适配器,他就是PagerAdapter。看一下api的图片, ViewPager的功能就是可以...
  • 前言:上篇中我们讲解了如何快速实现了一个滑动页面,但问题在于,PageAdapter必须要重写的四个函数,它们都各有什么意义,在上节的函数内部为什么要这么实现,下面我们就结合android...3、《ViewPager 详解(三)-...
  • 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38026503 记得第一次见到ViewPager这个控件,瞬间...时间长了,ViewPager的切换效果觉得枯燥,形成了审美疲劳~~我们需要改变,今天教大家如
  • 前言:前几篇文章讲解了ViewPager的普通实现方法,但android官方最推荐的一种实现方法却是使用fragment,下面我们使用fragment来重新实现一下第一篇《ViewPager 详解(一)---基本入门》所实现的效果。 系列文章...
  • 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40411921,本文出自:【张鸿洋的...有兄弟提出,ViewPager自带了一个setPageTransformer用于设置切换动画~本篇博文,将:1、介绍如何使用setPageTr
  • 前言:前面我们用了三篇的时间讲述了有关ViewPager的基础知识,到这篇就要进入点实际的了。在第三篇《ViewPager 详解(三)---PagerTabStrip与PagerTitleStrip添加标题栏的异同》中,我们说了,PagerTabStrip和...
  • ViewPager是一个常用的android组件,不过通常我们使用ViewPager的时候不能实现左右无限循环滑动,在滑到边界的时候会看到一个不能翻页的动画,可能影响用户体验。此外,某些区域性的ViewPager(例如展示广告或者公告...
  • 有时候在开发中会遇到一些“诡异”的要求,比如在ViewPager中嵌入ListView,或者再嵌入一个ViewPager,那么在滑动的时候就会造成被嵌入的XXView不能滑动了,那么现在就把最外层的ViewPager禁止滑动吧,让被嵌入的...
  • 目前大多数的APP都采用的是几个Tab标签以及多个界面滑动的形式来提供多层次的交互体验,最为常用的做法就是采用TabLayout+ViewPager+Fragment的方式,最近在公司项目中遇到类似的界面,也看了各个论坛很多份博客,...
1 2 3 4 5 ... 20
收藏数 51,975
精华内容 20,790
关键字:

viewpager