片段在重新附加时创建/恢复重复的视图

时间:2016-11-23 10:05:59

标签: android android-fragments android-viewpager fragment

我的ViewPagerOffscreenPageLimit=2有6页(很容易重现我的错误)。
6页中的所有数据均来自服务器。在onCreateView中,我向服务器发送请求,并在从服务器获取数据时刷新UI。

当我选择第一个标签并快速更改为最后一次时,某些寻呼机显示错误。那时我片段中的mMainLayout字段不为空。

例如,我的第一页中有一个ListView。当页面错误时,另一个 ListView 位于右侧 ListView 的顶部。当我尝试滚动 ListView 时,只移动了右侧(底部)。

我知道我的响应监听器持有 mMainLayout 和其他一些视图的引用,我在方法 onCreateView 中创建了一个新的 mMainLayout ,我想片段使用新的 mMainLayout 重新附加/恢复并删除旧的 mMainLayout (或从容器中删除它)。但我错了。

我知道FragmentPagerAdapterinstantiateItem中附加/重新附加片段并在destroyItem中分离。适配器没有删除片段。适配器没有制作新片段。片段保持视图并查看状态本身。

我从容器中删除 mMainLayout ,并将我的片段中的所有视图设置为onDestroyView,并在onSaveInstanceState中保存。但仍然很容易重现这个错误。

活性:

@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();
    }
}

片段:

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

我发现了这个:

Android fragment onCreateView creating duplicate views on top of each other

但我只在FragmentPagerAdapter.getItem中创建新片段。所以我们不同。

如果在方法 onCreateView mMainLayout 不为空,则通过删除 mMainLayout 的所有子项来解决此问题。一切都很好。但我仍然对为什么会发生这个错误感到困惑?

我做了一些测试 1.尝试添加一个片段两次,app crash和log说:java.lang.IllegalStateException:片段已添加:...

2.尝试将片段附加两次,系统不会调用Fragment.onCreateFragment.onCreateView

2 个答案:

答案 0 :(得分:3)

由于instantiateItem()的自定义实施,出现了这种情况 有一点我注意到了。你正在弄乱检查片段是否已经添加。

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

不知道为什么要为mMainLayout

创建类成员字段
//try to go with default
View view = inflater.inflate(R.layout.fragment_main, container, false);

建议您学习this

答案 1 :(得分:2)

您可以尝试使用代码,我面临类似的问题,并通过更改来解决。在这种情况下,不需要使onDestroyView中的所有引用都为空。因为在这种情况下,你需要在代码中的任何其他位置使用这些引用之前始终进行空检查。

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

mMainlayout不为空时,表示您的片段实例已经有mMainLayout的一个实例,并且已添加到ViewGroup container,无需再次添加它。当您将同一视图再次添加到同一容器时,您将遇到问题。