这里主要涉及到Fragment在ViewPager中显示时,一个同步和异步的问题。
这样一个场景:
你做一个播放音乐的App,在界面底部有一个控制栏可以开始\暂停音乐的播放,点击控制栏可以进入播放详情。假设你刚好点击了播放,然后再进入播放详情页面(Activity实现),你希望一进去会有一个动画效果,这个动画在Fragment里面实现。里面由ViewPager+Fragment实现,左右滑动可切换歌曲。
也许会有下面的代码
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
getCur_fragment().startRotate(); //开始动画
} catch (RemoteException e) {
e.printStackTrace();
}
}
要执行动画,那么界面必须出来,这么做,动画肯定不会执行,因为界面还没出来。
那么,Fragment的界面时什么时候出来的呢?
ViewPager的populate()方法
public void populate(){
//.......
mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);
mAdapter.finishUpdate(this); //FragmentPageAdapter进行了重写
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitNowAllowingStateLoss();
mCurTransaction = null;
}
}
FragmentPageAdapter重写了该方法,也就是去执行Fragment栈中所有操作(mCurTransaction.add(container.getId(), fragment);),而执行栈中的操作commitNow是一个同步任务。也就是说,此时Fragment才被attach到Activity中,Fragment中的View才被添加到ViewPager中。
现在问题在于,当监听器中的onPageSelected()方法执行的时候,ViewPager的populate()方法执行了吗?没有!
public void setCurrentItem(){
if (mFirstLayout) { //第一次布局,默认为true
// We don't have any idea how big we are yet and shouldn't have any pages either.
// Just set things up and let the pending layout handle things.
mCurItem = item;
if (dispatchSelected) {
dispatchOnPageSelected(item); //该方法执行
}
requestLayout(); //发送一个异步的布局消息
} else {
populate(item);
scrollToItem(item, smoothScroll, velocity, dispatchSelected);
}
}
也就是说,当你调用了setCurrentItem(),监听器的回调方法会同步执行,而populate()方法却要在下一个消息中执行。所以此时,Fragment的对象确实被创建出来了,他的生命周期方法却还没有执行。
解决方法:
- 在创建Fragment的时候,通过setArgument方法设置参数
- 在onAttach()中拿到Activity的引用,进而调用他的方法去获得参数