ViewPager使用介绍
https://blog.csdn.net/wenzhi20102321/article/details/53584344
ViewPager是个ViewGroup,用于横向分页显示,可以滑动切换页面。如果不需要滑动切换页面可以不用ViewPager。ViewPager必须搭配PagerAdapter使用,PagerAdapter是个抽象类,需要继承实现抽象方法。常用的两个子类是FragmentPagerAdapter和FragmentStatePagerAdapter。当每个Pager都对应一个Fragment时,可以使用这两个子类。具体的用法和注意事项后面再详细介绍。如果每个Pager对应的不是一个Fragment时,需要继承PagerAdapter,实现下面几个方法:
- instantiateItem(ViewGroup, int)
初始化一个Pager,ViewGroup参数是ViewPager本身,int参数是Pager所在的位置。这个方法需要生成一个View,并加入到ViewGroup中。并且需要返回一个与该Pager一一对应的对象,通常可以是该Pager的View对象。 - destroyItem(ViewGroup, int, Object)
销毁一个Pager,ViewGroup指ViewPager本身,int指Pager所在位置,Object指instantiateItem方法所返回的对象,该方法需要将Pager的View从ViewGroup移除; - getCount()
返回Pager的数量; - isViewFromObject(View, Object)
返回View与Object的对应关系,View是在instantiateItem方法中添加的View,Object是该方法返回的对象;
另外,ViewPager还有两个相对重要的方法:
- notifyDataSetChanged()
当页面增加、减少、移动位置时,需要调用此方法。这个方法是否起作用依赖于getItemPosition的返回结果; - getItemPosition(Object)
这个方法默认返回POSITION_UNCHANGED(-1)常量,表示该页的位置没有变化。所以notifyDataSetChanged方法就不会重新加载该页。可以修改这个方法的返回值使得notifyDataSetChanged起作用。可以返回实际位置,或者当Pager被删除后可以返回POSITION_NONE(-2);
另外,一般情况下通过instantiateItem添加的View是没有Id的,这种View在Activity异常后恢复时是不能自动恢复的。
继承PagerAdapter
public class MyPagerAdapter extends PagerAdapter {
private static final String TAG = MyPagerAdapter.class.getSimpleName();
private static final boolean DEBUG = true;
private Context context;
private List<PagerInfo> pagerInfos = new ArrayList<>();
private List<MyPager> pagers = new ArrayList<>();
public MyPagerAdapter(Context context, List<PagerInfo> pagerInfos) {
if (DEBUG) Log.d(TAG, "Constructor, pager count: " + pagerInfos.size());
this .context = context;
this.pagerInfos.addAll(pagerInfos);
for (int i=0; i<pagerInfos.size(); i++) {
pagers.add(null);
}
}
@Override
public int getCount() {
return pagerInfos.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (DEBUG) Log.d(TAG, "instantiateItem, position: " + position);
MyPager pager = pagers.get(position);
if (pager == null) {
pager = (MyPager)LayoutInflater.from(context).inflate(R.layout.view_my_pager, null);
pager.updatePager(pagerInfos.get(position));
pagers.set(position, pager);
}
ViewGroup parent = (ViewGroup)pager.getParent();
if (parent != null) {
parent.removeView(pager);
}
container.addView(pagers.get(position));
return pagers.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (DEBUG) Log.d(TAG, "destroyItem, position: " + position);
container.removeView((MyPager)object);
}
@Override
public int getItemPosition(Object object) {
MyPager pager = (MyPager)object;
if (pagers.contains(pager)) {
return pagers.indexOf(pager);
} else {
return POSITION_NONE;
}
}
public void addPager(PagerInfo pagerInfo) {
if (DEBUG) Log.d(TAG, "addPager, to end, position: " + pagerInfos.size());
pagerInfos.add(pagerInfo);
pagers.add(null);
notifyDataSetChanged();
}
public void addPager(int position, PagerInfo pagerInfo) {
if (DEBUG) Log.d(TAG, "addPager, position: " + position);
pagerInfos.add(position, pagerInfo);
pagers.add(position, null);
notifyDataSetChanged();
}
public void removePager(PagerInfo pagerInfo) {
removePager(pagerInfos.indexOf(pagerInfo));
}
public void removePager(int position) {
if (0 <= position && position < pagerInfos.size()) {
if (DEBUG) Log.d(TAG, "removePager, position: " + position);
pagerInfos.remove(position);
pagers.remove(position);
notifyDataSetChanged();
}
}
public void updatePager(PagerInfo pagerInfo) {
updatePager(pagerInfos.indexOf(pagerInfo));
}
public void updatePager(int position) {
if (0 <= position && position < pagerInfos.size()) {
if (DEBUG) Log.d(TAG, "updatePager, position: " + position);
PagerInfo pagerInfo = pagerInfos.get(position);
pagers.get(position).updatePager(pagerInfo);
}
}
}
当每个Pager都是Fragment时,可以使用FragmentPagerAdapter时。
- getItemId(int)
该方法获取position的Id,默认返回position。参数int是position。Id与position所在的Fragment一一对应,用于在FragmentManager中找到该Fragment。 - getItem()
该方法用来获取position所在位置的Fragment。这个方法被instantiateItem方法调用,一般情况下每个Page只会被调用一次。 - instantiateItem(ViewGroup, int)
由于Fragment的特殊性(受FragmentManager管控,有自己的生命周期),instantiateItem中首先会通过FragmentManager和Id获取position位置的Fragment,在没有获取到的情况才会调用getItem。所以一般情况下,每个Pager只会调用一次getItem方法。
当页的数量和位置都不改变时可以用Fragment,否则不建议使用。有两点原因:
- 当页数量或位置发生变化时,一般调用PagerAdapter.notifyDataSetChanged方法,从而触发ViewPager.dataSetChanged回调。在回调中调用Adapter.getItemPosition方法来检查ViewPager保存的所有页对象的位置是否发生变化。所以如果要使Adapter.notifyDataSetChanged方法起作用,一定要重载Adapter.getItemPosition返回实际位置。如果Pager对象位置没有变化,则ViewPager不做处理;如果发生变化,则记录新位置;如果Pager对象不存在了(POSITION_NONE),则调用PagerAdaper.detach。为了使getItemPosition返回的结果准确,Adapter中必须记录所有的Fragment。
- 在FragmentPagerAdapter.instantiateItem方法中,首先通过getItemId和FragmentManager查找当前Pager的Fragment。由于getItemId默认返回position,所以会找到以前的Fragment。如果需要动态更新Pager,getItemId不能返回position,必须和特定的Fragment绑定。Adpater需要记录所有Fragment的Id,当Pager的数量或位置改变时,对应的Id也需要改变。当Activity异常销毁后恢复时,Fragment也会恢复。对应Fragment的Id也必须能恢复。所以所以异常时需要保存所有Fragment的Id。
下面是一个例子:
继承FragmentPagerAdapter
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
private static final String TAG = MyFragmentPagerAdapter.class.getSimpleName();
private static final boolean DEBUG = true;
private final String PARAM_ADAPTER = "adapter";
private final String PARAM_IDS = "ids";
private final String PARAM_ID = "id";
private ArrayList<FragmentPagerInfo> pagerInfos = new ArrayList<>();
private List<MyFragment> fragments = new ArrayList<>();
private ArrayList<Integer> ids = new ArrayList<>();
private int id;
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
public MyFragmentPagerAdapter(FragmentManager fm, List<FragmentPagerInfo> pagerInfos) {
this(fm);
this.pagerInfos.addAll(pagerInfos);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (DEBUG) Log.d(TAG, "instantiateItem, position: " + position);
MyFragment fragment = (MyFragment)super.instantiateItem(container, position);
if (!fragments.contains(fragment)) {
while(fragments.size() <= position) {
fragments.add(null);
}
if (DEBUG) Log.d(TAG, "instantiateItem, Stored fragment: " + fragment);
fragments.set(position, fragment);
}
return fragment;
}
@Override
public Fragment getItem(int i) {
if (DEBUG) Log.d(TAG, "getItem, position: " + i);
while (fragments.size() <= i) {
fragments.add(null);
}
if (fragments.get(i) == null) {
if (DEBUG) Log.d(TAG, "getItem, create new fragment");
fragments.set(i,MyFragment.newInstance(pagerInfos.get(i)));
}
return fragments.get(i);
}
@Override
public long getItemId(int position) {
while(ids.size() <= position){
ids.add(null);
}
if (ids.get(position) == null) {
ids.set(position, id++);
}
if (DEBUG) Log.d(TAG, "getItemId, position:" + position + ", id: " + ids.get(position));
return ids.get(position);
}
@Override
public int getItemPosition(Object object) {
int position;
if (fragments.contains(object)) {
position = fragments.indexOf(object);
} else {
position = POSITION_NONE;
}
if (DEBUG) Log.d(TAG, "getItemPosition, position: " + position);
return position;
}
@Override
public int getCount() {
return pagerInfos.size();
}
public void onSaveInstanceState(Bundle outState) {
if (DEBUG) Log.d(TAG, "onSaveInstanceState");
Bundle b = new Bundle();
b.putIntegerArrayList(PARAM_IDS, ids);
b.putInt(PARAM_ID, id);
outState.putBundle(PARAM_ADAPTER, b);
}
public void onRestoreSavedInstance(Bundle savedInstanceState) {
if (DEBUG) Log.d(TAG, "onRestoreSavedInstance");
Bundle b = savedInstanceState.getBundle(PARAM_ADAPTER);
if (b!=null) {
ids.clear();
ids.addAll(b.getIntegerArrayList(PARAM_IDS));
id = b.getInt(PARAM_ID);
}
}
public void addPager(int position, FragmentPagerInfo pagerInfo) {
if (DEBUG) Log.d(TAG, "addPager, position: " + position);
pagerInfos.add(position, pagerInfo);
fragments.add(position, null);
ids.add(position, null);
notifyDataSetChanged();
}
public void addPager(FragmentPagerInfo pagerInfo) {
addPager(pagerInfos.size(), pagerInfo);
}
public void removePager(int position) {
if (DEBUG) Log.d(TAG, "removePager, position: " + position);
if (0 <= position && position < pagerInfos.size()) {
pagerInfos.remove(position);
fragments.remove(position);
ids.remove(position);
notifyDataSetChanged();
}
}
public void removePager(PagerInfo info) {
removePager(pagerInfos.indexOf(info));
}
public void updatePager(int position) {
if (DEBUG) Log.d(TAG, "updatePager, position: " + position);
if (0 <= position && position < pagerInfos.size()) {
MyFragment fragment = fragments.get(position);
if (fragment != null) {
fragment.updateView(pagerInfos.get(position));
}
}
}
public void updatePager(FragmentPagerInfo info) {
int position = pagerInfos.indexOf(info);
updatePager(position);
}
}
FragmentStatePagerAdapte和FragmentPagerAdapter的区别
FragmentStatePagerAdapter最多保存三个页面,超出范围的页面会被remove。remove的Fragment不受FragmentManager管控。当页面非常多时,FragmentStatePagerAdapter可以有效降低内存的消耗,付出的代价就是每次切换页面要更多的操作,也就是用时间换取空间。但是对于页面位置或数量的改变与FragmentPagerAdapter有相同的问题。