您的位置:首页 > 移动开发 > Android开发

Android入门--ViewPager使用总结

2015-04-26 15:35 323 查看
ViewPager,是在Android3.0之后新增的API,可以用于导航栏,页面切换等控件,其主要功能是使视图可以左右滑动,使用时注意需要导入android-support-v4.jar包,如果创建的项目是3.0之后的,系统会自动导包,就不需要费心。当然,如果遇到导包问题,也不要着急,下面是解决办法:先检查项目中是否存在android-support-v4.jar包,如果不存在,就检查sdk中是否存在v4的包,路径为 \sdk\extras\android\support\v4 ,如果存在android-support-v4.jar文件,直接复制到项目下的 lib文件夹中,然后选中点击右键–>Build Path–>Add to Build Path。到此,按理说应该导包完成了,当然,如果还有特殊情况,再特殊考虑

下面是对ViewPager的基本理解:

1)ViewPager类直接继承了ViewGroup类,所有它是一个容器类,可以在其中添加其他的view类。

2)ViewPager类需要一个PagerAdapter适配器类给它提供数据。

3)ViewPager经常和Fragment一起使用,并且提供了专门的FragmentPagerAdapter和FragmentStatePagerAdapter类供Fragment中的ViewPager使用。

在使用ViewPager的时候,还需要使用两个组件类分别是PagerTitleStrip类和PagerTabStrip类,PagerTitleStrip类直接继承自ViewGroup类,而PagerTabStrip类继承PagerTitleStrip类,所以这两个类也是容器类。但是有一点需要注意,在定义XML的layout的时候,这两个类必须是ViewPager标签的子标签,不然会出错

总结以上,使用ViewPager大致分为以下几步:

1.在想添加ViewPager控件的布局文件中添加ViewPager控件。

2.在Activity中实例化ViewPager控件。

3.创建Adapter,并设置ViewPager的Adapter。

以上,就是一个最简单的ViewPager的使用方法,其中的重点是第3点,也就是创建PagerAdapter和设置ViewPager的Adapter

接下来主要介绍ViewPager类和其中常用的方法,以及三种不同的适配器的使用,包括

PagerAdapter :简单的适配器

FragmentPagerAdapter:静态的适配器,

FragmentStatePagerAdapter:动态的适配器

以下的资料引用自网络

ViewPager:如其名所述,是负责翻页的一个 View。准确说是一个 ViewGroup,包含多个 View 页,在手指横向滑动屏幕时,其负责对 View 进行切换。为了生成这些 View 页,需要提供一个 PagerAdapter 来进行和数据绑定以及生成最终的 View 页

setAdapter(): ViewPager 通过 setAdapter() 来建立与 PagerAdapter 的联系。这个联系是双向的,一方面,ViewPager 会拥有 PagerAdapter 对象,从而可以在需要时调用 PagerAdapter 的方法;另一方面,ViewPager 会在 setAdapter() 中调用 PagerAdapter 的 registerDataSetObserver() 方法,注册一个自己生成的 PagerObserver 对象,从而在 PagerAdapter 有所需要时(如 notifyDataSetChanged()或 notifyDataSetInvalidated() 时),可以调用 Observer 的 onChanged() 或 onInvalidated() 方法,从而实现 PagerAdapter 向 ViewPager 方向发送信息。

dataSetChanged(): 在 PagerObserver.onChanged(),以及 PagerObserver.onInvalide() 中被调用。因此当 PagerAdapter.notifyDataSetChanged() 被触发时,ViewPager.dataSetChanged() 也可以被触发。该函数将使用 getItemPosition() 的返回值来进行判断,如果为 POSITION_UNCHANGED,则

什么都不做;如果为 POSITION_NONE,则调用 PagerAdapter.destroyItem() 来去掉该对象,并设置为需要刷新 (needPopulate = true) 以便触发PagerAdapter.instantiateItem() 来生成新的对象

PagerAdapter: PageAdapter 是 ViewPager 的适配器,ViewPager 将调用它来取得所需显示的页,而 PageAdapter 也会在数据变化时,通知 ViewPager。这个类也是FragmentPagerAdapter 以及 FragmentStatePagerAdapter 的基类。如果继承自该类,至少需要实现 instantiateItem(), destroyItem(), getCount() 以及 isViewFromObject()

getItemPosition(): 该方法用以返回给定对象的位置,给定对象是由 instantiateItem() 的返回值。在 ViewPager.dataSetChanged() 中将对该方法的返回值进行判断,以决定是否最终触发 PagerAdapter.instantiateItem() 方法。在 PagerAdapter 中的实现是直接传回 POSITION_UNCHANGED。如果该方法不被重载,则会一直返回 POSITION_UNCHANGED,从而导致 ViewPager.dataSetChanged() 被调用时,认为不必触发 PagerAdapter.instantiateItem()。很多人因为没有重载该方法,而导致调用PagerAdapter.notifyDataSetChanged() 后,什么都没有发生。

instantiateItem(): 在每次 ViewPager 需要一个用以显示的 Object 的时候,该方法都会被 ViewPager.addNewItem() 调用

notifyDataSetChanged(): 在数据集发生变化的时候,一般 Activity 会调用 PagerAdapter.notifyDataSetChanged(),以通知 PagerAdapter,而 PagerAdapter 则会通知在自己这里注册过的所有 DataSetObserver。其中之一就是在 ViewPager.setAdapter() 中注册过的 PageObserver。PageObserver 则进而调用ViewPager.dataSetChanged(),从而导致 ViewPager 开始触发更新其内含 View 的操作。

FragmentPagerAdapter :FragmentPagerAdapter 继承自 PagerAdapter。相比通用的 PagerAdapter,该类更专注于每一页均为 Fragment 的情况。如文档所述,该类内的每一个生成的 Fragment 都将保存在内存之中,因此适用于那些相对静态的页,数量也比较少的那种;如果需要处理有很多页,并且数据动态性较大、占用内存较多的情况,应该使用FragmentStatePagerAdapter。FragmentPagerAdapter 重载实现了几个必须的函数,因此来自 PagerAdapter 的函数,我们只需要实现 getCount(),即可。且,由于 FragmentPagerAdapter.instantiateItem() 的实现中,调用了一个新增的虚函数 getItem(),因此,我们还至少需要实现一个 getItem()。因此,总体上来说,相对于继承自 PagerAdapter,更方便一些。

getItem(): 如果需要向 Fragment 对象传递相对静态的数据时,我们一般通过 Fragment.setArguments() 来进行,这部分代码应当放到 getItem()。它们只会在新生成 Fragment 对象时执行一遍。

如果需要在生成 Fragment 对象后,将数据集里面一些动态的数据传递给该 Fragment,那么,这部分代码不适合放到 getItem() 中。因为当数据集发生变化时,往往对应的 Fragment 已经生成,如果传递数据部分代码放到了 getItem() 中,这部分代码将不会被调用。这也是为什么很多人发现调用 PagerAdapter.notifyDataSetChanged() 后,getItem() 没有被调用的一个原因。

instantiateItem(): 方法中判断一下要生成的 Fragment 是否已经生成过了,如果生成过了,就使用旧的,旧的将被 Fragment.attach();如果没有,就调用 getItem() 生成一个新的,新的对象将被 FragmentTransation.add()。

FragmentPagerAdapter 会将所有生成的 Fragment 对象通过 FragmentManager 保存起来备用,以后需要该 Fragment 时,都会从 FragmentManager 读取,而不会再次调用 getItem() 方法。

如果需要在生成 Fragment 对象后,将数据集中的一些数据传递给该 Fragment,这部分代码应该放到这个方法的重载里。在我们继承的子类中,重载该方法,并调用 FragmentPagerAdapter.instantiateItem() 取得该方法返回 Fragment 对象,然后,我们该 Fragment 对象中对应的方法,将数据传递过去,然后返回该对象。

否则,如果将这部分传递数据的代码放到 getItem()中,在 PagerAdapter.notifyDataSetChanged() 后,这部分数据设置代码将不会被调用。

destroyItem(): 该方法被调用后,会对 Fragment 进行 FragmentTransaction.detach()。这里不是 remove(),只是 detach(),因此 Fragment 还在 FragmentManager 管理中,Fragment 所占用的资源不会被释放。

FragmentStatePagerAdapter :FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 一样,是继承子 PagerAdapter。但是,和 FragmentPagerAdapter 不一样的是,正如其类名中的 ‘State’ 所表明的含义一样,该 PagerAdapter 的实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源;而在页面需要显示时,生成新的页面(就像 ListView 的实现一样)。这么实现的好处就是当拥有大量的页面时,不必在内存中占用大量的内存。

getItem(): Fragment.setArguments() 这种只会在新建 Fragment 时执行一次的参数传递代码,可以放在这里。

由于 FragmentStatePagerAdapter.instantiateItem() 在大多数情况下,都将调用 getItem() 来生成新的对象,因此如果在该方法中放置与数据集相关的 setter 代码,基本上都可以在 instantiateItem() 被调用时执行,但这和设计意图不符。毕竟还有部分可能是不会调用 getItem() 的。因此这部分代码应该放到 instantiateItem() 中。

instantiateItem(): 除非碰到 FragmentManager 刚好从 SavedState 中恢复了对应的 Fragment 的情况外,该方法将会调用 getItem() 方法,生成新的 Fragment 对象。新的对象将被 FragmentTransaction.add()。

FragmentStatePagerAdapter 就是通过这种方式,每次都创建一个新的 Fragment,而在不用后就立刻释放其资源,来达到节省内存占用的目的的。

destroyItem(): 将 Fragment 移除,即调用 FragmentTransaction.remove(),并释放其资源。

以上,介绍了ViewPager和其适配器的概念和一些常用的方法,下面通过实例来说明其具体的使用

1. PagerAdapter实现ViewPagerView的使用:

activity_main.xml中就是一个简单的ViewPager控件

[code]<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=".MainActivity" >

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>


view_0.xml, view_1.xml, view_2.xml, view_3.xml 这四个view界面的布局是一样的,这里就不重复了

[code]<?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:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_View1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="50sp" />

</LinearLayout>


MainActivity.java 这里为了简便,我直接new了一个PagerAdapter类的对象,没有单独写成一个类文件来继承PagerAdapter,然后设置Adapter就行了

[code]package com.example.viewpagertest;

import java.util.ArrayList;

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;
import android.widget.TextView;
import android.app.Activity;

public class MainActivity extends Activity {
    private ViewPager mViewPager = null;

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

        // 初始化ViewPager控件
        mViewPager = (ViewPager) findViewById(R.id.viewPager);

        // 初始化视图
        LayoutInflater mLi = LayoutInflater.from(this);
        View view_0 = mLi.inflate(R.layout.view_0, null);
        View view_1 = mLi.inflate(R.layout.view_1, null);
        View view_2 = mLi.inflate(R.layout.view_2, null);
        View view_3 = mLi.inflate(R.layout.view_3, null);

        // 将视图放到一个List列表中,方便存取
        final ArrayList<View> views = new ArrayList<View>();
        views.add(view_0);
        views.add(view_1);
        views.add(view_2);
        views.add(view_3);

        // 创建PagerAdapter类的对象
        PagerAdapter mAdaper = new PagerAdapter() {

            // 返回页面数量
            @Override
            public int getCount() {

                return views.size();
            }

            // 实例化一个页面,这里是主要的方法,是用来将View绑定到页面的方法,并对View中的控件进行设置
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                System.out.println("-->instantiateItem " + position);

                container.addView(views.get(position));

                TextView tv_view = null;

                // 根据滑动到当前页面的位置,获取到对应View中的控件,并对控件进行设置
                switch (position) {
                case 0:
                    tv_view = (TextView) views.get(position).findViewById(
                            R.id.tv_View0);
                    tv_view.setText("这是页面" + position);
                    break;
                case 1:
                    tv_view = (TextView) views.get(position).findViewById(
                            R.id.tv_View1);
                    tv_view.setText("这是页面" + position);
                    break;
                case 2:
                    tv_view = (TextView) views.get(position).findViewById(
                            R.id.tv_View2);
                    tv_view.setText("这是页面" + position);
                    break;
                case 3:
                    tv_view = (TextView) views.get(position).findViewById(
                            R.id.tv_View3);
                    tv_view.setText("这是页面" + position);
                    break;

                default:
                    break;
                }
                return views.get(position);
            }

            // 判断View是否来自Object
            @Override
            public boolean isViewFromObject(View arg0, Object arg1) {
                return arg0 == arg1;
            }

            // 销毁一个页面
            @Override
            public void destroyItem(ViewGroup container, int position,
                    Object object) {
                System.out.println("-->destroyItem " + position);
                container.removeView(views.get(position));
            }
        };

        // 最后,调用mViewPager的setAdapter方法来适配
        mViewPager.setAdapter(mAdaper);

    }

}


运行效果:






下面是logcat:



通过logcat能看到,当打开App时,初相的界面如图一所示,显示”这是页面 0” ,此时,如果当我向右滑动,将会出现“这是页面 1”,此时logcat弹出第一条信息,也就是初始化页面“2”,简单理解下就是当我滑动到下一个页面的时候,下下个一面已经在初始化了。然后当我向左滑的时候,会出现第二条logcat,也就是页面“2”被销毁了,理解就是,这是一种动态添加View的机制,页面的准备与销毁是在当前页面的前后各一个

2.FragmentPagerAdapter实现ViewPagerFragment的使用

使用FragmentPagerAdapter,是静态的加载Fragment,适用于页面较少的情况,所以会先定义好各个Fragment和其对应的View,然后通过一个Fragment的List集合来统一加载页面

activity_main.xml 这是主页显示的View布局文件,其中依然只是一个ViewPager控件

[code]<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=".MainActivity" >

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>


fragment_view_0.xml,,fragment_view_1.xml,,fragment_view_2.xml, fragment_view_3.xml 布局大致相同,这里就只列出一个:

[code]<?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:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tv_fragment_0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp" />

</LinearLayout>


Fragment_0.java,Fragment_1.java,Fragment_2.java,Fragment_3.java内容大致相同,这里就不重复了:

[code]package com.example.testviewpager;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class Fragment_0 extends Fragment {

    // 当Fragment_0被创建时调用
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        System.out.println("-->Fragment_0 onCreate");
    }

    // 当Activity中的onCreate()方法执行完调用
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        System.out.println("-->Fragment_0 onActivityCreated");
    }

    // 给当前的Fragment绘制UI布局
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        System.out.println("-->Fragment_0 onCreateView");

        View view = inflater.inflate(R.layout.fragment_view_0, null);
        TextView tv_fragment_0 = (TextView) view
                .findViewById(R.id.tv_fragment_0);
        tv_fragment_0.setText("这是Fragment 0");
        return view;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        System.out.println("-->Fragment_0 onDestroyView");
    }

    @Override
    public void startActivity(Intent intent) {
        super.startActivity(intent);
        System.out.println("-->Fragment_0 startActivity");
    }

}


MainActivity.java

[code]package com.example.testviewpager;

import java.util.ArrayList;

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

public class MainActivity extends FragmentActivity {

    // 定义适配器
    private pageAdapter mAdapter = null;
    // 定义ViewPage控件
    private ViewPager mPager = null;

    // 申明四个Fragment页面
    private Fragment_0 fragment_0 = null;
    private Fragment_1 fragment_1 = null;
    private Fragment_2 fragment_2 = null;
    private Fragment_3 fragment_3 = null;

    // 申明一个用于保存Fragment集合的List
    private ArrayList<Fragment> fragmentList = null;

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

        // 初始化ViewPager控件
        mPager = (ViewPager) findViewById(R.id.viewPager);

        // 初始化Fragment页面
        initFragment();

        // 初始化适配器
        mAdapter = new pageAdapter(getSupportFragmentManager(), fragmentList);

        // 设置适配器
        mPager.setAdapter(mAdapter);
    }

    public void initFragment() {
        fragmentList = new ArrayList<Fragment>();

        fragment_0 = new Fragment_0();
        fragment_1 = new Fragment_1();
        fragment_2 = new Fragment_2();
        fragment_3 = new Fragment_3();

        fragmentList.add(fragment_0);
        fragmentList.add(fragment_1);
        fragmentList.add(fragment_2);
        fragmentList.add(fragment_3);
    }

}


FragmentPagerAdapter.java

[code]package com.example.testviewpager;

import java.util.ArrayList;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class pageAdapter extends FragmentPagerAdapter {

    private ArrayList<Fragment> fragmentList = null;

    // 构造方法,传入的参数是一个FragmentManager类型
    public pageAdapter(FragmentManager fm, ArrayList<Fragment> fragmentList) {
        super(fm);
        this.fragmentList = fragmentList;
    }

    // 获取ViewPager的数量
    @Override
    public int getCount() {
        return fragmentList.size();
    }

    // 获取ViewPager中指定的Item,这里返回的是Fragment类型,可以看出ViewPager中嵌套的是Fragment
    @Override
    public Fragment getItem(int arg0) {
        return fragmentList.get(arg0);
    }

}


下面是运行效果:





下面是logcat:









未完待续
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: