今天我们来一起实现“爱阅”首页滑动切换分类浏览阅读的效果,并将ViewPager和TabLayout结合起来用以实现顶部导航栏的分类展示,并增加点击快速切换分类的功能。ViewPager+TabLayout也是当前最炙手可热的组合方式,我会首先对基础的知识点做一些讲解,然后对我们今天的内容进行实现。
首先看一下效果图:
点此进入目录:[干货] 十天 教你从创意到上线APP
1、ViewPager简介
ViewPager是android扩展包v4包中的类,这个类可以让用户左右切换当前的view
- ViewPager类直接继承了ViewGroup类,所以它是一个容器类,可以在其中添加其他的view类。
- ViewPager类需要一个PagerAdapter适配器类给它提供数据。
- ViewPager经常和Fragment一起使用,并且提供了专门的FragmentPagerAdapter和FragmentStatePagerAdapter类供Fragment中的ViewPager使用。
2、ViewPager的适配器
上文的简介中提到了PagerAdapter,其实和RecyclerView、ListView等控件使用一样,ViewPager需要设置PagerAdapter来完成页面和数据的绑定,这个PagerAdapter是一个基类适配器,我们经常用它来实现App引导图,它的子类有FragmentPagerAdapter和FragmentStatePagerAdapter,这两个子类适配器用于和Fragment一起使用,在安卓应用中它们就像RecyclerView一样出现的频繁。
(1)实现一个最基本的PagerAdapter
public class AdapterViewpager extends PagerAdapter {
private List<View> mViewList;
public AdapterViewpager(List<View> mViewList) {
this.mViewList = mViewList;
}
@Override
public int getCount() {
//必须实现
return mViewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
//必须实现
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
//必须实现,实例化
container.addView(mViewList.get(position));
return mViewList.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
//必须实现,销毁
container.removeView(mViewList.get(position));
}
}
instantiateItem()
可以看到instantiateItem()做了两件事,第一:将当前视图添加到container中,第二:返回当前View。也就是说instantiateItem()的功能是创建指定位置的页面视图,并且适配器有责任增加即将创建的View视图添加到这里给定的container中。它的返回值代表新增视图页面的Object(Key),这里没必要非要返回视图本身,也可以返回可以代表当前页面的任意值,只要你可以与你增加的View一一对应即可,比如position变量也可以做为Key。isViewFromObject()
该函数用来判断 instantiateItem() 函数所返回来的 Key 与一个页面视图是否是代表的同一个视图(即它俩是否是对应的,对应的表示同一个 View),如果对应的是同一个View回 true,否则返回 false。destroyItem()
该方法的功能是移除一个给定位置的页面,适配器有责任从容器中删除这个视图,这是为了确保在 finishUpdate(viewGroup) 返回时视图能够被移除。
不过说道destroyItem()这个方法的时候就不得不提及ViewPager的刷新问题了,因为ViewPager的刷新并不是我们最初想的调用一下notifyDataSetChanged()就完事了这么简单的,我们会在后文说明“爱阅”中遇到的坑和解决办法。
(2)实现一个最基本的FragmentPagerAdapter
/**
* Created by : WGH.
*/
public class ViewPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Category> mCategoryList;
private Context mContext;
private Fragment mFragment;
public ViewPagerAdapter(FragmentManager fm, Context context) {
super(fm);
mContext = context;
mCategoryList = DataCacheHelper.getInstance().getPagerChildCategories();
}
public void onCategorysChange(String key) {
if (key != null) {
mCategoryList = DataCache.getInstance().getChildCategorys(key);
notifyDataSetChanged();
} else {
DLog.e("onCategorysChange() Error!");
}
}
@Override
public Fragment getItem(int position) {
// 必须实现
return ViewPagerFragment.newInstance(mContext, mCategoryList.get(position));
}
@Override
public int getCount() {
// 必须实现
if (mCategoryList != null) {
return mCategoryList.size();
} else {
return 0;
}
}
@Override
public CharSequence getPageTitle(int position) {
// 选择性实现
return mCategoryList.get(position).getName();
}
public String getFragmentTag(int viewPagerId, int fragmentPosition) {
return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}
public String getFragmentTag(int viewPagerId, int fragmentPosition) {
return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
FragmentStatePagerAdapter的实现和FragmentPagerAdapter的实现一样就不在写了。而这里的getItemPosition()之所以这样写同样和ViewPager的刷新有关,我们后文讲解。
三个适配器的区别:
PagerAdapter是基类适配器是一个通用的ViewPager适配器,相比PagerAdapter,FragmentPagerAdapter和FragmentStatePagerAdapter更专注于每一页是Fragment的情况,而这两个子类适配器使用情况也是有区别的。FragmentPagerAdapter适用于页面比较少的情况,FragmentStatePagerAdapter适用于页面比较多的情况,我们可以从两个适配器的源码得知。
- FragmentStatePagerAdapter
@Override
public Object instantiateItem(ViewGroup container,