The fragment creates / restores the duplicate view when reattaching

I have 6 pages in ViewPager and OffscreenPageLimit=2 (easy to reproduce my error).
all data on 6 pages from the server. In onCreateView I send a request to the server and update the user interface when receiving data from the server.

When I select the first tab and quickly change it several times, some pagers do not display correctly. And while my mMainLayout field in the fragment is not null.

For example, I have a ListView on my first page. When the page is incorrect, another ListView is at the top of the right ListView. When I try to scroll through the ListView, only the right (bottom) moves.

I know that my response listener contains a link to mMainLayout and some other views, I create a new mMainLayout in the onCreateView method, I thought the fragment uses the new mMainLayout when it rejoins / restores and removes the old one (or removes it from the container). But I was wrong.

I know that FragmentPagerAdapter attach / reattach fragment in instantiateItem and detach in destroyItem . The adapter did not delete the fragment. The adapter did not create a new fragment. The snippet saves the view and views the states.

I remove mMainLayout from the container and set all the views in my zero snippet to onDestroyView and don't save anything to onSaveInstanceState . But still easy to reproduce the error.

activity:

 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); registerReceiver(receiver, new IntentFilter("com.souyidai.intent.action.log_fragment")); FragmentManager fragmentManager = getFragmentManager(); mResources = getResources(); mMainPagerAdapter = new MainPagerAdapter(fragmentManager); mPager = (ViewPager) findViewById(R.id.pager); mPager.setAdapter(mMainPagerAdapter); mPager.setOffscreenPageLimit(CACHE_SIZE); mIndicator = (TabPageIndicator) findViewById(R.id.indicator); mIndicator.setViewPager(mPager); ... } private class MainPagerAdapter extends FragmentPagerAdapter { public MainPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { MainConfig.TabItem tab = mTabs.get(position); String tabType = tab.getTabType(); String code = tab.getCode(); MainConfig.TabItem.SubTabItem subTabItem = tab.getSubitem().get(0); Fragment fragment = MainFragment.newInstance(code, subTabItem); return fragment; } @Override public CharSequence getPageTitle(int position) { MainConfig.TabItem tab = mTabs.get(position); return tab.getTitle(); } @Override public int getCount() { return mTabs.size(); } } 

fragment:

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mContainer = container; mMainLayout = inflater.inflate(R.layout.fragment_main, container, false); ... } @Override public void onDestroyView() { super.onDestroyView(); mContainer.removeView(mMainLayout); mContainer = null; mMainLayout = null; mSwipeRefreshLayout = null; mListView = null; mHeaderLayout = null; mFooterLayout = null; ... } 

android.support.v13.app.FragmentPagerAdapter

 @Override 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) { FragmentCompat.setMenuVisibility(fragment, false); FragmentCompat.setUserVisibleHint(fragment, false); } return fragment; } 

I find this:

Android onCreateView snippet creating repeating views on each other

But I only create a new fragment in FragmentPagerAdapter.getItem . So we are different.

I solve this by removing all the children of mMainLayout if mMainLayout is not null in the onCreateView method. That’s all right. But I'm still confused, why is this error happening?

I did some tests.
1. try to add fragment twice, application crash and log: java.lang.IllegalStateException: Fragment already added: ...
2. try to insert the fragment twice, the system does not call Fragment.onCreate or Fragment.onCreateView

+6
source share
2 answers

This situation arose because of the user implementation of instantiateItem() One thing I noticed. If you have already added a snippet, you will randomly check.

 @Override 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); //make sure you are not attaching already added fragment //try with comment and uncomment two lines below //if(!fragment.isAdded()) //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) { FragmentCompat.setMenuVisibility(fragment, false); FragmentCompat.setUserVisibleHint(fragment, false); } return fragment; } 

Not sure why you are creating a class member field for mMainLayout

 //try to go with default View view = inflater.inflate(R.layout.fragment_main, container, false); 

We recommend to study this

+3
source

You can try with the code, I ran into a similar problem that was solved with the following changes. In this case, you do not need to specify null references in onDestroyView . since in this case you need to always do a zero check before using this link elsewhere in the code.

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if(mMainLayout == null) { mMainLayout = inflater.inflate(R.layout.fragment_main, container,false); ... } return mMainLayout; } 

When mMainlayout not null, this means that the fragment instance already has one mMainlayout instance and is already added to the ViewGroup container , no need to add it again. You are facing a problem since you are adding the same view to the same container again.

+2
source

Source: https://habr.com/ru/post/1012581/


All Articles