我有一个由三个片段组成的活动。在其中一个片段中,我有一个由三个其他片段组成的viewpager。我在其中包含viewpager的片段中调用getChildFragmentManager()
。我搜索了SO并阅读了与此主题相关的几乎所有答案,例如:this和this,而且似乎最有用的是this,但无法解决我的问题。我想如果我可以在POSITION_NONE
中为getItemPosition(Object object)
返回FragmentStatePagerAdapter
并致电notifyDataSetChanged()
来解决我的问题,但每次执行此操作时我的应用都会因此错误而崩溃:
java.lang.IllegalStateException:FragmentManager已在执行 交易!!!
我提到我在我的片段中使用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;
}
}
和我的活动。你刚刚把我认为需要的零件包括在内。
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();
}
}
答案 0 :(得分:0)
经过多次搜索,我可以找到一种不太聪明的方法但暂时解决了我的问题。
首先,我使用了来自jwt-strategy docs帖子的课程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());
}
}
}
}
在我的片段中,我有一个在活动中实现的回调接口。在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);
}
}
}