ViewPager 入门到精通(一)

本文详细介绍了Android中ViewPager组件的使用方法,包括基本概念、PagerAdapter的实现方式、FragmentPagerAdapter的应用场景及实现,以及FragmentStatePagerAdapter的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 基本简介

      ViewPager 的存在意义就在于它能够让用户左右滑动屏幕来进行换页,为了实现这个功能,你需要加载一个 PagerAdapter 来实现这些需要滑动的view 的显示.

      ViewPager 大多情况下是和Fragment 联合使用的,这样提供了一个方便的管理每个page的lifecycle的方式。 一般来说,用户可以使用三种类型的PagerAdaper:

      1.PagerAdapter    2.FragmentPagerAdapter  3.FragmentStatePagerAdapter, 但是使用过ListView 的都应该清楚,这种使用adapter的方式是类似的。

     1. 1. 类继承关系如下:

           java.lang.Object

              android.view.View

                   android.view.ViewGroup

                        android.support.v4.view.ViewPager

     1.2  总括

           官方文档如下:

          Layout manager that allows the user to flip left and right through pages of data. You supply an implementation of a PagerAdapter to generate the pages that the view shows. Note this class is currently under early design and development. The API will likely change in later updates of the compatibility library, requiring changes to the source code of apps when they are compiled against the newer version. ViewPager is most often used in conjunction with Fragment, which is a convenient way to supply and manage the lifecycle of each page. There are standard adapters implemented for using fragments with the ViewPager, which cover the most common use cases. These are FragmentPagerAdapter and FragmentStatePagerAdapter; each of these classes have simple code showing how to build a full user interface with them.
          总的来说就如下几条:

          a  ViewPager主要用来左右滑动。(类似图片轮播)
          b  ViewPager要用适配器来连接“视图”和“数据”。(大家可以联想下listview的使用方法,原理是类似的)
          c  官方推荐ViewPager与Fragment一起使用,并且有专门的适配器。

   1.3  使用  PagerAdapter

        当你实现一个PagerAdapter时,你至少需要重写下面的几个方法:

            instantiateItem(ViewGroup, int)

            destroyItem(ViewGroup, int, Object)

            getCount()

            isViewFromObject(View, Object)

      先添加layout_viewpager

<?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.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v4.view.ViewPager>
</LinearLayout>


    再添加3个 page1.xml  page2.xml 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">
    <ImageView
        android:id="@+id/iv_first"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:scaleType="centerInside"
        android:background="@drawable/a2"/>
</RelativeLayout>
<?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">
    <ImageView
        android:id="@+id/iv_second"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:background="@drawable/aaa"
        android:scaleType="centerInside"/>
</RelativeLayout>


 

<?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">
    <ImageView
        android:id="@+id/iv_thrid"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:scaleType="centerInside"
        android:background="@drawable/aab"/>
</RelativeLayout>

在 MainActivity.java 中的代码:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_viewpager);
        initView();
    }

    private void initView() {
        myViewPager = (ViewPager)findViewById(R.id.view_pager);
        LayoutInflater inflater = getLayoutInflater();
        page1 = inflater.inflate(R.layout.page1, null);
        page2 = inflater.inflate(R.layout.page2, null);
        page3 = inflater.inflate(R.layout.page3, null);

        pageList = new ArrayList<View>();

        pageList.add(page1);
        pageList.add(page2);
        pageList.add(page3);

        myPageAdapter = new MyPageAdapter();
        myViewPager.setAdapter(myPageAdapter);

    }

    public class MyPageAdapter extends PagerAdapter {
        // 下面这个方法是用来显示 一个PagerView的内容的,由于图片显示的问题,或许会有OOM,或许会有耗时太大,这些不是本文的重点
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Log.v(TAG, "instantiateItem position " + position);
            View view = pageList.get(position);
            container.addView(view);
            return view;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(pageList.get(position));
        }

        @Override
        public int getCount() {
            return pageList.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    }

         一个最简单的PagerViewer就可以显示出来使用了。

       1.4 FragmentPagerAdapter 的使用

        大体上来说,这个和PagerAdapter的使用逻辑是相同的,只是它将简单的PagerAdapter换从了 FragmentPagerAdapter,从而Adapter的内容需要根据使用的Fragment 

       的情况来进行丰富,另外,MainActivity需要 进行一些针对fragment 和Activity 组合时的特殊效果,例如 tabcontent添加,又如tabitem 的动画效果等等。

        下面的代码添加了一些关于ViewPager 滚动的事件监听的接口,用来深入对ViewPager 的了解,滚动的监听无法就像 给普通的 view 添加 onClickHandlerListener一样

      我们需要给 ViewPager 添加一个 ViewPager.OnPageChangeListener ,而它又包含了几个接口内容的实现而已,具体细节请看下面的代码:

    

public class MainActivity extends AppCompatActivity implements
        ViewPager.OnPageChangeListener{

    private static String TAG = "PageView";

    private ViewPager myViewPager;
//    private View page1;
//    private View page2;
//    private View page3;
//    private ArrayList<View> pageList;
////    private MyPageAdapter myPageAdapter;


    private List<Fragment> fragmentList;
    private MyFragmentAdapter myFragmentAdapter;
    private TextView tab1;
    private TextView tab2;
    private TextView tab3;
    private ImageView line_tab;
    private int moveOne;
    private boolean isScrolling;
    private boolean isFingerLeaved;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_viewpager);
        initView();
    }

    private void initView() {
        myViewPager = (ViewPager)findViewById(R.id.view_pager);
        fragmentList = new ArrayList<Fragment>();
        CountingFragment f1 = new CountingFragment();
        CursorFragment f2 = new CursorFragment();
        ListFragment f3 = new ListFragment();
        fragmentList.add(f1);
        fragmentList.add(f2);
        fragmentList.add(f3);

        MyFragmentAdapter myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),fragmentList);
        myViewPager.setAdapter(myFragmentAdapter);
        myViewPager.setOnPageChangeListener(this);

        /**
         * 给每个fragment 添加一个tab,让这些tab的按键事件于fragment的显示相关联起来
         * 通过下面的代码我们可以看到两点:
         * 1: 代码的设计是有问题的,因为当添加一个fragment的时候,这个activity就需要修改,没有遵守 面向对象的设计思想
         * 2:FragmentPagerAdapter 的模式只适合于在具有少数的几个fragment的方式下使用,因为这些fragment一直是缓存着

         */

        tab1 = (TextView)findViewById(R.id.tv_page1);
        tab1.setTextColor(Color.BLUE);
        tab2 = (TextView)findViewById(R.id.tv_page2);
        tab3 = (TextView)findViewById(R.id.tv_page3);
        tab1.setOnClickListener(myClickListener);
        tab2.setOnClickListener(myClickListener);
        tab3.setOnClickListener(myClickListener);

        initLineImage();
//        LayoutInflater inflater = getLayoutInflater();

//
//        myPageAdapter = new MyPageAdapter();
//        myViewPager.setAdapter(myPageAdapter);

    }

    /**
     * 在设计好了tab 和 fragment 在 ViewPager 上面联动之后,我们发现fragment的设计中一般会在tab下面显示一个
     * line,这个接口就是为了初始化这个line,设置其长度和参数
     */
    private void initLineImage() {
        line_tab = (ImageView)findViewById(R.id.iv_line);
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        int screenWidth = dm.widthPixels;
        ViewGroup.LayoutParams lp = line_tab.getLayoutParams();
        lp.width = screenWidth / 3;
        line_tab.setLayoutParams(lp);
        moveOne = lp.width;
    }

    /*
       动态的实现了tab下面 line的显示动画,这个动画是已经被android封装好了的,比较简单
     */
    private void movePositionX(int toPosition, float positionOffset) {
        float curTranslationX = line_tab.getTranslationX();
        float toPositionX = moveOne * toPosition + positionOffset;
        ObjectAnimator animator = ObjectAnimator.ofFloat(line_tab, "translationX", curTranslationX, toPositionX);
        animator.setDuration(500);
        animator.start();
    }

    //让tab 按键和 viewPager 的fragment进行事件的联动
    View.OnClickListener myClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.tv_page1:
                    tab1.setTextColor(Color.BLUE);
                    tab2.setTextColor(Color.BLACK);
                    tab3.setTextColor(Color.BLACK);
                    //
                    myViewPager.setCurrentItem(0);
                    break;
                case R.id.tv_page2:
                    myViewPager.setCurrentItem(1);
                    tab2.setTextColor(Color.BLUE);
                    tab1.setTextColor(Color.BLACK);
                    tab3.setTextColor(Color.BLACK);
                    break;
                case R.id.tv_page3:
                    myViewPager.setCurrentItem(2);
                    tab3.setTextColor(Color.BLUE);
                    tab2.setTextColor(Color.BLACK);
                    tab1.setTextColor(Color.BLACK);
                    break;
            }
        }
    };

    /**
     *  onPageScrolled()/onPageSelected()/onPageScrollStateChanged
     *  这三个函数都是 ViewPager.OnPageChangeListener 继承而来,它们就类似于对于普通view的 handler事件一样,只是
     *  由于ViewPager的特殊性,它具备了三个接口,分别是如下,onPageScrolled()这个是在ViewPager正在滚动时调用
     *  在真机中,这个onPageScrolled()调用非常频繁,不能在里面直接写updateUI的操作,在这里就不详细说明了
     * @param position
     * @param positionOffset
     * @param positionOffsetPixels
     */
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (isScrolling == true) {
            movePositionX(position, positionOffset * moveOne);
        } else if (isFingerLeaved == true) {
            movePositionX(position, 0.0f);
        }
    }

    /**
     * 滚动ViewPager 完成后将调用这个接口,这个接口的作用一般是做滚动操作的最后动作
     * @param position
     */
    @Override
    public void onPageSelected(int position) {
        Log.v(TAG, "onPageSelected position: " + position);
        switch (position) {
            case 0:
                tab1.setTextColor(Color.BLUE);
                tab2.setTextColor(Color.BLACK);
                tab3.setTextColor(Color.BLACK);
                movePositionX(0, 0.0f);
                break;
            case 1:
                tab2.setTextColor(Color.BLUE);
                tab1.setTextColor(Color.BLACK);
                tab3.setTextColor(Color.BLACK);
                movePositionX(1, 0.0f);
                break;
            case 2:
                tab3.setTextColor(Color.BLUE);
                tab2.setTextColor(Color.BLACK);
                tab1.setTextColor(Color.BLACK);
                movePositionX(2, 0.0f);
                break;
        }
    }

    /**
     * 这个接口是用于获取scroll 的状态的,当手指正在drag的过程中,它的状态是SCROLL_STATE_DRAGGING
     * 当滚动的drag动作已经完成了,手离开了view,且View的显示还没有完全切换完,正在走向完成的时候,它
     * 的状态是SCROLL_STATE_SETTLING, 没有任何操作的是是 SCROLL_STATE_IDLE
     * @param state
     */
    @Override
    public void onPageScrollStateChanged(int state) {
        switch (state) {
            case ViewPager.SCROLL_STATE_IDLE:
                isScrolling = false;
                isFingerLeaved = false;
                break;
            case ViewPager.SCROLL_STATE_DRAGGING:
                isScrolling = true;
                isFingerLeaved = false;
                break;
            case ViewPager.SCROLL_STATE_SETTLING:
                isScrolling = false;
                isFingerLeaved = true;
                break;
        }
    }
}

    FragmentPagerAdapter 的实现如下:

    切记: 它需要实现的两个接口只有 getItem(int position),用于返回需要显示的Fragment, getCount()用于显示需要返回的Adapter的大小。

    

public class MyFragmentAdapter extends FragmentPagerAdapter {
    private List<Fragment> fragmentList;

    public MyFragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
        super(fm);
        this.fragmentList = fragmentList;
    }

    @Override
    public Fragment getItem(int position) {
        Log.v("Kyle" , "getItem " + position);
        return fragmentList.get(position);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }


}

上面的代码,还缺少了XML 和 Fragment,其中 Fragment 就是定义了普通的Fragment 创建即可,而主要的XML 也就是main_activity_Layout了,这个如下所示:

<?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">
    <include layout="@layout/tab"/>
    <ImageView
        android:id="@+id/iv_line"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:background="@color/colorblue"/>
    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v4.view.ViewPager>

</LinearLayout>

layout/tab" 如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tab"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    >
    <TextView
        android:id="@+id/tv_page1"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Page1"
        android:textColor="#ff000000"
        android:textSize="16sp"/>
    <TextView
        android:id="@+id/tv_page2"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Page2"
        android:textColor="#ff000000"
        android:textSize="16sp"/>
    <TextView
        android:id="@+id/tv_page3"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Page3"
        android:textColor="#ff000000"
        android:textSize="16sp"/>
</LinearLayout>



 

1.5  FragmentStateAdapter

        FragmentStatePagerAdapter更多用于大量页面,例如视图列表。当某个页面对用户不再可见时,他们的整个fragment就会被销毁,仅保留fragment状态。相比于

        FragmentPagerAdapter,这样做的好处是在访问各个页面时能节约大量的内存开销,但代价是在页面切换时会增加非常多的开销。

        具体实现区别并不太大,只是适用的环境有所变化而已,这里暂时由于时间原因就不详细说明的,等下次回想起来时再来完善

    




    




   
   

 

 

     

 

 

    

 

 

   
     
     

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值