這里主要涉及到Fragment在ViewPager中顯示時,一個同步和異步的問題。
這樣一個場景:
你做一個播放音樂的App,在界面底部有一個控制欄可以開始\暫停音樂的播放,點擊控制欄可以進入播放詳情。假設你剛好點擊了播放,然后再進入播放詳情頁面(Activity實現(xiàn)),你希望一進去會有一個動畫效果,這個動畫在Fragment里面實現(xiàn)。里面由ViewPager+Fragment實現(xiàn),左右滑動可切換歌曲。
也許會有下面的代碼
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();
}
}
要執(zhí)行動畫,那么界面必須出來,這么做,動畫肯定不會執(zhí)行,因為界面還沒出來。
那么,F(xiàn)ragment的界面時什么時候出來的呢?
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重寫了該方法,也就是去執(zhí)行Fragment棧中所有操作(mCurTransaction.add(container.getId(), fragment);),而執(zhí)行棧中的操作commitNow是一個同步任務。也就是說,此時Fragment才被attach到Activity中,F(xiàn)ragment中的View才被添加到ViewPager中。
現(xiàn)在問題在于,當監(jiān)聽器中的onPageSelected()方法執(zhí)行的時候,ViewPager的populate()方法執(zhí)行了嗎?沒有!
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); //該方法執(zhí)行
}
requestLayout(); //發(fā)送一個異步的布局消息
} else {
populate(item);
scrollToItem(item, smoothScroll, velocity, dispatchSelected);
}
}
也就是說,當你調(diào)用了setCurrentItem(),監(jiān)聽器的回調(diào)方法會同步執(zhí)行,而populate()方法卻要在下一個消息中執(zhí)行。所以此時,F(xiàn)ragment的對象確實被創(chuàng)建出來了,他的生命周期方法卻還沒有執(zhí)行。
解決方法:
- 在創(chuàng)建Fragment的時候,通過setArgument方法設置參數(shù)
- 在onAttach()中拿到Activity的引用,進而調(diào)用他的方法去獲得參數(shù)