Fragment page switching and UI update

Due to the use of different transaction methods, the scene is also unreasonable. Here we focus on two types of issues: show/hide and attach/dettach. Of course, what we can't do without is add/remove and replace.

1. Replace transaction

replace is relatively simple, corresponding to the simplest life cycle of Fragment, so the page switching can be done in onResume.

2. Add transaction

In fact, although add and remove are [add] and [remove], in fact, these two transactions are rarely used at the same time. A common use case is instead that the combination of attach/dettach+add and show/hide+add transactions is relatively common. In essence, the transaction combination of add+remove is similar to replace, so there is no need to remove.

A separate add transaction cannot implement page switching. Here we mainly describe attach/detach+add and show/hide+add.

 

2.1 attach/detach+add transaction combination

Refer to the android.support.v4.app.FragmentPagerAdapter source code:

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;
    }
}

In this scenario, using add is just a simple addition, and attach and dettach are responsible for page switching. Fragment is generally rebuilt in advance in ViewPager. Therefore, the traditional Fragment life cycle is not suitable. Here we see that setUserVisibleHint is called. Therefore, we can use the setUserVisibleHint mechanism.



@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

      

        if(getUserVisibleHint()) {
   
            onVisible();
        } else {
 
            onInvisible();
        }
}
protected void onVisible(){
      
}
protected void onInvisible(){

}

But there is a problem here. In the initialization method instantiateItem, if we force setUserVisibleHint without judgment, it may be executed before onCreate, causing life cycle confusion.

if (fragment != mCurrentPrimaryItem) {
    fragment.setMenuVisibility(false);
    fragment.setUserVisibleHint(false);
}

In fact, setPrimaryItem is more reasonable, because Fragment is created in advance in ViewPager and onAttach->onCreate is called. For the current problem, our workaround is


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 transaction combination

This type of page is mainly used for pages managed by FragmentManager itself.

   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;
    }

Similarly, for this type of life cycle, we can also refer to the processing method of attach/detach+add, but we have a better method here, and it is called together with onResume, that is onHiddenChanged, its advantage is that it will not be called during add. , it will only be called when hide/show. Therefore, onHiddenChanged is called when switching between fragments, and onResume is called when switching between activities. This update is better to solve the problem, and it is certain that onHiddenChanged will be called after onResume is executed once .


@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(){

}

 

3. FragmentTabHost transaction

FragmentTabHost is relatively simple, and the transaction mode of attach/detach+add is passed internally. For this kind of update, we can only achieve it manually.

Of course, we'd better define an interface, so I won't go into details here.

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324405466&siteId=291194637