Android ViewPager

珍惜作者劳动成果 转载请注明出处

ViewPager是什么?

实现在一个布局中,能够
进行多个内容界面的切换,
通过手指的滑动,可以让这些界面平滑滚动

ViewPager的使用

  1. ViewPager 是Android Support v4 包中存在的。
  2. ViewPager 是一个UI控件,可以直接在布局中使用
  3. ViewPager 需要Adapter来设置显示的内容。
  4. ViewPager 只能够左右滑动,用来放复杂的界面

PagerAdapter的使用

用于给ViewPager设置可以显示的控件、界面;
继承PagerAdapter,实现规定的方法。
ViewFlipper : 类似于ViewPager但不能够平滑滚动,可以直接放到布局中,在布局内部写控件,和ViewPager不同。
注意事项:1. 对于PagerAdapter直接子类,必须实现 instantiateItem, 以及 destroyItem 两个方法,instantiateItem 方法内部必须手动调用 container.addView,如果加载布局,要使用 类似ListView内部加载的方式

这里写图片描述

ViewPager页面加载的规则

  1. ViewPager当前页,左侧和右侧都已经加载,当向一个方向移动的时候,总是能够确保当前显示页面左侧右侧都存在,因此,每次移动的时候,都会销毁超出去的部分,加载当前页面左右的页面。
  2. 相应的PagerAdapter的调用,就会按照加载的顺序来调用;
  3. 当当前页面显示 0索引的时候,1代表的页面也存在,当页面显示最后一个的时候,倒数第二个也存在。
  4. 因为ViewPager需要平滑的移动左侧或者右侧的内容,所以,预先加载。

getPageTitle(int pos) 获取指定位置的标题

ViewPager复杂界面显示

  1. ViewPager 允许放置Fragment,当Fragment存在,功能可以实现非常复杂,完善的功能;现代Android客户端都会包含ViewPager + Fragment;
  2. ViewPager的Adapter 可以使用FragmentPagerAdapter,以及 FragmentStatePagerAdapter 两个适配器,来进行ViewPager包含Fragment的设置。

FragmentPagerAdapter

  1. 子类实现 getCount() 返回Fragment的总个数;
  2. 子类实现 Fragment getItem(int position) 获取第几个Fragment;
  3. 在Adapter之外创建List集合来存Fragment,这样,传递给Adapter的时候,就不需要判断索引了,直接获取Fragment就行了,由Activity来控制显示什么。
  4. Fragment生命周期管理:类似于PagerAdapter,每一个加载的时候,都会进入到resume()状态,当需要销毁的时候,并不是真正的销毁Fragment,而是进行视图的销毁,因此,每一个Fragment在移除的时候,都是调用到 onDestroyView() 这个状态;再次需要显示的时候,直接onCreateView() 回复状态。
  5. 对于数据的加载,再Fragmnet onCreate 当中进行的话,相当于只执行一次,不论ViewPager如何滑动,也只是执行一次。
  6. 如果需要每次切换回来,进行刷新,那么就不应该在onCreate进行获取。如果在onCreate和 onResume() 中都进行加载,那么也是可以的。 onCreate 用于初始化一次的数据加载。
    1. FragmentPagerAdapter 在切换Fragment的时候,当一个Fragment 需要移出布局的时候,会根据声明周期方法,调用到 onDestroyView,用于释放UI资源;但是不会再继续销毁了,Fragment的数据依然会保留;便于下一次ViewPager再显示这个Fragment的时候,直接恢复UI就可以了;
  7. 应用场景:通常都是应用程序最外层的Tab的管理,用于固定数量的、数据不需要大量刷新的场景;也就是软件主界面的Tab管理;

FragmentStatePagerAdapter

  1. FragmentStatePagerAdapter 作用:同样是在ViewPager中加载Fragment;
  2. FragmentStatePagerAdapter 在进行Fragment切换的时候,每次移除Fragment 的时候,都会将Fragment进行销毁,直接调用到生命周期方法: onDetach(),
    每次重新加载Fragment ,都会进行 onAttach() -> onCreate() -> onCreateView() -> onActivityCreated()
    每次销毁之前,都会调用Fragment的 onSaveInstanceState(), 每次加载Fragment的时候,都会自动的进行onCreate(Bundle),onCreateView(…,Bundle),参数中的Bundle都会包含之前 saveInstanceState 保存的数据,所以,如果需要保存用户动态数据,onSaveInstanceState() 保存数据, onCreateView() 重新从Bundle参数加载数据。

Fragment的参数传递

  1. Fragment 在 new之后,可以进行 setArguments的设置,设置参数,参数类型为 Bundle
  2. Fragment 不要创建 带有参数的构造方法,因为Fragment在销毁之后 ViewPager 如果再次加载的时候,参数就没有了
  3. 当使用setArguments(Bundle) 之后,在任意的Fragment生命周期中,都可以通过 getArguments() 来获取参数;
  4. 当Fragment 销毁之后再次加载,同样能够取到参数,通过这种方式来支持 ViewPager item的销毁和加载。

两个FragmentAdapter的区别

  1. FragmentPagerAdapter 会在切换的时候,会进行onDestroyView 的调用
  2. FragmentStatePagerAdapter,用于加载大量的Fragment用的,会减少一些内存问题,速度会慢一些;
  3. 对于每次销毁Fragment,再onCreate 这种,从运行效率上面,会慢一些;但是内存占用小一些。
  4. 官方的说明:
    FragmentPagerAdapter 用于数量是固定的,并且少量的Fragment显示的时候,来使用。应用场景:应用程序的模块划分;
    FragmentStatePagerAdapter: 用于数量多的,数量变化的Fragment显示;应用场景:
    支持 notifyDataSetChanged()

ViewPager对于onResume影响

  1. 默认情况下,当前ViewPager如果显示 0 位置的,那么 1位置的Fragment 也会 onResume,如果进行进行网络加载,那么会同时加载两个Fragment的数据。
  2. 默认情况下,可以同时有3个Fragment会进入onResume的状态;

实例代码

PicturePagerAdapter的写法


/**
 * Created by Lulu on 2016/9/14.
 * 1. 必须要重写/实现 四个方法; 不能够只有两个!!!!!
 */
public class PicturePageAdapter extends PagerAdapter {

    private static final String TAG = PicturePageAdapter.class.getSimpleName();
    private Context mContext;

    private List<Integer> mImageRes;

    private View.OnClickListener mOnClickListener;

    public void setOnClickListener(View.OnClickListener onClickListener){
        mOnClickListener = onClickListener;
    }


    public PicturePageAdapter(Context context, List<Integer> imageRes) {
        mContext = context;
        mImageRes = imageRes;
    }

    /**
     * 返回ViewPager一共有多少项
     */
    @Override
    public int getCount() {
        int ret = 0;
        if (mImageRes != null) {
            ret = mImageRes.size();
        }
        Log.d(TAG, "getCount: ");
        return ret;
    }

    /**
     * View 参数 是否 和 Object 有关系
     * <p>
     * ViewPager 会先进行对象的管理, 利用对象来管理和定位View
     * (通俗的讲: 看view和Object是否对应)
     *
     * @param view
     * @param object : 这个参数就是 instantiateItem 方法的返回值对象!!!!
     * @return
     */
    @Override
    public boolean isViewFromObject(View view, Object object) {
        Log.d(TAG, "isViewFromObject: ");
        return view == object;
    }

    /**
     * 当ViewPager需要显示一页的时候, 会调用这个方法, 传递指定的页数
     *
     * @param container
     * @param position
     * @return
     */
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Log.d(TAG, "instantiateItem: ");

        // 加载布局或者创建控件, 然后添加到 ViewGroup
        View ret = null;
        if (position == mImageRes.size() - 1) {
            //最后一个
            ret = LayoutInflater.from(mContext).inflate(R.layout.pic_last, container, false);
            ImageView imageView = (ImageView) ret.findViewById(R.id.main_pager);
            imageView.setImageResource(android.R.drawable.bottom_bar);
            Button btn = (Button) ret.findViewById(R.id.btn_jump_next);
            btn.setOnClickListener(mOnClickListener);
        } else {

            ImageView imageView = new ImageView(mContext);
            imageView.setImageResource(mImageRes.get(position));
            ret = imageView;
            //   !!!必须把创建/加载的视图, 手动添加到ViewGroup中!!!

        }
        container.addView(ret);


        return ret;

    }

    /**
     * 当ViewPager把一个页面左右移动的时候, 达到一个限制的位置之后, 就会删除这个页面
     * 调用这个方法
     *
     * @param container
     * @param position
     * @param object
     */
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {

        Log.d(TAG, "destroyItem: ");
        //必须手动的把创建的View, 从ViewGroup删除
        container.removeView((View) object);

    }
}

MainActivity.java


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewPager viewPager = (ViewPager) findViewById(R.id.main_pager);

        List<Integer> ids = new ArrayList<>();
        ids.add(R.mipmap.ic_launcher);
        ids.add(android.R.drawable.ic_dialog_alert);
        ids.add(android.R.drawable.btn_star);

        PicturePageAdapter adapter = new PicturePageAdapter(this, ids);

        //设置预加载页数,
        //设置2代表 当页面索引为2的时候, 当前页面索引保留两个, 右侧保留两个
//        viewPager.setOffscreenPageLimit(4);
        adapter.setOnClickListener(this);

        viewPager.setAdapter(adapter);
    }

通用FragmentAdapter的写法和使用


/**
 * Created by Lulu on 2016/9/14.
 */
public class CommonFragmentPagerAdapter extends FragmentPagerAdapter{
    private static final String TAG = CommonFragmentPagerAdapter.class.getSimpleName();
    private List<BaseFragment> mFragments;

    public CommonFragmentPagerAdapter(FragmentManager fm, List<BaseFragment> fragments) {
        super(fm);
        mFragments = fragments;
    }
    /**
     * 返回指定页码的 Fragment , 用于显示
     * @param position
     * @return
     */
    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }
    /**
     * 返回ViewPager的Fragment的个数
     * @return
     */
    @Override
    public int getCount() {
        int ret = 0;
        if (mFragments != null) {
            ret = mFragments.size();
        }
        Log.d(TAG, "getCount: " + ret);
        return ret;
    }


    @Override
    public CharSequence getPageTitle(int position) {
        return mFragments.get(position).getFragmentTitle();
    }
}

JDActivity.java



/**
 * 使用ViewPager 显示 Fragment
 * <p/>
 * 在onResume中不要有什么提示操作
 */
public class JDActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener, ViewPager.OnPageChangeListener {

    private ViewPager mPager;
    private RadioGroup mTabBar;

    private TextView mIndicator;

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

        mPager = (ViewPager) findViewById(R.id.jb_pager);

        mTabBar = (RadioGroup) findViewById(R.id.pager_tab_bar);

        mIndicator = (TextView) findViewById(R.id.text_indicator);
        List<BaseFragment> fragmentList = new ArrayList<>();
        fragmentList.add(new FirstFragment());
        fragmentList.add(new SecondFragment());
        fragmentList.add(new ThirdFragment());
        fragmentList.add(new FourthFragment());
        //参数1: FragmentManager: 如果在Activity中, 创建的Adapter那么必须使用 getSupportFragment,
        // 如果在Fragment内部, 创建Adapter, 那么必须使用getSupportFragmentManager
        CommonFragmentPagerAdapter adapter = new CommonFragmentPagerAdapter(
                getSupportFragmentManager(),
                fragmentList
        );
        mPager.setAdapter(adapter);

        //ViewPager 与 RadioGroup 联动/
        mTabBar.setOnCheckedChangeListener(this);
        mPager.addOnPageChangeListener(this);


    }

    @Override
    public void onCheckedChanged(RadioGroup group, int checkedId) {
        switch (checkedId) {
            case R.id.pager_tab_item_0:
                mPager.setCurrentItem(0);
                break;
            case R.id.pager_tab_item_1:
                mPager.setCurrentItem(1);
                break;
            case R.id.pager_tab_item_2:
                mPager.setCurrentItem(2);
                break;
            case R.id.pager_tab_item_3:
                mPager.setCurrentItem(3);
        }
    }

    ///ViewPager滚动监听方法///

    /**
     * 当页面在持续滚动的时候, 会自动回调, 能够表示页面滚动的情况
     *
     * @param position
     * @param positionOffset
     * @param positionOffsetPixels
     */
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        //1. 获取RadioGroup中指定的Child, position对应的Child
        View at = mTabBar.getChildAt(position);
        //2. 获取指定指示器控件的坐标
        float x = ViewCompat.getX(at);
        //3. 计算指示器的位置
        //positionOffset 代表一小格的百分比, 也就是当前RadioButton宽度的百分比距离
        x = x + (at.getWidth() * positionOffset);
        ViewCompat.setX(mIndicator, x);
    }

    /**
     * 滚动完成, 切换新页面的时候, 回调
     */
    @Override
    public void onPageSelected(int position) {
        switch (position) {
            case 0:
                mTabBar.check(R.id.pager_tab_item_0);
                break;
            case 1:
                mTabBar.check(R.id.pager_tab_item_1);
                break;
            case 2:
                mTabBar.check(R.id.pager_tab_item_2);
                break;
            case 3:
                mTabBar.check(R.id.pager_tab_item_3);
                break;

        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
    //end
}

使用Google的支持包实现滑动指示器

我们使用的是Google的com.android.support:design:24.1.1

第1种实现方式


public abstract class BaseFragment extends Fragment{
    public BaseFragment(){}
    public abstract String getFragmentTitle();
}

/**
 * Android5.0 以后 TabLayout可以与ViewPager直接联动
 */
public class SettingsActivity extends AppCompatActivity implements TabLayout.OnTabSelectedListener {


    private ViewPager mPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);
        //1. TabLayout 可以使用代码添加多个 Tab
        //2. 可以与ViewPager直接联动

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);

        mPager = (ViewPager) findViewById(R.id.settings_pager);


        // 3.在ViewPager对象初始化之后, TabLayout可以设置一个TabListener
        //  因为TabLayout的第一个Tab, 在添加的时候, 会自动调用Listener


        tabLayout.addOnTabSelectedListener(this);

        /*
        Add a tab to this layout. The tab will be added at the end of the list.
        If this is the first tab to be added it will become the selected tab.
         */

        //创建Tab对象
        TabLayout.Tab tab = tabLayout.newTab();
        tab.setText("First");
        tabLayout.addTab(tab);

        tab = tabLayout.newTab();
        tab.setText("Second");
        tabLayout.addTab(tab);

        tab = tabLayout.newTab();
        tab.setText("Third");
        tabLayout.addTab(tab);

        tab = tabLayout.newTab();
        tab.setText("Fourth");
        tabLayout.addTab(tab);

        List<BaseFragment> fragments = new ArrayList<>();

        fragments.add(new FirstFragment());
        fragments.add(new SecondFragment());
        fragments.add(new ThirdFragment());
        fragments.add(new FourthFragment());
        CommonFragmentPagerAdapter adapter = new CommonFragmentPagerAdapter(
                getSupportFragmentManager(),
                fragments
        );
        mPager.setAdapter(adapter);

        //TabLayout内部实现了一个OnPageChangerListener, 能够实现跟随
        mPager.addOnPageChangeListener(
                new TabLayout.TabLayoutOnPageChangeListener(tabLayout)
        );



    }

    /OnTabSelectedListener

    /**
     * 当Tab从没有选中到选中的状态时, 调用这个方法
     * @param tab
     */
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        //切换
        mPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

    /**
     * 当前Tab已经是选中的情况下, 再次点击这个Tab会回调这个方法
     * @param tab
     */
    @Override
    public void onTabReselected(TabLayout.Tab tab) {
        //刷新数据
    }
    ///OnTabSelectedListener/
}

第2种实现方式


public class NewsActivity extends AppCompatActivity {

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

        //1. TabLayout取出来
        //  ViewPager
        TabLayout tabLayout = (TabLayout) findViewById(R.id.news_tab_layout);
        ViewPager pager = (ViewPager) findViewById(R.id.news_pager);

        //2. ViewPager Adapter设置
        List<BaseFragment> fragments = new ArrayList<>();
        fragments.add(new FirstFragment());
        fragments.add(new SecondFragment());
        fragments.add (new ThirdFragment());
        fragments.add(new FourthFragment());

        CommonFragmentPagerAdapter adapter = new CommonFragmentPagerAdapter(
                getSupportFragmentManager(),
                fragments
        );
        pager.setAdapter(adapter);
        //3. setup
        // setupWithViewPager 要求ViwPager的Adapter必须去实现
        // getrPagerTitle(int position)
        tabLayout.setupWithViewPager(pager);


    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值