① FragmentAをFragmentStatePagerAdapterに追加
② FragmentBをFragmentStatePagerAdapterに追加
③ FragmentBをFragmentStatePagerAdapterから削除
④ FragmentCをFragmentStatePagerAdapterに追加
サンプルなんかを見ながら、FragmentStatePagerAdapterを実装してみましたが、
③までは見た目上は上手くいくが、④を実行すると③で削除したインスタンスが復元してしまいます。
いろいろ調べた結果、以下のことがわかりました。(勘違いなら指摘ください)
1. FragmentStatePagerAdapterはFragmentStatePagerAdapter#getItemで生成されたFragmentのインスタンスをメンバフィールドのListに保持している。
2. FragmentStatePagerAdapter#getItemでいくら別のインスタンスを返却しようとも、置き換わることはない
3. FragmentStatePagerAdapter#instantiateItemの実装として、メンバフィールドの値を返却する。
上記を踏まえた場合、PagerAdapterから実装しないと無理ゲー。
仕方なく PagerAdapter を実装することにします。
PagerAdapterの実装
public class SectionsPagerAdapter extends PagerAdapter { /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * Private instance fileds *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ private final Context context; private final List<Section> sections; private final FragmentManager fragmentManager; private FragmentTransaction currentTransaction = null; private ArrayList<Fragment.SavedState> savedStatuses = new ArrayList<Fragment.SavedState>(); private ArrayList<Fragment> fragments = new ArrayList<Fragment>(); private Fragment currentPrimaryItem = null; /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * public constructors *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ /** * * @param context */ public SectionsPagerAdapter(Context context, FragmentManager fragmentManager) { this.context = context; this.sections = new ArrayList<Section>(); this.fragmentManager = fragmentManager; } /** * * @param context */ public SectionsPagerAdapter(Context context, FragmentManager fragmentManager, List<Section> fragmentPages) { this.context = context; this.sections = fragmentPages; this.fragmentManager = fragmentManager; } /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * FragmentStatePagerAdapter methods *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ public boolean isViewFromObject(View view, Object object) { return (((Fragment)object).getView() == view); } /** */ @Override public int getCount() { return this.sections.size(); } /** */ @Override public CharSequence getPageTitle(int position) { return this.sections.get(position).getTitle(); } @Override public int getItemPosition(Object object) { android.util.Log.v("SectionsPageAdapter", "getItemPosition"); int index = this.fragments.indexOf(object); return index != -1 ? index : PagerAdapter.POSITION_NONE; } @Override public void finishUpdate(ViewGroup container) { if (this.currentTransaction != null) { this.currentTransaction.commitAllowingStateLoss(); this.currentTransaction = null; this.fragmentManager.executePendingTransactions(); } } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment) object; if (fragment != currentPrimaryItem) { if (currentPrimaryItem != null) { currentPrimaryItem.setMenuVisibility(false); } if (fragment != null) { fragment.setMenuVisibility(true); } currentPrimaryItem = fragment; } } @Override public Object instantiateItem(ViewGroup container, int position) { android.util.Log.v("SectionsPageAdapter", "instantiateItem:" + position); android.util.Log.v("SectionsPageAdapter", "this.fragments.size():" + this.fragments.size()); if (this.currentTransaction == null) { this.currentTransaction = this.fragmentManager.beginTransaction(); } if (this.fragments.size() > position) { Fragment fragment = (Fragment)this.fragments.get(position); if (fragment != null) { return fragment; } } Fragment fragment = this.instantiateFragment(position); if (this.savedStatuses.size() > position) { Fragment.SavedState fss = this.savedStatuses.get(position); if (fss != null) { try { fragment.setInitialSavedState(fss); } catch (Exception ex) { // Schon aktiv (kA was das heißt xD) ex.printStackTrace(); } } } while (this.fragments.size() <= position) { this.fragments.add(null); } fragment.setMenuVisibility(false); this.fragments.set(position, fragment); this.currentTransaction.add(container.getId(), fragment); return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { android.util.Log.v("SectionsPageAdapter", "destroyItem:" + position); Fragment fragment = (Fragment)object; if (this.currentTransaction == null) { this.currentTransaction = this.fragmentManager.beginTransaction(); } // /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * 削除対象のFragmentがsectionsに存在するかを確認する。 * 同一であると識別するのは、 * ・クラス名が一致する * ・argumentsが一致する * の二点を満たした場合。 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ String fullName = fragment.getClass().getName(); Bundle arguments = fragment.getArguments(); boolean isExist = false; for (Section section : this.sections) { // フラグメントのクラス名が一致するか if (section.getFragmentClassName().equals(fullName)) { if (section.getArguments() == null && arguments == null) { // 一致 isExist = true; } else if (section.getArguments() == null){ // NULL で 個数が0なら一致 isExist = arguments.keySet().size() == 0; } else if (arguments == null){ // NULL で 個数が0なら一致 isExist = section.getArguments().keySet().size() == 0; } else { boolean isKeyMatch = true; // 引数のキーを確認し、一致しない項目を探します。 for(String key : section.getArguments().keySet()) { if (!arguments.containsKey(key)) { // キーが欠落 isKeyMatch = false; break; } if (!section.getArguments().get(key).equals(arguments.get(key))) { // 値が不一致 isKeyMatch = false; break; } } isExist = isKeyMatch; } } // 存在した為、検索を中断する。 if (isExist) { break; } } if (isExist) { while (this.savedStatuses.size() <= position) { this.savedStatuses.add(null); } this.savedStatuses.set(position, this.fragmentManager.saveFragmentInstanceState(fragment)); while (this.fragments.size() <= position) { this.fragments.add(null); } this.fragments.set(position, null); } else { } this.currentTransaction.remove(fragment); } @Override public Parcelable saveState() { Bundle state = null; if (savedStatuses.size() > 0) { state = new Bundle(); Fragment.SavedState[] fss = new Fragment.SavedState[savedStatuses.size()]; savedStatuses.toArray(fss); state.putParcelableArray("states", fss); } for (int i = 0; i < fragments.size(); i++) { Fragment f = fragments.get(i); if (f != null) { if (state == null) { state = new Bundle(); } String key = "f" + i; fragmentManager.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"); savedStatuses.clear(); fragments.clear(); if (fss != null) { for (int i = 0; i < fss.length; i++) { savedStatuses.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 fragment = fragmentManager.getFragment(bundle, key); if (fragment != null) { while (fragments.size() <= index) { fragments.add(null); } fragment.setMenuVisibility(false); fragments.set(index, fragment); } else { android.util.Log.w("SectionsPageAdapter", "Bad fragment at key " + key); } } } } } /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * public methods *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ /** */ public Fragment instantiateFragment(int position) { if (this.sections.size() < position) { return null; } return Fragment.instantiate( this.context, this.sections.get(position).getFragmentClassName(), this.sections.get(position).getArguments()); } /** * Sectionを追加する。 * * @param resId タイトルに使う文字列リソースのID * @param fragmentClass フラグメントのクラス * @param arguments フラグメントを初期化するためのバンドル */ public void addSection(int resId, Class fragmentClass, Bundle arguments) { this.addSection(this.context.getString(resId), fragmentClass, arguments); } /** * Sectionを追加する。 * * @param title タイトル * @param fragmentClass フラグメントのクラス * @param arguments フラグメントを初期化するためのBundle */ public void addSection(String title, Class fragmentClass, Bundle arguments) { this.sections.add(new Section(title, fragmentClass, arguments)); // 新しくタブが追加されたことを通知しておく this.notifyDataSetChanged(); } /** * Sectionを削除する * @param section 削除するSection */ public void removePage(Section section) { if (this.getCount() == 1) { return; } int position = this.sections.indexOf(section); this.sections.remove(section); while (this.savedStatuses.size() <= position) { this.savedStatuses.add(null); } this.savedStatuses.remove(position); while (this.fragments.size() <= position) { this.fragments.add(null); } this.fragments.remove(position); this.notifyDataSetChanged(); } /** * Sectionを削除する * @param position 削除するインデックス */ public void removePage(int position) { if (this.getCount() == 1) { return; } this.sections.remove(position); while (this.savedStatuses.size() <= position) { this.savedStatuses.add(null); } this.savedStatuses.remove(position); while (this.fragments.size() <= position) { this.fragments.add(null); } this.fragments.remove(position); this.notifyDataSetChanged(); } /** * 指定したSectionを取得する * @param position index * @return */ public Section getSection(int position) { return this.sections.get(position); } /** * Section情報を全て取得する。 * @return */ public ArrayList<Section> getSections() { int size = this.getCount(); ArrayList>Section< result = new ArrayList>Section<(size); for (int index = 0; index < size; index++) { result.add(this.sections.get(index)); } return result; } /** * 指定されたSectionのindexを取得する。 * @param section indexを取得するSection * @return */ public int getSectionPosition(Section section) { return this.sections.indexOf(section); } }
上記で使用しているデータクラス
public class Section implements Parcelable { public static final Parcelable.Creator<Section> CREATOR = ( new Parcelable.Creator<Section>() { public Section createFromParcel(Parcel in) { Section result = new Section(in); return result; } public Section[] newArray(int size) { return new Section[size]; } }); /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * Private instance fields *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ /** * 表示するタイトル */ private String title; /** * フラグメントのクラス名 */ private String fragmentClassName; /** * フラグメントに設定する引数情報 */ private Bundle arguments; /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * Public Getter/Setter *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ /** * タイトルを取得します。 * @return */ public String getTitle() { return this.title; } /** * フラグメントに設定する引数情報を取得します。 * @return */ public Bundle getArguments() { return this.arguments; } /** * フラグメントのクラス名を取得します。 */ public String getFragmentClassName() { return this.fragmentClassName; } /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * Private constructors *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ private Section(Parcel in) { this.title = in.readString(); this.fragmentClassName = in.readString(); this.arguments = in.readBundle(); } /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* * public constructors *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ /** * * @param title * @param fragmentClass * @param arguments */ public Section(String title, Class fragmentClass, Bundle arguments) { this.title = title; this.fragmentClassName = fragmentClass.getName(); this.arguments = arguments; } /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* * Parcelable methods *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ /** */ @Override public int describeContents() { return 0; } /** */ @Override public void writeToParcel(Parcel out, int flags) { out.writeString(this.title); out.writeString(this.fragmentClassName); out.writeBundle(this.arguments); } }
これで追加削除が自由自在!ひゃっはー!
もっと楽な方法がある気がします。