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的事务模式,对于这种更新,我们只能通过手动方式来实现了。

当然,我们最好定义一个接口,这里不再赘述了。

猜你喜欢

转载自my.oschina.net/ososchina/blog/1649341