由于使用不通的事务方法,场景也是不通的,这里我们重点讨论show/hide与attach/dettach两类问题。当然,我们绕不开的是add/remove和replace。
replace相对简单,对应的是Fragment最简单的生命周期,因此页面的切换在onResume中即可。
实际上add和remove虽然是【添加】和【移除】,但是实际上这俩个事务很少同时使用。常见的使用情况反而是attach/dettach+add和show/hide+add事务的组合相对常见。本质上,add+remove的事务组合和replace类似,因此也没有必要去remove。
单独的add事务无法实现页面切换,这里我们主要说明attach/detach+add和show/hide+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(){
}
这类主要运用于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相对来说没有就没有那么简单了,内部通过了attach/detach+add的事务模式,对于这种更新,我们只能通过手动方式来实现了。
文章转自 https://dwz.pm/7q