Fragment 頁面切換與UI更新
由於使用不通的事務方法,場景也是不通的,這裡我們重點討論show/hide與attach/dettach兩類問題。當然,我們繞不開的是add/remove和replace。
一、replace事務
replace相對簡單,對應的是Fragment最簡單的生命週期,因此頁面的切換在onResume中即可。
二、add事務
實際上add和remove雖然是【新增】和【移除】,但是實際上這倆個事務很少同時使用。常見的使用情況反而是attach/dettach+add和show/hide+add事務的組合相對常見。本質上,add+remove的事務組合和replace類似,因此也沒有必要去remove。
單獨的add事務無法實現頁面切換,這裡我們主要說明attach/detach+add和show/hide+add。
2.1 attach/detach+add事務組合
參考android.support.v4.app.FragmentPagerAdapter原始碼:
public Object instantiateItem(ViewGroup container, int position) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } final long itemId = getItemId(position); // 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; } @Override public void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object + " v=" + ((Fragment)object).getView()); mCurTransaction.detach((Fragment)object); } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment)object; if (fragment != mCurrentPrimaryItem) { if (mCurrentPrimaryItem != null) { mCurrentPrimaryItem.setMenuVisibility(false); mCurrentPrimaryItem.setUserVisibleHint(false); } if (fragment != null) { fragment.setMenuVisibility(true); fragment.setUserVisibleHint(true); } mCurrentPrimaryItem = fragment; } } @Override public void finishUpdate(ViewGroup container) { if (mCurTransaction != null) { mCurTransaction.commitNowAllowingStateLoss(); mCurTransaction = null; } }
這種場景下,使用add只是簡單的新增,attach和dettach負責頁面的切換。Fragment在ViewPager中一般是提前重建的,因此,傳統的Fragment生命週期已經不適合,這裡我們看到setUserVisibleHint被呼叫,因此,我們可以使用,setUserVisibleHint機制。
@Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if(getUserVisibleHint()) { onVisible(); } else { onInvisible(); } } protected void onVisible(){ } protected void onInvisible(){ }
但是這裡有個問題,在初始化方法instantiateItem中,我們如果在沒有判斷的情況下,強行setUserVisibleHint,可能在onCreate之前執行,造成生命週期混亂。
if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); }
實際上在setPrimaryItem比較合理,因為提前在ViewPager提前建立了Fragment並且呼叫了onAttach->onCreate。對於當前的問題,我們的解決方法是
protected boolean isCreated = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isCreated = true;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(!isCreated) return;
if(getUserVisibleHint()) {
onShow();
} else {
onHide();
}
}
protected void onShow(){
}
protected void onHide(){
}
2.1 show/hide+add事務組合
這類主要運用於FragmentManager自行管理的頁面
public Fragment showFragment(FragmentManager fragmentManager,int viewId,int position, Bundle bundle) {
FragmentTransaction mCurTransaction = fragmentManager.beginTransaction();
try {
String name = makeFragmentName(viewId, position);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment == null) {
fragment = instantiateItem(position);
fragment.setUserVisibleHint(false);
}
if (mCurrentPrimaryItem != fragment) {
if (mCurrentPrimaryItem != null) {
mCurTransaction.hide(mCurrentPrimaryItem);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment.isAdded()) {
mCurTransaction.show(fragment);
} else {
mCurTransaction.add(viewId, fragment, makeFragmentName(mViewContainer.getId(), position));
}
mCurrentPrimaryItem = fragment;
}
if(bundle!=null)
mCurrentPrimaryItem.setArguments(bundle);
if (!mCurrentPrimaryItem.getUserVisibleHint()) {
mCurrentPrimaryItem.setUserVisibleHint(true);
}
mCurTransaction.commit();
mCurTransaction = null;
return fragment;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
同樣,對於這類生命週期,我們也可以參考attach/detach+add的處理方式,但是我們這裡存在更好的方法,而且配合onResume一起呼叫,那就是onHiddenChanged,他的優點是在add時不會呼叫,在hide/show時才會呼叫。因此,在fragment之間切換時會呼叫onHiddenChanged,在Activity之間切換時呼叫onResume,這種更新更好解決問題,而且可以肯定的是,onHiddenChanged在執行過一次onResume之後才會被呼叫。
@override
public void onResume(){
if(isResumed() && isVisible()){
//這裡主要isVisible()和getUserVisibleHint類似,onResume在Fragment不可見時也會呼叫,為了防止此情況發生,需要做判斷
onShow();
}
}
@override
public void onStop(){ //onstop被呼叫,Fragment頁面必然隱藏
onHide();
}
@override
public void onHiddenChanged(boolean hidd) {
if (hidd) {
//隱藏時所作的事情
onHide();
} else {
//顯示時所作的事情
onShow();
}
}
protected void onShow(){
}
protected void onHide(){
}
三、FragmentTabHost事務
FragmentTabHost相對來說沒有就沒有那麼簡單了,內部通過了attach/detach+add的事務模式,對於這種更新,我們只能通過手動方式來實現了。
文章轉自 https://dwz.pm/7q