刷新片段内的 viewpager 内的片段

Refresh a fragment inside a viewpager which is inside a fragment

我有一个 activity,它由三个片段组成。在其中一个片段中,我有一个由其他三个片段组成的 viewpager。我在其中包含 viewpager 的片段中调用 getChildFragmentManager() 。我搜索了 SO 并阅读了几乎所有与该主题相关的答案,例如:this and this and one that seemed most useful was this 但无法解决我的问题。我认为如果我可以 return POSITION_NONE for getItemPosition(Object object) in FragmentStatePagerAdapter 并调用 notifyDataSetChanged() 将解决我的问题,但每次我这样做时我的应用程序都会因此崩溃错误:

java.lang.IllegalStateException: FragmentManager is already executing transactions!!!

我提到我在我的片段中使用了 getChildFragmentManager()。 有人想过解决这个问题的方法吗?

这是父片段:

public class WordDetailFragment extends Fragment {

    @BindView(R.id.word_parts_tab_layout)
    TabLayout mTabLayout;
    @BindView(R.id.word_part_view_pager)
    ViewPager mViewPager;

    private WordPagerAdapter mPagerAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_word_detail, container, false);
        ButterKnife.bind(this, view);
        mPagerAdapter = new WordPagerAdapter(getChildFragmentManager(), getActivity());
        mViewPager.setAdapter(mPagerAdapter);
        mTabLayout.setupWithViewPager(mViewPager);
        return view;
    }

    public void notifyTranslateChanged() {
        mViewPager.getAdapter().notifyDataSetChanged();
    }
}

这是适配器的代码

public class WordPagerAdapter extends FragmentStatePagerAdapter {
    final int PAGE_COUNT = 3;
    private String[] tabTitles = new String[]{"Definition", "Example", "Col & fam"};
    private Context context;

    public WordPagerAdapter(FragmentManager fm, Context context) {
        super(fm);
        this.context = context;
    }

    @Override
    public int getCount() {
        return PAGE_COUNT;
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment;
        switch (position) {
            case 0:
                fragment = new WordDefinitionFragment();
                break;
            case 1:
                fragment = new WordExampleFragment();
                break;
            case 2:
                fragment = new WordColFamFragment();
                break;

                default: fragment = new WordDefinitionFragment();
        }
        return fragment;
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        // Generate title based on item position
        return tabTitles[position];
    }
}

viewpager中的片段之一

public class WordDefinitionFragment extends Fragment {


    @BindView(R.id.show_translate_switch)
    Switch mShowTranslateSwitch;


    private List<WordInfo> mWordInfoList;
    private List<Definition> mDefinitionList;
    private boolean mShowTranslation;
    private View mView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        mView = inflater.inflate(R.layout.fragment_word_definition, container, false);
        ButterKnife.bind(this, mView);

        mShowTranslation = SharedPrefHelper.getBooleanInfo(getActivity(), getString(R.string.show_translate_key));

        mShowTranslateSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                if (getActivity() != null)
                    ((WordActivity) getActivity()).changeTranslateMode(b);
                showTranslation(b);
            }
        });

        return mView;
    }
}

和我的 activity。你刚刚包含了我认为需要的部分。

public class WordActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceSate) {
        super.onCreate(savedInstanceSate);
        setContentView(R.layout.activity_word);


    }

    public void changeTranslateMode(boolean show) {
        SharedPrefHelper.setBooleanInfo(this, getString(R.string.show_translate_ke)y, show);
        ((WordDetailFragment)mWordDetailFragment).notifyTranslateChanged();
    }

}

而我的 logcat 错误是

经过多次搜索,我想出了一个不是很聪明但暂时解决了我的问题的方法。 首先,我使用了 this post 中的 class FixedFragmentStatePagerAdapter。我还必须删除恢复片段包的部分,因为它缓存了片段变量和视图。

public abstract class FixedFragmentStatePagerAdapter extends PagerAdapter {
    private static final String TAG = "PagerAdapter";
    private static final boolean DEBUG = false;

    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;

    private ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
    private ArrayList<String> mSavedFragmentTags = new ArrayList<>();
    private ArrayList<Fragment> mFragments = new ArrayList<>();
    private Fragment mCurrentPrimaryItem = null;

    public FixedFragmentStatePagerAdapter(FragmentManager fm) {
        mFragmentManager = fm;
    }

    /**
     * Return the Fragment associated with a specified position.
     */
    public abstract Fragment getItem(int position);

    public String getTag(int position) {
        return null;
    }

    @Override
    public void startUpdate(ViewGroup container) {
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Fragment fragment = getItem(position);
        String fragmentTag = getTag(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment + " t=" + fragmentTag);
       /* if (mSavedState.size() > position) {
            String savedTag = mSavedFragmentTags.get(position);
            if (TextUtils.equals(fragmentTag, savedTag)) {
                Fragment.SavedState fss = mSavedState.get(position);
                if (fss != null) {
                    fragment.setInitialSavedState(fss);
                }
            }
        }*/
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment, fragmentTag);

        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment)object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
                + " v=" + ((Fragment)object).getView() + " t=" + fragment.getTag());
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
            mSavedFragmentTags.add(null);
        }
        mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
        mSavedFragmentTags.set(position, fragment.getTag());
        mFragments.set(position, null);

        mCurTransaction.remove(fragment);
    }

    @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.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment)object).getView() == view;
    }

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        if (mSavedState.size() > 0) {
            state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
            state.putStringArrayList("tags", mSavedFragmentTags);
        }
        for (int i=0; i<mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle)state;
            bundle.setClassLoader(loader);
            Parcelable[] fss = bundle.getParcelableArray("states");
            mSavedState.clear();
            mFragments.clear();

            ArrayList<String> tags = bundle.getStringArrayList("tags");
            if (tags != null) {
                mSavedFragmentTags = tags;
            } else {
                mSavedFragmentTags.clear();
            }
            if (fss != null) {
                for (int i=0; i<fss.length; i++) {
                    mSavedState.add((Fragment.SavedState)fss[i]);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key: keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        while (mFragments.size() <= index) {
                            mFragments.add(null);
                        }
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
        }
    }
}

然后我在包含 viewpager 的片段中实现了 ViewPager.OnPageChangeListener。在 void onPageScrollStateChanged(int state) 方法中,我更新了 viewpager 中片段的视图。

public class WordDetailFragment extends Fragment implements ViewPager.OnPageChangeListener {

    @BindView(R.id.word_parts_tab_layout)
    TabLayout mTabLayout;
    @BindView(R.id.word_part_view_pager)
    ViewPager mViewPager;

    private WordPagerAdapter mPagerAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_word_detail, container, false);
        ButterKnife.bind(this, view);
        mPagerAdapter = new WordPagerAdapter(getChildFragmentManager(), getActivity());
        mViewPager.setAdapter(mPagerAdapter);
        mTabLayout.setupWithViewPager(mViewPager);
        mViewPager.addOnPageChangeListener(this);
        return view;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    Fragment mFragment;

    @Override
    public void onPageSelected(int position) {
        mFragment = mPagerAdapter.getItem(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            if (mFragment instanceof WordDefinitionFragment) {
                ((WordDefinitionFragment) mFragment).showTranslation();
               //Log.e("StateChanged", mFragment.getClass().getSimpleName());
            }
            if (mFragment instanceof WordExampleFragment) {
                ((WordExampleFragment) mFragment).showTranslation();
               //Log.e("StateChanged", mFragment.getClass().getSimpleName());
            }
            if (mFragment instanceof WordColFamFragment) {
                ((WordColFamFragment) mFragment).showTranslation();
                //Log.e("StateChanged", mFragment.getClass().getSimpleName());
            }
        }
    }


}

在我的片段中,我有一个在 activity 中实现的回调接口。在 void setUserVisibleHint(boolean isVisibleToUser) 方法中,我再次更新了 UI,因为邻居片段没有得到更新。

public class WordExampleFragment extends Fragment {

    @BindView(R.id.example_field_name_id)
    TextView mExampleFieldNameTV;
    @BindView(R.id.word_tv)
    TextView mWordTv;
    @BindView(R.id.play_sound_iv)
    ImageView mPlaySoundIv;
    @BindView(R.id.show_translate_switch)
    Switch mShowTranslateSwitch;

    private TextView mExample1ContentFaTV;

    private List<WordInfo> mWordInfoList;
    private List<Example> mExampleList;
    private int mWordNumber;

    //callback interface
    private TranslateModeChangeListener mWordPartFragments;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            mWordPartFragments = (TranslateModeChangeListener) context;
        } catch (ClassCastException ex) {
            throw new ClassCastException("Must implement WordPartFragments interface");
        }
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_word_example, container, false);
        ButterKnife.bind(this, view);

        mWordInfoList = WordActivity.sWordInfoList;
        mWordNumber = WordActivity.sWordNumber;
        mExampleList = mWordInfoList.get(mWordNumber).getExamples(); 

        showTranslation();
        mShowTranslateSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
               //this method is called from activity=> callback interface
               mWordPartFragments.changeTranslateMode(b);
                showTranslation();
            }
        });
        return view;
    }

    //this method is called when viewpager shows this fragment and it is visible to user
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser) {
            showTranslation();
        }
    }

    //this methoud updates my UI: shows or hides the translation
    public void showTranslation() {
        if (isAdded()) {
            boolean showTranslation = WordActivity.sTranslationShown;
            //Log.e("Ex", "added & shown = " + showTranslation);
            mShowTranslateSwitch.setChecked(showTranslation);
            mExample1ContentFaTV.setVisibility((showTranslation) ? View.VISIBLE : View.INVISIBLE);

        }
    }
}