工作中用到了
FragmentPagerAdapter
,所以先看看
FragmentPagerAdapter
的实现。
本文写得比较乱,仅仅是个人笔记,很多地方本人也没弄清楚
public abstract class FragmentPagerAdapter extends PagerAdapter
public abstract class FragmentPagerAdapter extends PagerAdapter
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
实际上 只是在
instantiateItem 中 调用了
getItem(position); 这个抽象方法。 其他方法这个adapter 已经基本实现,所以user使用fpadapter的时候 实现getItem方法 就 是给 instantiateItem返回 fragment了。
然而 返回的是一个fragment,并不是view,且PagerAdapter中
instantiateItem 要求的只是一个object。 那么ViewPager如何处理这个object?
ItemInfo addNewItem(int position, int index) {
ItemInfo ii = new ItemInfo();
ii.position = position;
ii.object = mAdapter.instantiateItem(this, position);
ii.widthFactor = mAdapter.getPageWidth(position);
if (index < 0 || index >= mItems.size()) {
mItems.add(ii);
} else {
mItems.add(index, ii);
}
return ii;
}
可见会在addNewItem中 将 这个 object封装 成 一个 iteminfo。
private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();
同时将这个info 放在了一个arraylist中、
正在使用 MyPagerAdapter extends PagerAdapter 的情况下,instantiateItem方法
正在使用 MyPagerAdapter extends PagerAdapter 的情况下,instantiateItem方法
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = View.inflate(MainActivity.this, R.layout.adapter_ad, null);
ImageView imageView = (ImageView) view.findViewById(R.id.image);
imageView.setBackgroundResource(list.get(position%list.size()).getIconResId());
//将view对象添加到viewpager,交给它管理
container.addView(view);
return view;
}
实际上每次返回的view 都要user手动 添加到viewpager中, destory的时候 要手动remove。 保证 viewpager 缓存pager 数目固定 ,与viewpager内部的items数组 数目应该也保持一致。
猜测fragmentpageradapter应该是在下面某句话中 将fragment的视图addview到了viewpager中? 望指正。
if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment); } else { fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); }addNewItem 会被populate 调用
populate 这个方法200 行,逻辑比较复杂。 大致是 处理
populate(
mCurItem
)
负责处理当前位置 int currentItem 的 item的显示,以及 这个位置前后 item的 进出,显示,index位置,在 mItems中是否移除 加入 等。
populate在整个viewpager中 有5处? 地方被调用。
populate在整个viewpager中 有5处? 地方被调用。
1 private final Runnable mEndScrollRunnable = new Runnable() {
public void run() {
setScrollState(SCROLL_STATE_IDLE);
populate();
}
};
用处不明
2 setAdapter时。line447
else if (!wasFirstLayout) {
populate();
} else {
requestLayout();
}
看出来应该是第一次setAdapter会造成 第一次layout,所以调用了 populate来 additem,获得 view展示。
3.public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) line 656
用户调用此方法改变page的 位置时?
4.public void setOffscreenPageLimit(int limit)
用户调用此方法改变page的 位置时?
4.public void setOffscreenPageLimit(int limit)
if (limit < DEFAULT_OFFSCREEN_PAGES) {
Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
DEFAULT_OFFSCREEN_PAGES);
limit = DEFAULT_OFFSCREEN_PAGES;
}
if (limit != mOffscreenPageLimit) {
mOffscreenPageLimit = limit;
populate();
}
这是一个比较常用的方法。 可以看到 当user 设置的 offsreen页面 与当前值不符合的时候, 会调用 populate,来 增加或者减少 当前缓存页面。
5.void smoothScrollTo(int x, int y, int velocity) line838
平滑移动时
平滑移动时
6.onMeasure()
// Make sure we have created all fragments that we need to have shown.
mInLayout = true;
populate();
mInLayout = false;
可见,populate 完后,就把 layout标记设置为false。 确实 是用来 在滑动,layout情况下页面改变后加载页面的。
7.public boolean onInterceptTouchEvent(MotionEvent ev)
// Let the user 'catch' the pager as it animates.
mScroller.abortAnimation();
mPopulatePending = false;
populate();
mIsBeingDragged = true;
在ACTION_DOWN分支中。
8. public boolean onTouchEvent(MotionEvent ev) line 2043
依然是在ACTION_DOWN分支中。
mScroller.abortAnimation();
mPopulatePending = false;
populate();
// Remember where the motion event started
一共8处。
总结一下:
1.viewpager 并没有像listview一样,将user(使用这个控件的人) 返回的view 内部自己addview 到 viewgroip的成员mChildren[] 数组中。需要user手动在instantiateItem方法中手动调用一次addview。
同时viewpager也没有像listview一样 有从mChildren[]中移除子view的操作,需要用户在destory方法中手动remove。
为何viewpager要这么设计?
2.viewpager内部自己定义了一个ArrayList的
mItems成员 来保存 user返回的object(view),猜想 这个mItems的数量应该和mChildren[]数量应该是一致的?
3.viewpager如果有上百个页面,开始只加载3个,那么在快速滑动的时候 肯定是不断的在创建新的view销毁旧的view,并没有复用机制?viewpager没有复用机制 是因为考虑到每个page条目可能差别较大无法复用?不像listview一样 都属于同一类型?